diff --git a/.project/concepts/nextgraph-platform/decision_2026-06-17_eventually-library.md b/.project/concepts/nextgraph-platform/decision_2026-06-17_eventually-library.md index 9c831c0..db333ce 100644 --- a/.project/concepts/nextgraph-platform/decision_2026-06-17_eventually-library.md +++ b/.project/concepts/nextgraph-platform/decision_2026-06-17_eventually-library.md @@ -38,16 +38,22 @@ Tout le polyfill qui compense l'immaturité de NextGraph (pas de lecture cross-w - **Migration** : retirer l'alias de build + l'appel de bootstrap → le client redevient le vrai SDK ; **traduire les grants émulés en vraies caps** (étape de données). Le **mécanisme cible de l'index global** reste à décider (app singleton ?, [[knowledge_apps-and-services]]) — ce n'est **pas** un backend. Le code applicatif ne bouge pas. - Le [[brief_2026-06-15_shared-wallet-shim]] décrit désormais **comment Festipod consomme `ng-eventually`** (les mécanismes y sont *réalisés par la lib*), plus une implémentation interne à l'app. -## Statut d'intégration (2026-06-22) +## Statut d'intégration (2026-06-25) -Premier branchement réalisé, **en passthrough** (la lib forwarde tout vers le vrai SDK ; mécanismes du polyfill encore stubés) : +**Tout le runtime NextGraph de l'app passe par la lib** (en passthrough — la lib forwarde au vrai SDK, mécanismes du polyfill encore stubés) : -- `@ng-eventually/client` ajouté en **dépendance locale** de Festipod (`file:../../nextgraph/ng-eventually-js/packages/client`). -- **`useShape`** importé depuis `@ng-eventually/client` (`useShapeWithDefaults`, `harness-ng`) ; **vrai SDK injecté** via `configure({ ng, useShape })` dans `ngSession` (`@ng-eventually/client/polyfill`). -- Types `NgLike`/`UseShapeLike` de la lib **assouplis** pour accepter le vrai SDK. -- **Validé** : build Festipod · `@ui` 4/4 · **`@data` 8/8 contre le broker** · lib (typecheck + 4 tests). Comportement identique (passthrough) → la plomberie de remplacement est prouvée. +- `@ng-eventually/client` en **dépendance locale** (`file:../../nextgraph/ng-eventually-js/packages/client`). +- Surface routée via `@ng-eventually/client` : **`useShape`** (`useShapeWithDefaults`, `harness-ng`), **`init`** et **`initNg`** (signals), **`ng`** (login) dans `ngSession`. +- **Point d'injection unique** : `ngSession` importe le vrai SDK **uniquement** pour `configure({ ng, useShape, init, initNg })`, puis utilise les exports de la lib. L'engine ORM reçoit le vrai `ng` (passé à `initNg`) — plomberie interne, pas un appel applicatif. +- **Exception assumée** : `src/shared/test-harness/auth-setup.tsx` (bootstrap du wallet de test, antérieur à `configure`) reste sur `@ng-org/web`. Les imports **de types** restent aussi sur `@ng-org/*`. +- La lib expose `init`/`initNg` (forwarders, `src/lifecycle.ts`) ; `EventuallyConfig` accepte `init`/`initNg` ; `NgLike`/`UseShapeLike` assouplis pour le vrai SDK. +- **Types via la lib (2026-06-25)** : la lib **ré-exporte** `ShapeType`/`BaseType`/`Schema`/`DeepSignalSet`/`NG` ; l'app importe ses types depuis `@ng-eventually/client`. `export type` est **effacé au build** → **aucun import runtime `@ng-org`** ajouté dans la lib (pas de double copie). `@ng-org` en **devDependencies** de la lib (typecheck seulement). +- **Point d'injection unique (option 1)** : dans l'app, **seul `ngSession`** importe le vrai SDK au runtime — uniquement pour `configure(...)`. Tout le reste de l'app (data, lifecycle, login, types) passe par la lib. + - **Pourquoi pas « lib importe le SDK elle-même »** : la lib étant dans un **repo séparé** (arbre `node_modules` distinct), si elle importait `@ng-org` au runtime, le bundle aurait **deux copies** d'`@ng-org` → l'ORM (signaux mono-instance) casserait. L'injection garantit **un seul exemplaire** (celui de Festipod). *(Le « zéro accès direct » exigerait la lib en workspace dans le repo — écarté pour la garder externe ; cf. options 2/3 discutées.)* + - **Exceptions assumées** (hors « app ») : `src/shared/test-harness/auth-setup.tsx` (bootstrap wallet de test) et `src/shared/test-harness/harness.tsx` (harness **mock**, `deepSignal`) gardent un import direct `@ng-org`. Les **bindings ORM générés** (`festipodShapes.*`) aussi (types générés). +- **Validé** : build Festipod · `@ui` 4/4 · **`@data` 8/8 (43 steps) contre le broker** · lib (typecheck + 4 tests). -Reste à implémenter dans la lib (les stubs `TODO`) : filtre de lecture sur l'ORM réactif, garde d'écriture, `inbox.post`, login wallet partagé. +Reste à implémenter dans la lib (les stubs `TODO`, nécessitent la couche comptes/grants) : filtre de lecture sur l'ORM réactif, garde d'écriture, `inbox.post`, login wallet partagé. ## Open Questions diff --git a/src/shared/hooks/useShapeWithDefaults.ts b/src/shared/hooks/useShapeWithDefaults.ts index 4ee549b..5f4cf8c 100644 --- a/src/shared/hooks/useShapeWithDefaults.ts +++ b/src/shared/hooks/useShapeWithDefaults.ts @@ -9,8 +9,7 @@ */ import { useShape } from '@ng-eventually/client'; -import type { ShapeType, BaseType } from '@ng-org/shex-orm'; -import type { DeepSignalSet } from '@ng-org/alien-deepsignals'; +import type { ShapeType, BaseType, DeepSignalSet } from '@ng-eventually/client'; export interface ShapeWithDefaults { /** Mapped items from NG store */ items: AppT[]; diff --git a/src/shared/test-harness/harness-ng.tsx b/src/shared/test-harness/harness-ng.tsx index 5f23eba..cd20a25 100644 --- a/src/shared/test-harness/harness-ng.tsx +++ b/src/shared/test-harness/harness-ng.tsx @@ -12,7 +12,7 @@ import { createRoot } from 'react-dom/client'; import { NextGraphProvider, useNextGraph } from '../context/NextGraphContext'; import { FestipodDataProvider, useFestipodData } from '../context/FestipodDataContext'; import { useShape } from '@ng-eventually/client'; -import type { DeepSignalSet } from '@ng-org/alien-deepsignals'; +import type { DeepSignalSet } from '@ng-eventually/client'; import { FpEventShapeType, FpUserProfileShapeType, diff --git a/src/shared/utils/ngBootstrap.ts b/src/shared/utils/ngBootstrap.ts index c9de98a..03acc58 100644 --- a/src/shared/utils/ngBootstrap.ts +++ b/src/shared/utils/ngBootstrap.ts @@ -5,7 +5,7 @@ * has events/users, it's a returning user — skip seeding. */ -import type { DeepSignalSet } from '@ng-org/alien-deepsignals'; +import type { DeepSignalSet } from '@ng-eventually/client'; import type { FpEvent, FpUserProfile, FpParticipation } from '../shapes/orm/festipodShapes.typings'; import { ensureGraphNuri } from './ngGraph'; import { diff --git a/src/shared/utils/ngSession.ts b/src/shared/utils/ngSession.ts index 46deb1b..fc3b284 100644 --- a/src/shared/utils/ngSession.ts +++ b/src/shared/utils/ngSession.ts @@ -1,14 +1,16 @@ -import { ng, init as initNgWeb } from "@ng-org/web"; -import type { NG } from "@ng-org/web"; -import { initNg as initNgSignals } from "@ng-org/orm"; +// Injection point — the ONLY app module that imports the real @ng-org SDK, to +// inject it into the ng-eventually polyfill. Every other Festipod module gets +// its NextGraph surface from @ng-eventually/client. Removed at migration. +import { ng as realNg, init as realInit } from "@ng-org/web"; +import type { NG } from "@ng-eventually/client"; +import { initNg as realInitNg } from "@ng-org/orm"; import { useShape as realUseShape } from "@ng-org/orm/react"; -import { configure as configureEventually } from "@ng-eventually/client/polyfill"; +import { configure } from "@ng-eventually/client/polyfill"; -// Inject the REAL SDK into the ng-eventually polyfill. The app imports the -// SDK-shaped exports (`useShape`, `ng`, `inbox`) from `@ng-eventually/client`, -// which delegate to what we inject here. This is the single injection point — -// removed at migration (the app then points back at the real SDK). -configureEventually({ ng, useShape: realUseShape }); +// SDK-shaped surface used by ngSession itself — taken from the lib, not @ng-org. +import { ng, init as initNgWeb, initNg as initNgSignals } from "@ng-eventually/client"; + +configure({ ng: realNg, useShape: realUseShape, init: realInit, initNg: realInitNg }); export let session: NextGraphSession | undefined; @@ -33,17 +35,17 @@ let initPromise: Promise | null = null; export function init(): Promise { if (initPromise) return initPromise; console.log('[NG session] init() called — registering callback'); - initPromise = initNgWeb( + initPromise = (initNgWeb( async (event: any) => { session = event.session; - session!.ng ??= ng; + session!.ng ??= realNg; console.log('[NG session] Connected — private_store:', session!.private_store_id); resolveSessionPromise(session!); - initNgSignals(ng, session!); + initNgSignals(realNg, session!); }, true, [] - ).catch((error) => { + ) as Promise).catch((error: any) => { console.error('[NG session] init error:', error); rejectSessionPromise(error); });