feat: le polyfill possède la session et la normalisation des identités

Pour démarrer, une application devait écrire une promesse autour du callback
d'init(), attraper l'événement loggedin, puis fournir un thunk getSession qui
dépiaute session_id et les trois identifiants de store dans notre forme. Plus un
normalizeId. C'est précisément la plomberie que ce paquet existe pour absorber :
chaque application la réécrirait à l'identique, et c'est elle qui a produit deux
défauts aujourd'hui — un blocage et un partage cassé en silence.

En amont, une session est RENDUE ; une application n'en assemble jamais une à
partir de champs bruts. Et les identités virtuelles sont une invention du
polyfill, donc leur normalisation lui appartient.

Le wrapper init() enveloppe désormais le callback de l'appelant : il capture
l'événement, en dérive la session, puis appelle le callback avec le même
événement. Le paquet n'appelle jamais init de sa propre initiative — il
l'enveloppe. Sans callback, il capture quand même.

getSession et normalizeId quittent la surface publiée. Le chemin d'injection
reste pour les harnais, mais inatteignable depuis l'entrée : vérifié par un
import à l'exécution et par un configure() refusé à la compilation.

Défaut trouvé et corrigé en route : le broker envoie session_id en NOMBRE, et le
convertir en chaîne faisait refuser tous les appels par le binding wasm. La
valeur ne fait que transiter, elle est relayée telle quelle. Reste que toute la
chaîne la type string — inexactitude antérieure à ce commit, à traiter à part.

Une application écrit maintenant : configure({ ng, useShape, init, sharedWallet }).
This commit is contained in:
Sylvain Duchesne
2026-08-12 17:39:12 +02:00
parent 7a4d9b492f
commit cc8a95d303
20 changed files with 501 additions and 141 deletions
+25 -1
View File
@@ -22,6 +22,7 @@
import { getConfig } from "../shared-wallet/bootstrap";
import { settleIdentity } from "../shared-wallet/access-gate";
import { captureSession } from "../shared-wallet/session";
/**
* Forwards to the real `@ng-org/web` `init`, once the identifier is in the address bar.
@@ -38,11 +39,34 @@ import { settleIdentity } from "../shared-wallet/access-gate";
*
* The "not injected" error stays SYNCHRONOUS: it is a wiring mistake rather than a runtime
* one, and it threw synchronously before this forwarder had anything to await.
*
* ── It also LISTENS on the way through, and that is the one argument it touches ──
* The real `init` delivers the session by calling its callback with
* `{ status: "loggedin", session }` — once, and it is the only channel that ever produces
* one (`@ng-org/web` `dist/ngweb.js:113-137`, VERIFIED). Until 2026-08-12 every application
* had to catch that event itself and hand the library a thunk unwrapping it, which is a
* shape the target never asks anyone to build and which two consumers in a row got wrong.
*
* So the callback in position 0 is WRAPPED: the wrapper reads the event, keeps the session
* (`shared-wallet/session.ts`), and then calls the caller's callback with that same event,
* unchanged and un-narrowed. Nothing else about the call moves — the remaining arguments and
* the return value pass straight through, and the caller's callback still sees exactly what
* the real `init` sent it. It is the one place a wrapper can be, because it is the one place
* that knows both what the caller asked and what the SDK will answer.
*
* A caller that passes NO callback is the same act with nobody listening — upstream accepts
* it (`callback: Function | null`, and the call site is guarded). The wrapper still goes in,
* so the package gets its session either way, and calls nothing afterwards.
*/
export function init(...args: any[]): any {
const f = getConfig().init;
if (!f) throw new Error("[ng-eventually] init() not injected — pass it to configure()");
return settleIdentity().then(() => f(...args));
const [callback, ...rest] = args;
const listen = (event: unknown): unknown => {
captureSession(event);
return typeof callback === "function" ? callback(event) : undefined;
};
return settleIdentity().then(() => f(listen, ...rest));
}
/** Forwards to the real `@ng-org/orm` `initNg` (ORM signals). */