refactor(api): le bootstrap redescend de quatre appels à un

L'objectif acté était deux appels spécifiques au polyfill, voire un. Il en publiait
quatre. Chacun des trois de trop était une raison que la BIBLIOTHÈQUE a, pas un besoin
qu'une application a :

- **`configureStoreRegistry`** existait parce qu'il y a deux internes à câbler — le SDK
  injecté d'un côté, la session de l'autre. Vu de l'appelant, les deux disent « voici ce
  qu'il te faut pour tourner ». Replié dans `configure`, qui prend désormais
  `getSession` / `normalizeId` / `pointerGuard`.
- **`setCurrentUser`** n'a plus lieu d'être publié depuis que le portail d'accès est
  passé dans le polyfill : c'est lui qui pose l'identité. Et une application qui nomme
  sa propre identité est exactement le geste qui inverse le modèle — il ne doit pas
  exister d'appel publié vers lequel se tourner. Le harnais e2e, lui, joue plusieurs
  identités sur une même page ; il y accède par le chemin interne, ce qu'un harnais a
  le droit de faire et une application non.
- **`connectedUser`** est maintenant attendu DANS `ensureIdentity`. Ce n'était pas une
  commodité : la suite applicative avait montré qu'une app devait l'attendre elle-même,
  sinon une note qu'on venait de lui partager se lisait comme illisible. J'avais traité
  le symptôme dans l'app d'exemple ; le défaut était côté bibliothèque. En amont, ouvrir
  la session EST la connexion — aucune application n'attend un second appel.

Reste donc `configure({ … })`, plus `await ensureIdentity()` dont le site d'appel
survit à la migration : une application attendra toujours une session avant de rendre.

Le test étendu hier a fait son travail : les deux contrôles de contrat sont passés au
rouge sur `configureStoreRegistry`, `connectedUser` et `StoreRegistryDeps` dès que la
surface a bougé.

180 tests unitaires, e2e 40/40 (3,4 min) et applicatif 10/10 (0,8 min).
This commit is contained in:
Sylvain Duchesne
2026-08-07 12:06:15 +02:00
parent b98fcaa77d
commit 0455a408b6
26 changed files with 205 additions and 114 deletions
+45 -41
View File
@@ -19,34 +19,36 @@ Per the design principle (`README.md` § *Design principle*): an absent implemen
## 1. Bootstrap and configuration ## 1. Bootstrap and configuration
### Today — `@ng-eventually/sdk` (the POLYFILL-ERA block of `src/index.ts`; everything here is removed at migration) ### Today — `@ng-eventually/sdk`: **one call**
```ts ```ts
// all from shared-wallet/bootstrap.ts // shared-wallet/bootstrap.ts
export interface EventuallyConfig { export interface EventuallyConfig {
ng: NgLike; ng: NgLike; // the REAL @ng-org/web ng
useShape: UseShapeLike; useShape: UseShapeLike; // the REAL @ng-org/orm useShape
sharedWallet?: SharedWalletConfig; // the gate's, § 2bis getSession?: () => Promise<RegistrySession>; // the wallet session (a thunk)
normalizeId?: (id: string) => string;
pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number };
sharedWallet?: SharedWalletConfig; // the gate's, § 2bis
currentUser?: PrincipalId; currentUser?: PrincipalId;
debugAccessLog?: boolean; debugAccessLog?: boolean;
init?: (...args: any[]) => any; init?: (...args: any[]) => any;
initNg?: (...args: any[]) => any; initNg?: (...args: any[]) => any;
} }
export function configure(c: EventuallyConfig): void; export function configure(c: EventuallyConfig): void;
export interface StoreRegistryDeps {
getSession: () => Promise<RegistrySession>;
normalizeId?: (id: string) => string;
pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number };
}
export function configureStoreRegistry(deps: StoreRegistryDeps): void;
// NOT published (2026-08-07, with the entry merge) — merging the two doors made
// publishing these a visible choice rather than an inherited one, and the choice is no:
// getConfig, getStoreRegistryDeps internal wiring; the surface reaches them by import
// resetConfig, resetStoreRegistry test resets; the suite reaches them the same way
``` ```
**The count is the contract here.** The agreed target was two polyfill-era calls, or one; it had drifted to four, and each extra one was a reason the LIBRARY has rather than a need an application has. Four became one on 2026-08-07:
| Was published | Where it went |
|---|---|
| `configureStoreRegistry` + `StoreRegistryDeps` | folded into `configure` — two bootstrap calls existed because the library has two internals, which is not a reason a caller should pay |
| `setCurrentUser` | the access gate sets the identity (§ 2bis). An application naming its own identity is the gesture that INVERTS the model; it must not have a published call to reach for |
| `connectedUser` | `ensureIdentity` awaits it. Upstream, opening the session IS the connection — no application awaits a second call |
| `getConfig`, `getStoreRegistryDeps`, `resetConfig`, `resetStoreRegistry` | internal wiring and test resets, reached by their internal path (2026-08-07, with the entry merge) |
So an application's whole bootstrap is `configure({ … })` plus `await ensureIdentity()` — and the second of those keeps its call site after migration.
### Target ### Target
**NO COUNTERPART, by design.** The whole subject is the polyfill bootstrap: it exists to inject the real SDK without a hard import (build-alias safety). At migration the consumer initializes the real SDK directly, with the two calls in § 2, and `configure` / `configureStoreRegistry` are deleted (`docs/migration-guide.md` § 7). Nothing in the target takes an "injected `ng`". **NO COUNTERPART, by design.** The whole subject is the polyfill bootstrap: it exists to inject the real SDK without a hard import (build-alias safety). At migration the consumer initializes the real SDK directly, with the two calls in § 2, and `configure` / `configureStoreRegistry` are deleted (`docs/migration-guide.md` § 7). Nothing in the target takes an "injected `ng`".
@@ -548,16 +550,19 @@ export async function openDocumentInbox(doc: Nuri): Promise<Nuri>;
### Today ### Today
```ts ```ts
// PUBLISHED — the POLYFILL-ERA block of `src/index.ts`, both with no counterpart. // PUBLISHED: nothing. Identity is established by `ensureIdentity()` (§ 2bis) and the
export function setCurrentUser(id: PrincipalId | null): void; // shared-wallet/bootstrap.ts // connection is awaited inside it.
export async function connectedUser(): Promise<void>; // emulated-verifier/connect.ts //
// NOT published, and each removal is a gesture an application no longer performs:
// NOT published (removed 2026-08-05) — identity persistence is the application's job // setCurrentUser (2026-08-07) naming one's own identity — the step that inverts the
// upstream too, and asking the library who you signed in is a shared-wallet convenience: // model. The gate does it; the e2e harness, which plays
// shared-wallet/virtual-users.ts IdentityStore, browserIdentityStore, // several identities on one page, reaches it internally.
// VirtualUserStorage, ACCOUNT_STORAGE_KEY // connectedUser (2026-08-07) awaited inside `ensureIdentity`; upstream, opening the
// shared-wallet/bootstrap.ts getCurrentUser // session IS the connection.
// The access gate persists what IT needs (§ 2bis); nothing else has to be exposed. // getCurrentUser (2026-08-05) an application knows who it signed in.
// IdentityStore, browserIdentityStore, VirtualUserStorage, ACCOUNT_STORAGE_KEY
// (2026-08-05) persisting an identity is the application's job
// upstream too; the gate persists what IT needs.
``` ```
### Target ### Target
@@ -573,8 +578,8 @@ declare function user_disconnect(user_id: string): Promise<void>;
``` ```
- `IdentityStore` / `browserIdentityStore` (the persisted identity id) — **NO COUNTERPART**; they exist only because every virtual user shares one wallet, and they are no longer published at all. Removed at migration (`docs/migration-guide.md` § 5). - `IdentityStore` / `browserIdentityStore` (the persisted identity id) — **NO COUNTERPART**; they exist only because every virtual user shares one wallet, and they are no longer published at all. Removed at migration (`docs/migration-guide.md` § 5).
- `setCurrentUser`**NO COUNTERPART**; the relay of an identity the broker cannot see. Disappears with the shared wallet. `getCurrentUser` was its read side and is gone from the surface: an application knows who it signed in. - `setCurrentUser`**NO COUNTERPART**; the relay of an identity the broker cannot see. Disappears with the shared wallet, and is no longer published: the gate is the only caller an application needs.
- `connectedUser()` — the awaitable form of what the target does **automatically**: the recipient's verifier processes its inbox as messages arrive/at connection (`Verifier::inbox`, `engine/verifier/src/verifier.rs:1674`). VERIFIED at level 1 that no consumer call is needed upstream; the polyfill fires it from `setCurrentUser` for the same reason. A consumer should treat it as "await a deterministic start" (tests), not as an operation the future SDK will name. - `connectedUser()` (internal since 2026-08-07) — the awaitable form of what the target does **automatically**: the recipient's verifier processes its inbox as messages arrive/at connection (`Verifier::inbox`, `engine/verifier/src/verifier.rs:1674`). VERIFIED at level 1 that no consumer call is needed upstream; the polyfill fires it from `setCurrentUser` for the same reason. A consumer should treat it as "await a deterministic start" (tests), not as an operation the future SDK will name.
--- ---
@@ -625,21 +630,20 @@ Exported, but not SDK surface. Coding against these builds knowledge that migrat
### `@ng-eventually/sdk` — `src/index.ts` (the only entry since 2026-08-07) ### `@ng-eventually/sdk` — `src/index.ts` (the only entry since 2026-08-07)
```text ```text
direct: BaseType, DeepSignalSet, DocChange, DocChangeType, EventuallyConfig, InboxScope, NG, NgLike, Nuri, NuriLike, PrincipalId, ReadCap, RegistrySession, Schema, Scope, ShapeObservable, ShapeQuery, ShapeType, SharedWalletConfig, StoreRegistryDeps, UnionSubject, Unsubscribe, UseShapeLike, configure, configureStoreRegistry, connectedUser, docChangeType, ensureIdentity, init, initNg, ng, readUnion, setCurrentUser, subscribeDoc, subscribeDocs, useShape, watchShape direct: BaseType, DeepSignalSet, DocChange, DocChangeType, EventuallyConfig, InboxScope, NG, NgLike, Nuri, NuriLike, PrincipalId, ReadCap, RegistrySession, Schema, Scope, ShapeObservable, ShapeQuery, ShapeType, SharedWalletConfig, UnionSubject, Unsubscribe, UseShapeLike, configure, docChangeType, ensureIdentity, init, initNg, ng, readUnion, subscribeDoc, subscribeDocs, useShape, watchShape
docs: depositInto, docCreate, sparqlQuery, sparqlUpdate docs: depositInto, docCreate, sparqlQuery, sparqlUpdate
inbox: Deposit, PostOptions, materialize, post, postToDocument, processInbox, read, readForDocument, readSynced, share, watch inbox: Deposit, PostOptions, materialize, post, postToDocument, processInbox, read, readForDocument, readSynced, share, watch
storeRegistry: createEntityDoc, listMyEntityDocs, openDocumentInbox, resolveScopeGraph, resolveWriteGraph storeRegistry: createEntityDoc, listMyEntityDocs, openDocumentInbox, resolveScopeGraph, resolveWriteGraph
``` ```
**Of these, four are POLYFILL-ERA and have no target counterpart**`configure`, **Of these, exactly ONE is polyfill-era with no target counterpart**`configure` (plus
`configureStoreRegistry`, `setCurrentUser`, `connectedUser` (plus the types the types `EventuallyConfig`, `RegistrySession`). It is the deletion list, and
`EventuallyConfig`, `StoreRegistryDeps`, `RegistrySession`). They are the deletion list, `src/index.ts` groups it under a heading that says so. `ensureIdentity` is a second in
and `src/index.ts` groups them under a heading that says so. `ensureIdentity` is a fifth substance — the shared-wallet gate — but its *call site* survives (§ 2bis).
in substance — the shared-wallet gate — but the *call site* survives (§ 2bis).
Six symbols the previous `/polyfill` entry published are gone from the surface entirely: Nine symbols published before 2026-08-07 are gone from the surface: `configureStoreRegistry`
`getConfig` and `getStoreRegistryDeps` (internal wiring, reached through and `StoreRegistryDeps` (folded into `configure`), `setCurrentUser` and `connectedUser`
`shared-wallet/bootstrap`), `resetConfig` / `resetStoreRegistry` / `resetCaps` (test (§ 1), `getConfig` / `getStoreRegistryDeps` (internal wiring), `resetConfig` /
resets, reached by their internal path), and the direct `share` re-export — `inbox.share` `resetStoreRegistry` / `resetCaps` (test resets), and the direct `share` re-export —
was always the same function, and publishing it twice blurred the boundary it was meant `inbox.share` was always the same function, and publishing it twice blurred the boundary
to mark. it was meant to mark.
+2 -2
View File
@@ -4,7 +4,7 @@
**Scope.** The complement of [`docs/api-contract.md`](./api-contract.md): every module export under `packages/sdk/src/` that is NOT reachable from the two published entry points (`package.json` maps exactly `.``src/index.ts` and `./polyfill``src/polyfill.ts`). A consumer never reads this document; a maintainer does. The internal code is held to the same standard as the surface — as close as possible to what NextGraph does or plans — so every subject below carries the same target-side analysis. Written 2026-08-04, verified against the `nextgraph-rs` clone (HEAD `213338f6`) and the installed `@ng-org/web@0.1.2-alpha.13` declarations (`node_modules/.bun/@ng-org+web@0.1.2-alpha.13/node_modules/@ng-org/web/dist/index.d.ts`, hereafter `index.d.ts`). **Scope.** The complement of [`docs/api-contract.md`](./api-contract.md): every module export under `packages/sdk/src/` that is NOT reachable from the two published entry points (`package.json` maps exactly `.``src/index.ts` and `./polyfill``src/polyfill.ts`). A consumer never reads this document; a maintainer does. The internal code is held to the same standard as the surface — as close as possible to what NextGraph does or plans — so every subject below carries the same target-side analysis. Written 2026-08-04, verified against the `nextgraph-rs` clone (HEAD `213338f6`) and the installed `@ng-org/web@0.1.2-alpha.13` declarations (`node_modules/.bun/@ng-org+web@0.1.2-alpha.13/node_modules/@ng-org/web/dist/index.d.ts`, hereafter `index.d.ts`).
**How the boundary was computed — mechanically, from the `export` statements.** `index.ts` re-exports wholesale (`export *` / `export * as ns`) from `types.ts`, `inbox.ts`, `docs.ts`, `surface/read-model.ts`, and by name everything `surface/use-shape.ts`, `surface/watch-shape.ts`, `lifecycle.ts`, `sparql.ts` export, and `subscribeDoc`/`subscribeDocs`/`docChangeType` (+ types) from `subscribe.ts`; its `storeRegistry` namespace is the **`surface/placement.ts` slice only** (`createEntityDoc`, `listMyEntityDocs`, `resolveScopeGraph`, `resolveWriteGraph`, `openDocumentInbox`). `model/nuri.ts` is now internal in full — the guards `isNuri`/`hasReadCap` stopped being published when the entries became permissive-in (`NuriLike` validated at the door). *(The second entry, `polyfill.ts`, was merged into `index.ts` on 2026-08-07; its polyfill-era symbols — `configure`, `configureStoreRegistry`, `setCurrentUser`, `connectedUser` and their types — now sit in a marked block of the single entry, and the machinery accessors and test resets it used to publish are no longer published at all.)* Everything else that carries `export` in a `src/` module is internal and inventoried here. Eight modules are internal in their entirety: `shared-wallet/access-log.ts`, `emulated-verifier/machinery.ts`, `surface/ng-proxy.ts`, `emulated-verifier/open-repo.ts`, `shared-wallet/outbox-log.ts`, `shared-wallet/physical.ts`, `emulated-verifier/reach.ts`, `emulated-verifier/read-filter.ts`. Four are internal in part: `nuri.ts`, `emulated-verifier/connect.ts`, `subscribe.ts`, `shared-wallet/account-registry.ts`. **How the boundary was computed — mechanically, from the `export` statements.** `index.ts` re-exports wholesale (`export *` / `export * as ns`) from `types.ts`, `inbox.ts`, `docs.ts`, `surface/read-model.ts`, and by name everything `surface/use-shape.ts`, `surface/watch-shape.ts`, `lifecycle.ts`, `sparql.ts` export, and `subscribeDoc`/`subscribeDocs`/`docChangeType` (+ types) from `subscribe.ts`; its `storeRegistry` namespace is the **`surface/placement.ts` slice only** (`createEntityDoc`, `listMyEntityDocs`, `resolveScopeGraph`, `resolveWriteGraph`, `openDocumentInbox`). `model/nuri.ts` is now internal in full — the guards `isNuri`/`hasReadCap` stopped being published when the entries became permissive-in (`NuriLike` validated at the door). *(The second entry, `polyfill.ts`, was merged into `index.ts` on 2026-08-07, and the polyfill-era block was then cut to ONE published call, `configure`. `configureStoreRegistry`, `setCurrentUser` and `connectedUser` became internal the same day — folded, replaced by the gate, and awaited inside it respectively; the machinery accessors and test resets are internal too. All four are inventoried below.)* Everything else that carries `export` in a `src/` module is internal and inventoried here. Eight modules are internal in their entirety: `shared-wallet/access-log.ts`, `emulated-verifier/machinery.ts`, `surface/ng-proxy.ts`, `emulated-verifier/open-repo.ts`, `shared-wallet/outbox-log.ts`, `shared-wallet/physical.ts`, `emulated-verifier/reach.ts`, `emulated-verifier/read-filter.ts`. Four are internal in part: `nuri.ts`, `emulated-verifier/connect.ts`, `subscribe.ts`, `shared-wallet/account-registry.ts`.
**Labels** are those of `docs/api-contract.md`: **PASSTHROUGH (level 3/2, VERIFIED)**, **LEVEL-1 SHAPE (model VERIFIED, JS surface ASSUMED)**, **ASSUMPTION**, **NO COUNTERPART**. Level numbers per `README.md` § *The three references*: 3 = JS ORM, 2 = wasm binding (`@ng-org/web`), 1 = Rust engine. One label recurs here that the surface contract rarely needs: **NO COUNTERPART, shared-wallet machinery** — the code below the emulation's floor, which the target has no image of because the target has no shared wallet. Per the design principle, an absent implementation is never treated as evidence about the future. **Labels** are those of `docs/api-contract.md`: **PASSTHROUGH (level 3/2, VERIFIED)**, **LEVEL-1 SHAPE (model VERIFIED, JS surface ASSUMED)**, **ASSUMPTION**, **NO COUNTERPART**. Level numbers per `README.md` § *The three references*: 3 = JS ORM, 2 = wasm binding (`@ng-org/web`), 1 = Rust engine. One label recurs here that the surface contract rarely needs: **NO COUNTERPART, shared-wallet machinery** — the code below the emulation's floor, which the target has no image of because the target has no shared wallet. Per the design principle, an absent implementation is never treated as evidence about the future.
@@ -139,7 +139,7 @@ The polyfill of capability-based read access: a Proxy view over the reactive set
export function startConnect(): void; export function startConnect(): void;
``` ```
Fire-and-forget wrapper over the published `connectedUser()` (restore Links, then drain every inbox), called by `setCurrentUser` so inbox processing is the library's job, not the app's. Fire-and-forget wrapper over `connectedUser()` (internal since 2026-08-07, awaited inside `ensureIdentity`) (restore Links, then drain every inbox), called by `setCurrentUser` so inbox processing is the library's job, not the app's.
- **LEVEL-1 SHAPE for the timing, VERIFIED**: upstream the recipient's verifier processes inbox messages as they arrive, with no consumer call (`Verifier::inbox``process_inbox`, `engine/verifier/src/verifier.rs:1674-1690`); firing on connection is the emulation's equivalent moment. The restore-before-drain order is a lib choice; upstream "restore" does not exist as a step (applied caps are already in the User branch replay). - **LEVEL-1 SHAPE for the timing, VERIFIED**: upstream the recipient's verifier processes inbox messages as they arrive, with no consumer call (`Verifier::inbox``process_inbox`, `engine/verifier/src/verifier.rs:1674-1690`); firing on connection is the emulation's equivalent moment. The restore-before-drain order is a lib choice; upstream "restore" does not exist as a step (applied caps are already in the User branch replay).
- `startConnect` itself disappears at migration; the automatic-processing behaviour it fabricates is native. - `startConnect` itself disappears at migration; the automatic-processing behaviour it fabricates is native.
+3 -3
View File
@@ -142,7 +142,7 @@ public/protected/private stores — on top of one shared wallet.
`listMyEntityDocs(id, scope)` (its own account, bounded — no cross-account fan-out). `listMyEntityDocs(id, scope)` (its own account, bounded — no cross-account fan-out).
- **Generic by construction.** The registry knows only the three native scopes, - **Generic by construction.** The registry knows only the three native scopes,
zero application entity kind. The consumer application maps its entities to a scope zero application entity kind. The consumer application maps its entities to a scope
and injects the session + identity-id normalization via `configureStoreRegistry({ and injects the session + identity-id normalization through `configure({
getSession, normalizeId })` (`shared-wallet/bootstrap.ts`, published by the entry). getSession, normalizeId })` (`shared-wallet/bootstrap.ts`, published by the entry).
The `store≠document` two axes materialize here directly: the registry moves along The `store≠document` two axes materialize here directly: the registry moves along
@@ -238,7 +238,7 @@ store-id:
Both resolve the native store ids from the injected session Both resolve the native store ids from the injected session
(`RegistrySession.protectedStoreId` / `publicStoreId`, alongside the existing (`RegistrySession.protectedStoreId` / `publicStoreId`, alongside the existing
`privateStoreId` anchor). The consumer application hands the whole session to the `privateStoreId` anchor). The consumer application hands the whole session to the
lib at the one injection point (`configureStoreRegistry({ getSession })`) — that is lib at the one injection point (`configure({ getSession })`) — that is
wiring, not placement logic; everything else in the consumer application speaks only wiring, not placement logic; everything else in the consumer application speaks only
in scopes. If the session omits `protectedStoreId`, the non-private scopes fall back in scopes. If the session omits `protectedStoreId`, the non-private scopes fall back
to the private store rather than emit a broken NURI. to the private store rather than emit a broken NURI.
@@ -361,7 +361,7 @@ another name.
### Sharing, publication, and the recipient ### Sharing, publication, and the recipient
- **`setCurrentUser(id)` (`shared-wallet/bootstrap.ts`)** — the SDK's "current identity" call. - **`setCurrentUser(id)` (`shared-wallet/bootstrap.ts`, INTERNAL since 2026-08-07 — `ensureIdentity` is what an application calls)** — the SDK's "current identity" call.
It selects *whose* caps are consulted, lazily, so the delivered subset always It selects *whose* caps are consulted, lazily, so the delivered subset always
reflects the identity in effect at read time. reflects the identity in effect at read time.
- **`inbox.share(doc, toUser)`** — the one sharing act the lib exposes. Recipients - **`inbox.share(doc, toUser)`** — the one sharing act the lib exposes. Recipients
+1 -1
View File
@@ -23,7 +23,7 @@ The four journeys the suite runs, and what each proves:
| Bob leaves a message on Alice's note, and only Alice reads it | A depositor FINDS the address from the note itself; depositing grants no reading | | Bob leaves a message on Alice's note, and only Alice reads it | A depositor FINDS the address from the note itself; depositing grants no reading |
| Each actor's list holds their own notes | The boundary, seen from the only place that matters: the screen | | Each actor's list holds their own notes | The boundary, seen from the only place that matters: the screen |
It has also found three defects of its own, each an application-side one the harness could not see: `connectedUser()` had to be awaited at sign-in (a note just shared with you reads as unreadable otherwise), a stale answer stayed on screen beside a fresh question, and changing the scope did not refresh the list. It has also found three defects of its own, each one the harness could not see. The first turned out to be a LIBRARY defect rather than an application one: the connection work had to be awaited at sign-in, or a note just shared with you read as unreadable — so `ensureIdentity` now awaits it, and `connectedUser` left the published surface. The other two were the application's: a stale answer stayed on screen beside a fresh question, and changing the scope did not refresh the list.
## Running it ## Running it
+17 -20
View File
@@ -36,13 +36,10 @@ import {
readUnion, readUnion,
storeRegistry, storeRegistry,
subscribeDoc, subscribeDoc,
connectedUser,
type Nuri, type Nuri,
type Scope, type Scope,
// Polyfill-era — these three go away, and they are the whole of what goes away. // Polyfill-era — ONE call, and it is the whole of what goes away.
configure, configure,
configureStoreRegistry,
setCurrentUser,
} from "@ng-eventually/sdk"; } from "@ng-eventually/sdk";
import { ng as realNg, init as realInit } from "@ng-org/web"; import { ng as realNg, init as realInit } from "@ng-org/web";
@@ -57,11 +54,11 @@ interface Note {
body: string; body: string;
} }
// --- bootstrap: the two polyfill-era calls --------------------------------- // --- bootstrap: ONE polyfill-era call --------------------------------------
// //
// Everything else an application calls is SDK surface, preserved at migration. These // Everything else an application calls is SDK surface, preserved at migration. This one
// two are the scaffolding: `configure` becomes inert (the app will import the real SDK) // is the scaffolding, and at migration it goes: the app imports the real SDK, and the
// and the identity will come from the wallet instead of a barrier. // identity comes from the wallet instead of a barrier.
let session: { session_id: string } | null = null; let session: { session_id: string } | null = null;
const sessionReady = new Promise<{ session_id: string }>((resolve) => { const sessionReady = new Promise<{ session_id: string }>((resolve) => {
@@ -81,9 +78,6 @@ configure({
fileUrl: "/shared-wallet.ngw", fileUrl: "/shared-wallet.ngw",
password: (globalThis as { __NOTEBOOK_WALLET_PASSWORD__?: string }).__NOTEBOOK_WALLET_PASSWORD__ ?? "", password: (globalThis as { __NOTEBOOK_WALLET_PASSWORD__?: string }).__NOTEBOOK_WALLET_PASSWORD__ ?? "",
}, },
});
configureStoreRegistry({
getSession: async () => { getSession: async () => {
const s = session ?? (await sessionReady); const s = session ?? (await sessionReady);
return { return {
@@ -190,18 +184,18 @@ function currentIdentity(): string {
* Sign in. The library shows its access barrier when it needs one; the day the wallet * Sign in. The library shows its access barrier when it needs one; the day the wallet
* supplies the identity, this resolves silently and nothing here changes. * supplies the identity, this resolves silently and nothing here changes.
* *
* The `connectedUser()` await is not optional decoration, and the applicative e2e is * One await, and it covers everything: the gate resolves the identity AND waits for the
* what found that out: setting an identity FIRES the connection work — restoring what * connection work it fires (restoring what others shared with you, draining your
* others shared with you, draining your inboxes — but does not wait for it. Render * inboxes). The application used to have to await that second part itself — the
* before it lands and a note someone just shared reads as unreadable, which looks like * applicative e2e is what found it out, because a note someone had just shared read as
* a permission problem and is a timing one. At migration this becomes the session * unreadable, which looks like a permission problem and is a timing one. The library
* opening, and the await stays exactly where it is. * absorbed it: upstream, opening the session IS the connection, and no application
* awaits a second call.
*/ */
async function signIn(): Promise<void> { async function signIn(): Promise<void> {
await ensureIdentity(); await ensureIdentity();
await sessionReady; await sessionReady;
identity = readIdentityBack(); identity = readIdentityBack();
await connectedUser();
} }
/** The library owns the identity; the app asks for it rather than remembering it. */ /** The library owns the identity; the app asks for it rather than remembering it. */
@@ -280,5 +274,8 @@ async function main(): Promise<void> {
void main(); void main();
// The e2e suite drives this app through the DOM. It exposes nothing else: a test that // The e2e suite drives this app through the DOM. It exposes nothing else: a test that
// needed a back door would be testing something an application cannot do. // needed a back door would be testing something an application cannot do. `watchNote`
(globalThis as { __notebook?: unknown }).__notebook = { watchNote, setCurrentUser }; // is here because reactivity has no visible surface in this UI yet — not as an escape
// hatch, and it takes no identity: switching user means reloading with another `?ng-id=`,
// exactly as switching upstream means opening another wallet.
(globalThis as { __notebook?: unknown }).__notebook = { watchNote };
+9 -9
View File
@@ -5,10 +5,11 @@ One entry point. Most of what it publishes has the same signature as the future
and is a drop-in for `@ng-org/web` / `@ng-org/orm`: as NextGraph matures it resolves to and is a drop-in for `@ng-org/web` / `@ng-org/orm`: as NextGraph matures it resolves to
the real SDK (build alias removed) with no code change. the real SDK (build alias removed) with no code change.
**Four calls do not, and they are the whole of what you will delete:** `configure`, **One call does not, and it is the whole of what you will delete:** `configure`. It
`configureStoreRegistry`, `setCurrentUser`, `connectedUser`. They exist because one exists because one shared wallet hosts every user; upstream, an application imports the
shared wallet hosts every user; upstream, an application imports the SDK and each user SDK and each user opens their own wallet. `src/index.ts` groups it under a heading that
opens their own wallet. `src/index.ts` groups them under a heading that says so. says so. (`ensureIdentity` is a second in substance — the shared-wallet gate — but its
call site survives: an application still awaits a session before it renders.)
*(There were two entry points until 2026-08-07, `.` and `./polyfill`, and the second one *(There were two entry points until 2026-08-07, `.` and `./polyfill`, and the second one
WAS that list. One door is easier to import from and says less — hence the grouping, and WAS that list. One door is easier to import from and says less — hence the grouping, and
@@ -27,13 +28,12 @@ Per-symbol, with the target signature and an epistemic label on every claim:
import { import {
// SDK-shaped — the real SDK replaces these in place. // SDK-shaped — the real SDK replaces these in place.
ensureIdentity, storeRegistry, inbox, readUnion, docs, ensureIdentity, storeRegistry, inbox, readUnion, docs,
// Polyfill-era — these go away, and they are the whole of what goes away. // Polyfill-era — one call, and it is the whole of what goes away.
configure, configureStoreRegistry, setCurrentUser, configure,
} from "@ng-eventually/sdk"; } from "@ng-eventually/sdk";
configure({ ng: realNg, useShape: realUseShape, sharedWallet }); configure({ ng: realNg, useShape: realUseShape, getSession, sharedWallet });
configureStoreRegistry({ getSession }); await ensureIdentity(); // resolves who I am, and waits for the connection work
await ensureIdentity(); // who am I (shared wallet)
const doc = await storeRegistry.createEntityDoc(me, "protected"); const doc = await storeRegistry.createEntityDoc(me, "protected");
await docs.sparqlUpdate(sid, `INSERT DATA { … }`, doc); await docs.sparqlUpdate(sid, `INSERT DATA { … }`, doc);
const subjects = await readUnion(await storeRegistry.listMyEntityDocs(me, "protected")); const subjects = await readUnion(await storeRegistry.listMyEntityDocs(me, "protected"));
+11 -4
View File
@@ -17,9 +17,6 @@
import { ng as realNg, init as realInit } from "@ng-org/web"; import { ng as realNg, init as realInit } from "@ng-org/web";
import { import {
configure, configure,
configureStoreRegistry,
setCurrentUser,
connectedUser,
docs, docs,
subscribeDoc, subscribeDoc,
subscribeDocs, subscribeDocs,
@@ -33,7 +30,17 @@ import {
// application must not — but through the internal path, never the published entry. // application must not — but through the internal path, never the published entry.
// `storeRegistry` above is the app-facing slice; these are the shim internals. // `storeRegistry` above is the app-facing slice; these are the shim internals.
import * as registryInternals from "../src/shared-wallet/account-registry"; import * as registryInternals from "../src/shared-wallet/account-registry";
import { getCaps, getCurrentUser, resetCaps } from "../src/shared-wallet/bootstrap"; // The harness plays SEVERAL identities on one page — something no application does, and
// the reason `setCurrentUser` / `configureStoreRegistry` are no longer published. It
// reaches them by their internal path, like the rest of its machinery.
import {
configureStoreRegistry,
setCurrentUser,
getCaps,
getCurrentUser,
resetCaps,
} from "../src/shared-wallet/bootstrap";
import { connectedUser } from "../src/emulated-verifier/connect";
import * as virtualUsers from "../src/shared-wallet/virtual-users"; import * as virtualUsers from "../src/shared-wallet/virtual-users";
import { ensureIdentity } from "@ng-eventually/sdk"; import { ensureIdentity } from "@ng-eventually/sdk";
// The harness narrows for its OWN assertions; a consumer never has to (the entries take // The harness narrows for its OWN assertions; a consumer never has to (the entries take
+20 -8
View File
@@ -78,16 +78,28 @@ export type { NG } from "@ng-org/web";
* Inject the real SDK, and tell the library about the shared wallet. Upstream nothing * Inject the real SDK, and tell the library about the shared wallet. Upstream nothing
* is injected an application imports the SDK and opens its own wallet so this call * is injected an application imports the SDK and opens its own wallet so this call
* is the shape of that absence. `docs/api-contract.md` § 1. * is the shape of that absence. `docs/api-contract.md` § 1.
*
* **It is the ONLY call here**, and keeping it that way is the design target: an
* application's bootstrap should be one line to delete, not four.
*/ */
export { configure, configureStoreRegistry, setCurrentUser } from "./shared-wallet/bootstrap"; export { configure } from "./shared-wallet/bootstrap";
export type { EventuallyConfig, StoreRegistryDeps } from "./shared-wallet/bootstrap"; export type { EventuallyConfig } from "./shared-wallet/bootstrap";
export type { RegistrySession } from "./shared-wallet/account-registry"; export type { RegistrySession } from "./shared-wallet/account-registry";
/**
* Await the connection work `setCurrentUser` fires: restore what was shared with this // --- what this block deliberately does NOT contain --------------------------
* user, and drain its inboxes. An application need not call it the work runs anyway //
* but it may want to know it has finished. Upstream this is the session opening. // Three calls were published here and removed on 2026-08-07, when the count had drifted
*/ // to four against a target of two. Each removal is a thing an application no longer does:
export { connectedUser } from "./emulated-verifier/connect"; //
// - `configureStoreRegistry` — folded into `configure`. Two bootstrap calls existed
// because the library has two internals, which is not a reason a caller should pay.
// - `setCurrentUser` — the access gate sets the identity (`ensureIdentity`, below).
// An application naming its own identity is the gesture that inverts the model, and
// it must not have a published call to reach for. The e2e harness plays several
// identities on one page and reaches it by its internal path, which is what a
// harness is allowed to do and an application is not.
// - `connectedUser` — `ensureIdentity` awaits it. Upstream, opening the session IS the
// connection; no application awaits a second call, so ours should not either.
// ── the access gate — polyfill-era in substance, one line in the app ──────── // ── the access gate — polyfill-era in substance, one line in the app ────────
// One call before the app renders. It shows a technical barrier only while the shared // One call before the app renders. It shows a technical barrier only while the shared
+23 -2
View File
@@ -45,6 +45,7 @@ import {
getStoreRegistryDeps, getStoreRegistryDeps,
setCurrentUser, setCurrentUser,
} from "./bootstrap"; } from "./bootstrap";
import { connectedUser } from "../emulated-verifier/connect";
/** /**
* Normalize an identifier the SAME way the shim keys accounts on. * Normalize an identifier the SAME way the shim keys accounts on.
@@ -221,12 +222,12 @@ function askForIdentity(cfg: SharedWalletConfig): Promise<string> {
* the URL, and a plain reload finds it in storage. * the URL, and a plain reload finds it in storage.
*/ */
export async function ensureIdentity(): Promise<void> { export async function ensureIdentity(): Promise<void> {
if (getCurrentUser() !== null) return; if (getCurrentUser() !== null) return connected();
const known = storedIdentity(); const known = storedIdentity();
if (known) { if (known) {
setCurrentUser(known); setCurrentUser(known);
return; return connected();
} }
const cfg = getConfig().sharedWallet; const cfg = getConfig().sharedWallet;
@@ -249,4 +250,24 @@ export async function ensureIdentity(): Promise<void> {
const normalized = normalizeIdentity(chosen); const normalized = normalizeIdentity(chosen);
rememberIdentity(normalized); rememberIdentity(normalized);
setCurrentUser(normalized); setCurrentUser(normalized);
return connected();
}
/**
* Wait for the connection work `setCurrentUser` fires restoring what others shared
* with this user, draining its inboxes before this call resolves.
*
* **Not a convenience: a correctness fix, found by the applicative e2e.** Setting an
* identity FIRES that work and does not wait for it. An application that rendered on
* `ensureIdentity()` alone could read a note someone had just shared with it as
* unreadable which looks like a permission problem and is a timing one, in the one
* place where the difference is invisible (nothing throws; a read is simply empty).
*
* Doing it here rather than exposing `connectedUser()` is the point: the awaited thing
* has NO counterpart upstream there, opening the session IS the connection, and no
* application awaits a second call. So the polyfill absorbs it, and an application's
* bootstrap keeps the shape it will still have after migration.
*/
async function connected(): Promise<void> {
await connectedUser();
} }
+40 -8
View File
@@ -51,17 +51,40 @@ export interface StoreRegistryDeps {
pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number }; pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number };
} }
/**
* Everything the polyfill needs, in ONE call.
*
* It used to take two `configure` for the SDK injection, `configureStoreRegistry` for
* the session because the two belonged to different internals. That is a reason the
* library has, not one an application should pay for: from a caller's side both are
* "here is what you need to run", and two bootstrap calls is one more thing to delete
* at migration than there needs to be. Merged 2026-08-07; the registry's own wiring
* function stays internal.
*/
export interface EventuallyConfig { export interface EventuallyConfig {
/** The REAL `@ng-org/web` `ng` (injected to avoid a hard import / alias loop). */
ng: NgLike;
/** The REAL `@ng-org/orm` `useShape`. */
useShape: UseShapeLike;
/**
* Resolve the wallet session. Shared-wallet only: upstream the session IS the user, so
* there is nothing to inject an application opens its wallet and the SDK knows.
* A thunk, so it may be given before the session exists.
*/
getSession?: () => Promise<RegistrySession>;
/** Normalize an identity id for shim keying. Default: trim. */
normalizeId?: (id: string) => string;
/**
* POINTER micro-guard budget see {@link StoreRegistryDeps.pointerGuard}. Left unset
* a single read, which keeps the synchronous unit fakes fast.
*/
pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number };
/** /**
* The shared wallet this deployment hands out, and what the access gate needs to do * The shared wallet this deployment hands out, and what the access gate needs to do
* it (`shared-wallet/access-gate.ts`). Absent no gate; the caller sets the identity * it (`shared-wallet/access-gate.ts`). Absent no gate; the caller sets the identity
* itself. Disappears with the gate: upstream a user opens their own wallet. * itself. Disappears with the gate: upstream a user opens their own wallet.
*/ */
sharedWallet?: SharedWalletConfig; sharedWallet?: SharedWalletConfig;
/** The REAL `@ng-org/web` `ng` (injected to avoid a hard import / alias loop). */
ng: NgLike;
/** The REAL `@ng-org/orm` `useShape`. */
useShape: UseShapeLike;
/** Initial current user; may also be set later via {@link setCurrentUser}. */ /** Initial current user; may also be set later via {@link setCurrentUser}. */
currentUser?: PrincipalId; currentUser?: PrincipalId;
/** /**
@@ -114,6 +137,16 @@ export function configure(c: EventuallyConfig): void {
cfg = c; cfg = c;
currentUser = c.currentUser ?? null; currentUser = c.currentUser ?? null;
setAccessLog(c.debugAccessLog ?? false); setAccessLog(c.debugAccessLog ?? false);
// The session wiring is part of the same act — see {@link EventuallyConfig}. Omitted
// only by unit suites that never touch the registry; those get the same
// "must be configured" error they got before, from `getStoreRegistryDeps`.
if (c.getSession) {
configureStoreRegistry({
getSession: c.getSession,
...(c.normalizeId ? { normalizeId: c.normalizeId } : {}),
...(c.pointerGuard ? { pointerGuard: c.pointerGuard } : {}),
});
}
} }
/** @internal — used by the SDK-shaped wrappers to reach the injected real SDK. */ /** @internal — used by the SDK-shaped wrappers to reach the injected real SDK. */
@@ -130,10 +163,9 @@ export function resetConfig(): void {
} }
/** /**
* Wire the storeRegistry's consumer-injected dependencies (session + identity-id * Wire the storeRegistry's dependencies. INTERNAL since 2026-08-07: an application
* normalization). Must be called before any storeRegistry.* use. Separate from * passes these to {@link configure}, which calls this. Still exported for the library's
* {@link configure} because it's storeRegistry-specific and, like the shim, * own suites, which wire the registry alone.
* disappears at migration.
*/ */
export function configureStoreRegistry(deps: StoreRegistryDeps): void { export function configureStoreRegistry(deps: StoreRegistryDeps): void {
// Fire the outbox inspection (Volet 3 of the low-level data-path trace) once, // Fire the outbox inspection (Volet 3 of the low-level data-path trace) once,
+2 -1
View File
@@ -7,7 +7,8 @@
*/ */
import { getCurrentUser } from "../src/shared-wallet/bootstrap"; import { getCurrentUser } from "../src/shared-wallet/bootstrap";
import { test, expect, afterEach } from "bun:test"; import { test, expect, afterEach } from "bun:test";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { ensureIdentity } from "../src/shared-wallet/access-gate"; import { ensureIdentity } from "../src/shared-wallet/access-gate";
+3 -1
View File
@@ -21,7 +21,9 @@
import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test"; import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test";
import { setAccessLog, enabled, shortNuri } from "../src/shared-wallet/access-log"; import { setAccessLog, enabled, shortNuri } from "../src/shared-wallet/access-log";
import { docCreate, sparqlUpdate, sparqlQuery } from "../src/surface/docs"; import { docCreate, sparqlUpdate, sparqlQuery } from "../src/surface/docs";
import { configure, configureStoreRegistry, connectedUser, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { connectedUser } from "../src/emulated-verifier/connect";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+2 -1
View File
@@ -26,7 +26,8 @@ import {
resetRegistryCache, resetRegistryCache,
} from "../src/shared-wallet/account-registry"; } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
import { configure, configureStoreRegistry } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetOpenedRepos } from "../src/emulated-verifier/open-repo"; import { resetOpenedRepos } from "../src/emulated-verifier/open-repo";
+2 -1
View File
@@ -23,7 +23,8 @@
import { describe, it, expect, mock, afterAll, beforeEach } from "bun:test"; import { describe, it, expect, mock, afterAll, beforeEach } from "bun:test";
import { ensureAccount, resolveWriteGraph, resetRegistryCache } from "../src/shared-wallet/account-registry"; import { ensureAccount, resolveWriteGraph, resetRegistryCache } from "../src/shared-wallet/account-registry";
import { resetOpenedRepos } from "../src/emulated-verifier/open-repo"; import { resetOpenedRepos } from "../src/emulated-verifier/open-repo";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetInfrastructure } from "../src/emulated-verifier/reach"; import { resetInfrastructure } from "../src/emulated-verifier/reach";
+3 -1
View File
@@ -26,7 +26,9 @@ import {
} from "../src/shared-wallet/account-registry"; } from "../src/shared-wallet/account-registry";
import { documentInboxAddress, openDocumentInbox } from "../src/emulated-verifier/branch-registers"; import { documentInboxAddress, openDocumentInbox } from "../src/emulated-verifier/branch-registers";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
import { configure, configureStoreRegistry, connectedUser, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { connectedUser } from "../src/emulated-verifier/connect";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { share } from "../src/surface/inbox"; import { share } from "../src/surface/inbox";
import { post, postToDocument, read as readInbox } from "../src/surface/inbox"; import { post, postToDocument, read as readInbox } from "../src/surface/inbox";
+2 -1
View File
@@ -25,7 +25,8 @@ test("throws a clear error when configure() was not called", async () => {
}); });
// From here on, a fake real `ng` is injected via configure(). // From here on, a fake real `ng` is injected via configure().
import { configure, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps } from "../src/shared-wallet/bootstrap"; import { resetCaps } from "../src/shared-wallet/bootstrap";
function fakeNg() { function fakeNg() {
+2 -1
View File
@@ -2,7 +2,8 @@ import { test, expect, mock, beforeEach, afterAll } from "bun:test";
import { post, read, materialize, watch } from "../src/surface/inbox"; import { post, read, materialize, watch } from "../src/surface/inbox";
import { userInbox, resetRegistryCache } from "../src/shared-wallet/account-registry"; import { userInbox, resetRegistryCache } from "../src/shared-wallet/account-registry";
import type { Deposit } from "../src/surface/inbox"; import type { Deposit } from "../src/surface/inbox";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
+2 -1
View File
@@ -21,7 +21,8 @@ import { test, expect, mock, afterAll } from "bun:test";
import { createEntityDoc, resetRegistryCache, userInbox, listMyEntityDocs } from "../src/shared-wallet/account-registry"; import { createEntityDoc, resetRegistryCache, userInbox, listMyEntityDocs } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
import type { Nuri, ReadCap } from "../src/model/types"; import type { Nuri, ReadCap } from "../src/model/types";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { share } from "../src/surface/inbox"; import { share } from "../src/surface/inbox";
import { read as readInbox } from "../src/surface/inbox"; import { read as readInbox } from "../src/surface/inbox";
+2 -1
View File
@@ -1,7 +1,8 @@
import { getCaps } from "../src/shared-wallet/bootstrap"; import { getCaps } from "../src/shared-wallet/bootstrap";
import { test, expect, mock, afterEach } from "bun:test"; import { test, expect, mock, afterEach } from "bun:test";
import { makeNg } from "../src/surface/ng-proxy"; import { makeNg } from "../src/surface/ng-proxy";
import { configure, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig } from "../src/shared-wallet/bootstrap";
// This suite injects a fake `ng` via configure() and declares WRITE caps — // This suite injects a fake `ng` via configure() and declares WRITE caps —
+2 -1
View File
@@ -20,7 +20,8 @@
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test"; import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
import { ensureRepoOpen, ensureReposOpen, resetOpenedRepos } from "../src/emulated-verifier/open-repo"; import { ensureRepoOpen, ensureReposOpen, resetOpenedRepos } from "../src/emulated-verifier/open-repo";
import { readUnion } from "../src/surface/read-model"; import { readUnion } from "../src/surface/read-model";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetInfrastructure } from "../src/emulated-verifier/reach"; import { resetInfrastructure } from "../src/emulated-verifier/reach";
import { resetRegistryCache } from "../src/shared-wallet/account-registry"; import { resetRegistryCache } from "../src/shared-wallet/account-registry";
+2 -1
View File
@@ -9,7 +9,8 @@ import { test, expect, mock, afterEach } from "bun:test";
import { exposeReadCap, fetchReadCap, resetPublicStoreFetches } from "../src/emulated-verifier/public-store"; import { exposeReadCap, fetchReadCap, resetPublicStoreFetches } from "../src/emulated-verifier/public-store";
import { mintCap } from "../src/emulated-verifier/caps"; import { mintCap } from "../src/emulated-verifier/caps";
import { getCaps } from "../src/shared-wallet/bootstrap"; import { getCaps } from "../src/shared-wallet/bootstrap";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { Nuri } from "../src/model/types"; import type { Nuri } from "../src/model/types";
+2 -1
View File
@@ -14,7 +14,8 @@ import { test, expect, mock, afterAll } from "bun:test";
import { sparqlQuery, sparqlUpdate, depositInto } from "../src/surface/docs"; import { sparqlQuery, sparqlUpdate, depositInto } from "../src/surface/docs";
import { createEntityDoc, resetRegistryCache, userInbox } from "../src/shared-wallet/account-registry"; import { createEntityDoc, resetRegistryCache, userInbox } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { mayReach, mustNotAttempt } from "../src/emulated-verifier/reach"; import { mayReach, mustNotAttempt } from "../src/emulated-verifier/reach";
import { hasReadCap } from "../src/model/nuri"; import { hasReadCap } from "../src/model/nuri";
+2 -1
View File
@@ -2,7 +2,8 @@ import { getCaps } from "../src/shared-wallet/bootstrap";
import { test, expect, mock, afterAll } from "bun:test"; import { test, expect, mock, afterAll } from "bun:test";
import { readUnion } from "../src/surface/read-model"; import { readUnion } from "../src/surface/read-model";
import type { Nuri } from "../src/model/types"; import type { Nuri } from "../src/model/types";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps } from "../src/shared-wallet/bootstrap"; import { resetCaps } from "../src/shared-wallet/bootstrap";
// The cap registry is process-wide, so each inject() starts from an empty one: // The cap registry is process-wide, so each inject() starts from an empty one:
+2 -1
View File
@@ -10,7 +10,8 @@ import {
resetRegistryCache, resetRegistryCache,
} from "../src/shared-wallet/account-registry"; } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
import { configure, configureStoreRegistry } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
// This suite injects a fake `ng` via configure(); bun runs test files in a // This suite injects a fake `ng` via configure(); bun runs test files in a
+2 -1
View File
@@ -1,6 +1,7 @@
import { test, expect, mock, afterAll } from "bun:test"; import { test, expect, mock, afterAll } from "bun:test";
import { subscribeDoc, subscribeDocs } from "../src/surface/subscribe"; import { subscribeDoc, subscribeDocs } from "../src/surface/subscribe";
import { configure, configureStoreRegistry } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { RegistrySession } from "../src/shared-wallet/account-registry"; import type { RegistrySession } from "../src/shared-wallet/account-registry";
+2 -1
View File
@@ -25,7 +25,8 @@
import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test"; import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test";
import { watchShape } from "../src/surface/watch-shape"; import { watchShape } from "../src/surface/watch-shape";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index"; import { configure } from "../src/index";
import { configureStoreRegistry, setCurrentUser } from "../src/shared-wallet/bootstrap";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap"; import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetRegistryCache, createEntityDoc } from "../src/shared-wallet/account-registry"; import { resetRegistryCache, createEntityDoc } from "../src/shared-wallet/account-registry";
import { resetOpenedRepos, setOpenTimeoutForTests, getSyncState } from "../src/emulated-verifier/open-repo"; import { resetOpenedRepos, setOpenTimeoutForTests, getSyncState } from "../src/emulated-verifier/open-repo";