feat(frontend): adapter web HTTP+WebSocket derrière les ports (#13)
Lot F1 du chantier server/client mode : nouvel adaptateur web branché derrière les ports d'invocation et de flux live, permettant au frontend de dialoguer avec le backend via HTTP + WebSocket en mode client/serveur. Le mode desktop (Tauri IPC) reste inchangé. - frontend/src/adapters/http : invoker HTTP, client live WebSocket, gateways request/response et stream, frames, garde unsupported (7 fichiers + 2 tests). - frontend/src/app : câblage DI (di.tsx) et son test, typage vite-env.d.ts. Validé : build vert, garde no-direct-invoke verte, 724 tests verts, desktop inchangé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
77
frontend/src/adapters/http/unsupported.ts
Normal file
77
frontend/src/adapters/http/unsupported.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Desktop-only surfaces that have no web equivalent in V1 (ticket #13, lot F1):
|
||||
* the native folder picker, OS view-windows and the main↔detached focused-project
|
||||
* channel, plus SSH/WSL remote. On the web client these must fail with a clear,
|
||||
* explicit error rather than reaching for Tauri (which is absent).
|
||||
*
|
||||
* The B0 inventory classified these as `desktop-only`; F1 does NOT implement them
|
||||
* for web. If/when the product wants an equivalent (e.g. a server-side folder
|
||||
* browser to replace `pickFolder`), it gets its own lot.
|
||||
*/
|
||||
|
||||
import type { GatewayError, Unsubscribe } from "@/domain";
|
||||
import type {
|
||||
FocusedProject,
|
||||
FocusedProjectGateway,
|
||||
RemoteGateway,
|
||||
ViewWindowClosed,
|
||||
ViewWindowSnapshot,
|
||||
WindowGateway,
|
||||
} from "@/ports";
|
||||
|
||||
/** Throws a stable, explicit "unsupported on web" {@link GatewayError}. */
|
||||
export function unsupportedOnWeb(what: string): never {
|
||||
const err: GatewayError = {
|
||||
code: "UNSUPPORTED_ON_WEB",
|
||||
message: `${what} is not available in the web client (desktop-only).`,
|
||||
};
|
||||
throw err;
|
||||
}
|
||||
|
||||
/** Web stub: OS view-windows are desktop-only (no `WebviewWindow` on the web). */
|
||||
export class WebWindowGateway implements WindowGateway {
|
||||
openViewWindow(): Promise<void> {
|
||||
return unsupportedOnWeb("Detaching a panel into an OS window");
|
||||
}
|
||||
closeViewWindow(): Promise<void> {
|
||||
return unsupportedOnWeb("Closing a detached OS window");
|
||||
}
|
||||
listOpenViewWindows(): Promise<ViewWindowSnapshot[]> {
|
||||
// Best-effort by contract (callers treat rejection as "no reconciliation"),
|
||||
// but there are simply never detached OS windows on web → empty list.
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
onViewWindowClosed(
|
||||
_handler: (event: ViewWindowClosed) => void,
|
||||
): Promise<Unsubscribe> {
|
||||
// No OS windows ⇒ nothing ever fires; return a no-op unsubscribe.
|
||||
return Promise.resolve(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Web stub for the focused-project channel. With no detached windows on web the
|
||||
* channel has no cross-window consumer; F1 keeps it inert (no publish, no focus).
|
||||
* A future multi-tab web build could back this with `BroadcastChannel`.
|
||||
*/
|
||||
export class WebFocusedProjectGateway implements FocusedProjectGateway {
|
||||
setFocusedProject(_project: FocusedProject | null): Promise<void> {
|
||||
// No-op: nothing reads the channel on web V1.
|
||||
return Promise.resolve();
|
||||
}
|
||||
getFocusedProject(): Promise<FocusedProject | null> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
onFocusedProjectChanged(
|
||||
_handler: (project: FocusedProject | null) => void,
|
||||
): Promise<Unsubscribe> {
|
||||
return Promise.resolve(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/** Web stub: SSH/WSL remote connection is desktop-only in V1. */
|
||||
export class WebRemoteGateway implements RemoteGateway {
|
||||
connect(): Promise<void> {
|
||||
return unsupportedOnWeb("Remote (SSH/WSL) connection");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user