Files
IdeaSDK/frontend/src/app/di.tsx
Blomios a7197fc53b feat(frontend): produire un bundle web distinct en transport http (#74 F1)
Le transport est figé au build par Vite : un seul `dist` ne peut pas servir
à la fois le desktop (IPC Tauri) et le navigateur (HTTP+WS). Le serveur
embarqué servait donc un bundle desktop, d'où `__TAURI_INTERNALS__ is
undefined` côté navigateur.

- `vite.config.ts` devient une factory `({ mode })` : le mode par défaut émet
  le bundle desktop (`dist`), `--mode web` lit `.env.web` et émet le bundle
  navigateur (`dist-web`).
- `.env.web` porte `VITE_TRANSPORT=http` dans un fichier de mode plutôt qu'en
  préfixe de commande, syntaxe qui n'existe pas sous Windows (bundle NSIS).
- `transport.ts` extrait le prédicat `transportFromEnv()`, partagé par l'app et
  la config de build : le constant `__IDEA_TRANSPORT__` et le transport résolu
  dérivent de la même variable via le même prédicat, ils ne peuvent pas diverger.
- `main.tsx` publie `__IDEA_TRANSPORT__` sur `window` : l'affectation est un
  effet de bord, elle survit à la minification et rend un bundle identifiable
  sans grep d'un symbole minifié. Les deux jeux d'adapters étant présents dans
  les deux bundles, leur présence ne prouve rien.

Le chemin desktop est inchangé : le web reste opt-in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:09:33 +02:00

83 lines
2.5 KiB
TypeScript

/**
* Dependency-injection provider for the UI ports.
*
* Chooses one of three transport implementations (ticket #13): the mock adapters
* (`VITE_USE_MOCK`), the web HTTP+WS adapters (`VITE_TRANSPORT="http"`), or the
* default Tauri desktop adapters. Components consume gateways via
* {@link useGateways} and never construct adapters or call `invoke()` themselves.
*/
import {
createContext,
useContext,
useMemo,
type ReactNode,
} from "react";
import type { Gateways } from "@/ports";
import { createTauriGateways } from "@/adapters";
import { createMockGateways } from "@/adapters/mock";
import { createHttpWsGateways } from "@/adapters/http";
import { transportFromEnv, type Transport } from "./transport";
const GatewaysContext = createContext<Gateways | null>(null);
export type { Transport };
/** Whether the mock adapters should be used (env-driven, overridable in tests). */
export function shouldUseMock(): boolean {
const flag = import.meta.env.VITE_USE_MOCK;
return flag === "1" || flag === "true";
}
/**
* Whether the web (client/server) HTTP+WS adapters should be used. Selected
* explicitly via `VITE_TRANSPORT="http"` so the desktop path stays the default
* and is never affected (ticket #13, lot F1).
*/
export function shouldUseHttp(): boolean {
return transportFromEnv(import.meta.env.VITE_TRANSPORT) === "http";
}
/** Resolves which transport to use. Mock wins, then explicit web, else Tauri. */
export function resolveTransport(): Transport {
if (shouldUseMock()) return "mock";
return transportFromEnv(import.meta.env.VITE_TRANSPORT);
}
/** Resolves the gateway set for the current environment. */
export function resolveGateways(): Gateways {
switch (resolveTransport()) {
case "mock":
return createMockGateways();
case "http":
return createHttpWsGateways();
case "tauri":
return createTauriGateways();
}
}
interface DIProviderProps {
children: ReactNode;
/** Optional explicit gateways (used by tests/Storybook). */
gateways?: Gateways;
}
export function DIProvider({ children, gateways }: DIProviderProps) {
const value = useMemo(() => gateways ?? resolveGateways(), [gateways]);
return (
<GatewaysContext.Provider value={value}>
{children}
</GatewaysContext.Provider>
);
}
/** Hook returning the injected gateways. Throws if used outside the provider. */
export function useGateways(): Gateways {
const ctx = useContext(GatewaysContext);
if (!ctx) {
throw new Error("useGateways must be used within a <DIProvider>");
}
return ctx;
}