66a40fbb89
Chaque étape de cet écran n'existe que parce qu'un wallet héberge plusieurs
identités. Une app qui l'implémente écrit du code qu'elle devra supprimer, et
pire, du code qui enseigne à ses auteurs un modèle que NextGraph n'a pas :
« je nomme mon identité ». Le premier consommateur en avait ~300 lignes — un
gate, un écran, un module wallet, un contexte d'identité, trois features. C'est
le travail de la bibliothèque, pas le sien.
`ensureIdentity()` : un appel, avant que l'app s'affiche. Il ne prend PAS
d'identifiant — nommer une identité est l'étape qui disparaîtra, donc elle ne
doit pas figurer dans la signature. Le jour où le wallet fournit l'identité,
l'appel se résout sans rien afficher et le code de l'appelant ne bouge pas.
L'écran est en DOM natif, sous shadow root : c'est une barrière technique
montrée avant qu'une application s'affiche, comme une demande de mot de passe
sur une bêta fermée. La lier à un framework obligerait chaque consommateur à
adopter ce framework pour un écran voué à disparaître.
L'ordre de résolution de l'identité est pinné par des tests, parce que s'y
tromper échoue en SILENCE : le parcours traverse deux partitions localStorage
distinctes — la page et l'iframe du broker — et seul l'URL franchit la
frontière. Si le stockage l'emportait, l'iframe lirait une identité vide,
provisionnerait un second utilisateur virtuel, et l'utilisateur reviendrait dans
un espace vide sans la moindre erreur.
Les identifiants du wallet partagé (fichier, mot de passe) passent par
`configure()` : ce sont des données de déploiement, et cet appel est déjà celui
qui devient inerte à la migration. Au passage, l'ancien champ `sharedWallet:
{ name, secret }` — inutilisé nulle part — est remplacé.
168 tests unitaires, typecheck vert.
70 lines
4.0 KiB
TypeScript
70 lines
4.0 KiB
TypeScript
/**
|
|
* @ng-eventually/client — the surface a consumer application codes against.
|
|
*
|
|
* Everything here has a target-SDK counterpart, verified or assumed, listed in
|
|
* `docs/api-contract.md`. Import `ng` / `useShape` from here rather than from the
|
|
* SDK during the polyfill period; at migration the build alias is removed and
|
|
* these resolve to the real SDK.
|
|
*
|
|
* **This entry carries no machinery.** The earlier header claimed it exposed "ONLY
|
|
* what `@ng-org/web` / `@ng-org/orm` expose", which was false as written: it also
|
|
* shipped the whole `store-registry` module (account resolution, cap registers,
|
|
* cache resets) and `accounts` (browser identity persistence, polyfill-era with no
|
|
* SDK counterpart). Both leaked machinery onto the entry whose promise is that it
|
|
* survives migration. `storeRegistry` is now the app-facing slice only
|
|
* (`store-registry-api.ts`); `accounts` moved to `/polyfill`.
|
|
*
|
|
* The polyfill bootstrap — `configure`, the capability helpers, the current user,
|
|
* identity persistence — lives at `@ng-eventually/client/polyfill`: everything an
|
|
* application needs TODAY that will not exist tomorrow, kept apart so what goes
|
|
* away is visible at the import line.
|
|
*/
|
|
|
|
export * from "./model/types";
|
|
export { useShape } from "./surface/use-shape";
|
|
export { watchShape } from "./surface/watch-shape";
|
|
export type { ShapeQuery, ShapeObservable } from "./surface/watch-shape";
|
|
export { init, initNg } from "./surface/lifecycle";
|
|
// The access gate: one call, before the app renders. It shows a technical barrier only
|
|
// while the shared wallet needs one — the day the wallet supplies the identity it
|
|
// resolves silently, and this line stays as it is (`shared-wallet/access-gate.ts`).
|
|
export { ensureIdentity } from "./shared-wallet/access-gate";
|
|
export type { SharedWalletConfig } from "./shared-wallet/access-gate";
|
|
export * as inbox from "./surface/inbox";
|
|
export * as docs from "./surface/docs";
|
|
export { subscribeDoc, subscribeDocs, docChangeType } from "./surface/subscribe";
|
|
export type { DocChange, DocChangeType, Unsubscribe } from "./surface/subscribe";
|
|
// `readUnion` is exposed as a function, not under a `readModel` namespace: "model" is
|
|
// neither the target's vocabulary nor neutral glue, and the namespace bought nothing —
|
|
// it held one published function. Renamed 2026-08-03 by the vocabulary check.
|
|
export { readUnion } from "./surface/read-model";
|
|
export type { UnionSubject } from "./surface/read-model";
|
|
export * as storeRegistry from "./surface/placement";
|
|
|
|
// SPARQL injection-safety helpers — so the app can reuse the same escaping /
|
|
// validation when it builds SPARQL by interpolation. `escapeLiteral` for string
|
|
// literals, `escapeIri` to embed untrusted values in an IRI, `assertNuri` to
|
|
// validate trusted-shaped NURIs before embedding them in an IRI.
|
|
export { escapeLiteral, escapeIri, assertNuri } from "./surface/sparql";
|
|
|
|
// NURI type guards — the doors through which an app's own `string` (read back
|
|
// from storage, a URL, JSON, a form) becomes a typed `Nuri` or `ReadCap`. `Nuri`
|
|
// and `ReadCap` are template literal types, so an app that narrows with these
|
|
// gets the same compile-time distinction the library uses internally — in
|
|
// particular, it cannot pass a bare reference where a cap is required. Narrow
|
|
// with these rather than casting: a cast re-opens exactly the confusion the
|
|
// types exist to close.
|
|
export { isNuri, hasReadCap } from "./model/nuri";
|
|
|
|
// SDK type re-exports — so the app imports these from @ng-eventually/client too,
|
|
// not from @ng-org. `export type` is ERASED at build, so this adds NO runtime
|
|
// @ng-org import to the lib (no risk of a duplicate SDK copy in the bundle).
|
|
export type { ShapeType, BaseType, Schema } from "@ng-org/shex-orm";
|
|
export type { DeepSignalSet } from "@ng-org/alien-deepsignals";
|
|
export type { NG } from "@ng-org/web";
|
|
|
|
import { makeNg } from "./surface/ng-proxy";
|
|
|
|
/** SDK-identical `ng` (wrapped). Drop-in replacement for `@ng-org/web`'s `ng`. */
|
|
export const ng: Record<string, any> = makeNg();
|