refactor: renommer client → sdk, et fusionner les deux portes en une

Deux mouvements de surface, aucun changement de comportement.

**`packages/client` → `packages/sdk`, `@ng-eventually/client` → `@ng-eventually/sdk`.**
« client » ne disait rien : ce paquet EST le SDK que l'application appelle, et c'est
tout ce qu'elle appelle. L'ancien nom reste comme mot-clé de recherche dans
`docs/source-layout-by-fate.md` et le tableau des paquets du README.

**Une seule entrée.** L'entrée `./polyfill` disparaît ; ses symboles applicatifs —
`configure`, `configureStoreRegistry`, `setCurrentUser`, `connectedUser` et leurs types
— vivent dans un bloc `POLYFILL-ERA` de `src/index.ts`.

Ce que la seconde porte portait mérite d'être nommé avant d'être retiré : *ce qu'on
importe de ce chemin est exactement ce qu'on supprimera à la migration*. Une seule
porte perd ce signal — rien à la ligne d'import ne distingue `configure`, qui part, de
`docs`, que le vrai SDK remplace sur place. Trois choses le portent désormais : le bloc
lui-même, l'inventaire d'exports de `docs/api-contract.md` (épinglé par
`test/vocabulary.test.ts`, donc il ne peut pas rancir en silence), et le contrôle de
vocabulaire sur les noms publiés.

**Six symboles quittent la surface au passage**, et la fusion est ce qui a rendu le
choix visible plutôt qu'hérité :

- `getConfig` / `getStoreRegistryDeps` — câblage interne, atteint par
  `shared-wallet/bootstrap` ;
- `resetConfig` / `resetStoreRegistry` / `resetCaps` — remises à zéro de test, atteintes
  par leur chemin interne, ce qui est leur raison d'être ;
- le `share` direct — `inbox.share` a toujours été la même fonction, et la publier deux
  fois brouillait la frontière qu'elle servait à marquer.

Corrections d'affirmations fausses trouvées en chemin : le contrat annonçait `isNuri` /
`hasReadCap` sur la porte SDK alors qu'ils ne sont plus exportés depuis le passage au
permissif en entrée (`NuriLike` validé à la porte) ; le README du paquet documentait
`capFor`, `shareCap`, `getCaps` et `publishRepoLink`, dont aucun n'existe ; et le README
de l'app d'exemple affirmait que la suite e2e la pilote, ce qui reste à faire.

179 tests unitaires, typecheck bibliothèque / exemple / harnais, e2e 42/42 contre le
broker en ligne — mesuré une fois après le renommage, une fois après la fusion.
This commit is contained in:
Sylvain Duchesne
2026-08-07 11:05:19 +02:00
parent 0832338201
commit 0eb25286c8
85 changed files with 423 additions and 413 deletions
-114
View File
@@ -1,114 +0,0 @@
# @ng-eventually/client
Two entry points — the data-plane is SDK-identical, the polyfill bootstrap is
separate:
| Import | Surface |
|---|---|
| `@ng-eventually/client` | The same signature as the SDK — `ng`, `useShape`, `inbox` (+ types). 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. |
| `@ng-eventually/client/polyfill` | The only non-SDK surface — `configure`, `setCurrentUser`, and the capability surface (`capFor`, `shareCap`, `getCaps`). It falls away as NextGraph matures. |
> **Reading is key possession, and the isolation here is still fake.** The cap
> surface has the shape of the real model — you hold a document's `ReadCap` or you
> do not read it, and there is no authorization list anywhere — but nothing is
> encrypted yet and several read paths bypass the guard entirely. Nothing this
> library does may be described as "anonymous" or "private" until per-document
> encryption lands (P1b).
```ts
// bootstrap (the only non-SDK call) — inject the real SDK
import { configure } from "@ng-eventually/client/polyfill";
configure({ ng: realNg, useShape: realUseShape, sharedWallet, currentUser });
// from here on, a pure SDK surface:
import { ng, useShape, inbox } from "@ng-eventually/client";
await ng.doc_create(/* … */);
const set = useShape(MyShape, scope); // filtered to what the identity may read
await inbox.post(targetInbox, ref); // deposit (anticipated SDK API)
```
## Principle — the polyfill compensates, it never extends
**The polyfill's ONLY reason to exist is to bridge a NextGraph implementation gap
or a bug.** Every non-SDK surface must map to a capability NextGraph will provide
natively, and must fall away at that point. The polyfill MUST NOT add functionality
of its own — no bespoke features, no observability/tooling, no convenience API that
isn't strictly "NextGraph will do this natively later." The test for any proposed
addition: *does it compensate a real, exhibited NextGraph gap or bug?* If not, it
does not belong here — build it in the consumer application, not in the polyfill.
Corollary: a compensation whose gap is not actually exhibited on the target broker
is dead weight, not defensive code — it should be removed, not kept "just in case."
What the polyfill adds on top of the real SDK (each emulated for now, native as
NextGraph matures):
- Shared-wallet identity (one wallet for everyone; the current identity id is
relayed to the SDK).
- Capability emulation — per-identity **cap possession** (`capFor`) and a read filter
over it: you read the documents whose cap you hold. Creating a document files its
cap; receiving one is an inbox deposit. There is no authorization list.
- Anticipated methods (inbox `post`, `shareCap`) with their future-SDK shapes,
emulated for now.
Generic: no application domain. The consumer application injects its shapes and
performs the acts of sharing. The relationship concept ("who is connected to whom")
is the consumer application's own — the client exposes only "share this one
document's cap to that inbox".
### The cap surface in three calls
```ts
import { capFor, shareCap, getCaps } from "@ng-eventually/client/polyfill";
import { storeRegistry } from "@ng-eventually/client";
// Creating a document records its cap and you hold it — nothing to declare.
const doc = await storeRegistry.createEntityDoc(myId, "protected");
capFor(doc); // → `${doc}:r:…` — you hold it
// Share it with one recipient, addressed by their inbox. They need no "receive"
// operation: their existing inbox.watch absorbs it.
await shareCap(capFor(doc)!, theirInbox);
// Publishing is TWO acts: place the data in your public store, and circulate its
// LINK. There is no discovery — you cannot be found, you can only be reached — so
// the link has to travel: into an inbox, or into a document the reader already
// holds. The bare NURI would name the document without opening it.
const link = getCaps().publishRepoLink(publicDoc);
await shareCap(link, theirInbox);
```
The one invariant to keep in mind: **you never derive a cap from a bare reference.**
You look it up in what you hold, or you were given it. A `did:ng:o:…` without `:r:`
names a document and grants nothing.
### The types carry that invariant
`Nuri` and `ReadCap` are **template literal types**, not `string` aliases:
```ts
type Nuri = `did:ng:${string}`
type ReadCap = `did:ng:${string}:r:${string}`
```
They are still strings — assignable to `string`, JSON-serializable, no wrapper — but
the distinction is checked. A `ReadCap` goes wherever a `Nuri` is expected (a cap
*is* a NURI with the key inside); the reverse does not compile:
```ts
await shareCap(doc, theirInbox); // ✗ Argument of type '`did:ng:${string}`' is not
// assignable to '`did:ng:${string}:r:${string}`'
```
A string that comes from outside your code — storage, a URL, JSON, a form — is a
plain `string`. **Narrow it, do not cast it**: a cast re-opens exactly the confusion
the types close.
```ts
import { isNuri, hasReadCap } from "@ng-eventually/client";
const saved = localStorage.getItem("cap");
if (saved && hasReadCap(saved)) await shareCap(saved, theirInbox); // ✓ narrowed
```
The runtime guards remain regardless — a JavaScript caller never meets the compiler,
and a cast bypasses it — so passing a bare reference where a cap belongs throws with
a message that says so.
-58
View File
@@ -1,58 +0,0 @@
/**
* @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";
// 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();
-70
View File
@@ -1,70 +0,0 @@
/**
* `@ng-eventually/client/polyfill` — the polyfill-era door.
*
* Everything importable here disappears at migration, and that is the point of the
* separate entry: what an application imports from this path is exactly what it will
* have to delete. The SDK-identical entry (`@ng-eventually/client`) carries the other
* promise — a target counterpart for every symbol.
*
* This file only RE-EXPORTS. The implementation lives with the fate it belongs to:
* the injection store and the current-user relay in `shared-wallet/bootstrap.ts`
* (no counterpart, evaporates), the cap registry in `emulated-verifier/caps.ts`
* (stands in for the verifier's own bookkeeping). Keeping the store here made the
* published entry a dependency of the code it publishes.
*/
export type { StoreRegistryDeps, EventuallyConfig } from "./shared-wallet/bootstrap";
export {
configure,
getConfig,
resetConfig,
configureStoreRegistry,
getStoreRegistryDeps,
resetStoreRegistry,
setCurrentUser,
resetCaps,
} from "./shared-wallet/bootstrap";
// Cap surface — polyfill-era (caps are emulated now; native at migration).
// Re-exported here so the whole polyfill API lives under /polyfill. `share`
// lives in `inbox.ts` because sharing IS an inbox deposit (upstream: a sealed
// message carrying the cap), but it is surfaced here so the cap vocabulary stays
// on the polyfill side of the boundary rather than in the SDK-identical entry.
export { share } from "./surface/inbox";
export { connectedUser } from "./emulated-verifier/connect";
// --- what is deliberately NOT published --------------------------------------
//
// Removed 2026-08-05, each because an application coding against it learns something it
// must unlearn — the one failure this library exists to prevent:
//
// - `getCaps` / `CapRegistry` — the emulation's engine room. The consumer question is
// `capFor(doc)`: do I hold this? The registry object has neither a successor nor an
// inert form, so anything built on it must be rewritten rather than left alone.
// - `getCurrentUser` — an application knows who it signed in; asking the library back
// is a convenience of the shared wallet, not a brick of the model.
// - `virtualUsers` / `IdentityStore` — remembering an identity between sessions is the
// application's job upstream too. The gate persists what IT needs
// (`shared-wallet/access-gate.ts`); nothing else has to be exposed.
//
// And one more, removed 2026-08-06 with the public-store emulation:
//
// - `hasCap(doc)` — "do I hold this document's cap?". It read like "may I read this?",
// and once a public store serves its caps to whoever asks
// (`emulated-verifier/public-store.ts`) the two answers part company: a readable
// document answers `false` right up until something asks for it. Nor is the question
// one the target answers — upstream you open a document and find out. It had no
// caller outside the tests, which now use the internal registry directly.
//
// What remains here is the whole polyfill-era surface: `configure`, `setCurrentUser`,
// `share`, `connectedUser` and the test resets. Two of them are what an application calls.
// --- identity persistence (polyfill-era, no SDK counterpart) ----------------
//
// Moved here from the SDK-identical entry on 2026-08-03. `accounts` persists WHICH
// virtual user is connected, in browser storage — a notion that exists only because
// one shared wallet hosts several identities. The real SDK has no counterpart: there
// each user opens their own wallet, and "who am I" is the session. Shipping it from
// the SDK entry advertised as durable something that disappears at migration.
// Config-shaped types the bootstrap needs; both describe the shim, not the SDK.
export type { VirtualUserRecord, RegistrySession } from "./shared-wallet/account-registry";
+123
View File
@@ -0,0 +1,123 @@
# @ng-eventually/sdk
One entry point. Most of what it publishes has the same signature as the future SDK —
`ng`, `useShape`, `watchShape`, `docs`, `inbox`, `storeRegistry`, `readUnion` (+ types) —
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.
**Four calls do not, and they are the whole of what you will delete:** `configure`,
`configureStoreRegistry`, `setCurrentUser`, `connectedUser`. They exist because one
shared wallet hosts every user; upstream, an application imports the SDK and each user
opens their own wallet. `src/index.ts` groups them under a heading that says so.
*(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
hence `docs/api-contract.md`, whose export inventory a test keeps honest.)*
Per-symbol, with the target signature and an epistemic label on every claim:
[`docs/api-contract.md`](../../docs/api-contract.md).
> **Reading is key possession, and the isolation here is still fake.** The cap surface
> has the shape of the real model — you hold a document's `ReadCap` or you do not read
> it, and there is no authorization list anywhere — but nothing is encrypted yet and
> the stand-in key is a constant. Nothing this library does may be described as
> "anonymous" or "private" until per-document encryption lands (P1b).
```ts
import {
// SDK-shaped — the real SDK replaces these in place.
ensureIdentity, storeRegistry, inbox, readUnion, docs,
// Polyfill-era — these go away, and they are the whole of what goes away.
configure, configureStoreRegistry, setCurrentUser,
} from "@ng-eventually/sdk";
configure({ ng: realNg, useShape: realUseShape, sharedWallet });
configureStoreRegistry({ getSession });
await ensureIdentity(); // who am I (shared wallet)
const doc = await storeRegistry.createEntityDoc(me, "protected");
await docs.sparqlUpdate(sid, `INSERT DATA { … }`, doc);
const subjects = await readUnion(await storeRegistry.listMyEntityDocs(me, "protected"));
```
## Principle — the polyfill compensates, it never extends
**Its only reason to exist is to bridge a NextGraph implementation gap.** Every
non-SDK surface must map to something NextGraph will provide natively, and must fall
away at that point — no bespoke features, no observability, no convenience API that
isn't strictly *"NextGraph will do this later"*. The test for any proposed addition:
*does it compensate a real, exhibited gap?* If not, it belongs in the consumer
application. And a compensation whose gap is not actually exhibited on the target
broker is dead weight, not defensive code.
Both halves are binding — **the surface AND the implementation** stay as close as
possible to what NextGraph plans. The question to ask at every choice: *would this
make a caller learn something it has to UNLEARN at migration?* If yes, it is a
deviation, whatever it buys.
What the polyfill adds, each emulated now and native later:
- **Shared-wallet identity** — one wallet hosts every user, so the library fabricates
*virtual users* and confines every access to the connected one
(`emulated-verifier/reach.ts`). Upstream, each user opens their own wallet.
- **Capability emulation** — per-identity cap possession plus a read filter over it:
you read the documents whose cap you hold. There is no authorization list, because
the real model has none.
- **Inbox** — `post`, `postToDocument`, `share`, and the recipient's processing.
The model is verified (an inbox is a keypair on one repo); no JS surface exists yet.
Generic by construction: no application domain here. See
[`examples/notebook`](../../examples/notebook) for an application written against it,
which the e2e suite drives.
## How a document is reached — the three acts, and no others
```ts
import { storeRegistry, inbox, readUnion } from "@ng-eventually/sdk";
// 1. CREATE — you hold its cap, with nothing to declare.
const doc = await storeRegistry.createEntityDoc(me, "protected");
// 2. GIVE TO READ — name the document and the person. The key is looked up and
// sealed into a deposit; the recipient applies it by connecting, with nothing
// to call. Irreversible: there is no revoking a key already handed out.
await inbox.share(doc, "bob");
// 3. CIRCULATE THE REFERENCE — no call at all. Every reference this surface returns
// is BARE: it names the document and grants nothing. If the document sits in a
// PUBLIC store, the store serves its read cap to whoever asks, so the bare
// reference is enough to read it — and if it does not, the reference still names
// it and opens nothing.
const publicDoc = await storeRegistry.createEntityDoc(me, "public");
// …put `publicDoc` in a QR code, a message, another document. Nothing else to do.
await readUnion([publicDoc]); // a stranger holding only this reads it
```
**The invariant behind all three: you never derive a cap from a bare reference.** You
look it up in what you hold, you were given it, or a public store served it. A
`did:ng:o:…` without `:r:` names a document and opens nothing — which is what makes
confidentiality composable: a widely circulated document may point at a restricted
one, and following the reference gets you a name, not a key. See
[`docs/readcap-and-nuri-model.md`](../../docs/readcap-and-nuri-model.md) § 0.
## The types carry that invariant
`Nuri` and `ReadCap` are **template literal types**, not `string` aliases:
```ts
type Nuri = `did:ng:${string}`
type ReadCap = `did:ng:${string}:r:${string}`
```
They are still strings — assignable to `string`, JSON-serializable, no wrapper — but
the distinction is checked. A `ReadCap` goes wherever a `Nuri` is expected (a cap *is*
a NURI with the key inside); the reverse does not compile.
**Permissive in, precise out.** Public entries take `NuriLike` (`Nuri | string`) and
validate at the door, so a value coming from storage, a URL or a form needs no
narrowing and no cast on your side; what they *return* is a precise `Nuri`. The
runtime checks stay regardless — a JavaScript caller never meets the compiler.
```ts
const saved = localStorage.getItem("doc"); // string | null
if (saved) await readUnion([saved]); // ✓ validated at the door
```
@@ -1,10 +1,10 @@
# SDK reference — reading data with `@ng-eventually/client`
# SDK reference — reading data with `@ng-eventually/sdk`
**Audience:** anyone using `@ng-eventually/client` (the app that consumes it, and
**Audience:** anyone using `@ng-eventually/sdk` (the app that consumes it, and
the lib itself when honoring the contract). This is the reference on the SDK's
**read/reactivity surface** — how you read data and how a read stays live.
`@ng-eventually/client` is written and consumed as if NextGraph were a **finished,
`@ng-eventually/sdk` is written and consumed as if NextGraph were a **finished,
mature SDK**: documents per entity placed by scope, capabilities, inboxes, and a
**reactive ORM**. This file documents that finished-SDK contract. Where today's
emulation does not yet deliver it, that is called out in one clearly-separated
@@ -27,7 +27,7 @@ cited by `file:symbol` throughout so a future agent can re-verify cheaply.
> reads are the exception, not the rule.**
```ts
import { useShape } from "@ng-eventually/client";
import { useShape } from "@ng-eventually/sdk";
import { EventShapeType } from "…/shapes/orm/…";
function EventList() {
@@ -108,9 +108,9 @@ and every subsequent patch to a `DeepSignalSet`
`useDeepSignal` (`@ng-org/alien-deepsignals/react`). Vue and Svelte adapters exist
alongside the React one (`sdk/js/orm/src/frontendAdapters/{vue,svelte}/`).
`@ng-eventually/client` re-exports `useShape` from
`@ng-eventually/sdk` re-exports `useShape` from
[`../src/surface/use-shape.ts`](../src/surface/use-shape.ts); import it from the SDK
(`@ng-eventually/client`), never from `@ng-org/orm` directly.
(`@ng-eventually/sdk`), never from `@ng-org/orm` directly.
### What you get, in order
@@ -161,7 +161,7 @@ computes a result and returns once (`sparql_query`,
`sdk/js/lib-wasm/src/lib.rs:352`/`553`; no "subscribe to a query" exists —
`sparql_query` is not reactive).
In `@ng-eventually/client` the one-shot read is exposed as:
In `@ng-eventually/sdk` the one-shot read is exposed as:
- **`docs.sparqlQuery(sid, query, base?, anchor?)`** — a raw anchored SPARQL query
([`../src/surface/docs.ts`](../src/surface/docs.ts)). `anchor` = the document NURI to read; the
@@ -1,6 +1,6 @@
/**
* Real-broker plumbing for the SDK e2e harness a DEDICATED test wallet for
* `@ng-eventually/client`, fully separate from any consumer app's profile.
* `@ng-eventually/sdk`, fully separate from any consumer app's profile.
*
* Adapted from the Festipod app's `src/shared/support/hooks.ts` (the reference
* real-broker Playwright flow): headless wallet CREATION on nextgraph.eu, broker
@@ -21,7 +21,7 @@
*
* Standalone (NOT `bun test`). Run:
* bun run e2e/reactivity-doc-subscribe.ts
* (or `bun run test:e2e:reactivity` from packages/client)
* (or `bun run test:e2e:reactivity` from packages/sdk)
*
* It reuses the exact real-broker plumbing of run.ts / broker.ts: the dedicated lib
* wallet, the broker iframe, `window.__sdk`. The CROSS case opens a SECOND page on
@@ -1,9 +1,9 @@
/**
* Real-broker e2e runner for `@ng-eventually/client` the polyfill's OWN suite,
* Real-broker e2e runner for `@ng-eventually/sdk` the polyfill's OWN suite,
* in the SDK domain (no application concepts), with a DEDICATED wallet.
*
* Standalone (NOT `bun test`), so it never mixes into the fake-ng unit suite.
* Run: `bun run e2e/run.ts` (or `bun run test:e2e` from packages/client).
* Run: `bun run e2e/run.ts` (or `bun run test:e2e` from packages/sdk).
*
* It: builds the SDK page bundle, creates/reuses the dedicated lib wallet, opens
* the broker iframe on the real broker with that wallet, waits for `window.__sdk`
@@ -1,7 +1,7 @@
/**
* SDK e2e harness entry the MINIMAL page loaded inside the broker iframe.
*
* It imports the REAL `@ng-org/web` `ng` + this package (`@ng-eventually/client`),
* It imports the REAL `@ng-org/web` `ng` + this package (`@ng-eventually/sdk`),
* configures the polyfill session injection exactly the way a consumer does
* (`configure` + `configureStoreRegistry`), waits for the real broker to hand back
* a session, then exposes `window.__sdk`: a flat bag of async methods the
@@ -19,10 +19,7 @@ import {
configure,
configureStoreRegistry,
setCurrentUser,
resetCaps,
connectedUser,
} from "@ng-eventually/client/polyfill";
import {
docs,
subscribeDoc,
subscribeDocs,
@@ -31,19 +28,19 @@ import {
storeRegistry,
useShape as libUseShape,
watchShape,
} from "@ng-eventually/client";
} from "@ng-eventually/sdk";
// The harness tests the LIBRARY, so it legitimately reaches machinery a consumer
// application must not — but through the internal path, never the published entry.
// `storeRegistry` above is the app-facing slice; these are the shim internals.
import * as registryInternals from "../src/shared-wallet/account-registry";
import { getCaps, getCurrentUser } from "../src/shared-wallet/bootstrap";
import { getCaps, getCurrentUser, resetCaps } from "../src/shared-wallet/bootstrap";
import { documentInboxAddress } from "../src/emulated-verifier/branch-registers";
import * as virtualUsers from "../src/shared-wallet/virtual-users";
import { ensureIdentity } from "@ng-eventually/client";
import { ensureIdentity } from "@ng-eventually/sdk";
// The harness narrows for its OWN assertions; a consumer never has to (the entries take
// plain strings and validate inside). Internal path, like the rest of its machinery.
import { isNuri } from "../src/model/nuri";
import type { Nuri, ShapeObservable, ShapeQuery } from "@ng-eventually/client";
import type { Nuri, ShapeObservable, ShapeQuery } from "@ng-eventually/sdk";
const { IdentityStore } = virtualUsers;
@@ -1,13 +1,12 @@
{
"name": "@ng-eventually/client",
"name": "@ng-eventually/sdk",
"version": "0.0.0",
"type": "module",
"description": "SDK-identical client wrapper over @ng-org/web + @ng-org/orm with emulated capabilities and inbox. Drop-in; remove at migration.",
"description": "SDK-identical wrapper over @ng-org/web + @ng-org/orm with emulated capabilities and inbox. Drop-in; remove at migration.",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./polyfill": "./src/polyfill.ts"
".": "./src/index.ts"
},
"peerDependencies": {
"@ng-org/web": "*",
@@ -16,10 +15,18 @@
"@ng-org/alien-deepsignals": "*"
},
"peerDependenciesMeta": {
"@ng-org/web": { "optional": true },
"@ng-org/orm": { "optional": true },
"@ng-org/shex-orm": { "optional": true },
"@ng-org/alien-deepsignals": { "optional": true }
"@ng-org/web": {
"optional": true
},
"@ng-org/orm": {
"optional": true
},
"@ng-org/shex-orm": {
"optional": true
},
"@ng-org/alien-deepsignals": {
"optional": true
}
},
"devDependencies": {
"@ng-org/web": "0.1.2-alpha.13",
+102
View File
@@ -0,0 +1,102 @@
/**
* `@ng-eventually/sdk` — the one door. Everything an application imports, it imports
* from here.
*
* ── What the single entry costs, and how that cost is paid ────────────────
* There were two entries until 2026-08-07 (`.` and `./polyfill`), and the second one
* carried a signal worth naming before removing it: *what you import from that path is
* exactly what you will delete at migration*. One door loses that — nothing at an
* import line now distinguishes `configure`, which goes away, from `docs`, which the
* real SDK replaces in place. Three things carry it instead:
*
* 1. **The `POLYFILL-ERA` block below**, which is the deletion list. It is short by
* construction, and it is meant to keep shrinking.
* 2. **`docs/api-contract.md`**, which rules on every symbol with an epistemic label
* (PASSTHROUGH / LEVEL-1 SHAPE / ASSUMPTION / NO COUNTERPART) and whose export
* inventory is pinned by `test/vocabulary.test.ts` — so it cannot go stale
* quietly, which a hand-kept list would.
* 3. **The names themselves.** Every published name is built from the target's own
* vocabulary or carries a marker saying why it exists only here — pinned by the
* same test. A name that has to disappear says so.
*
* ── What is deliberately NOT published ────────────────────────────────────
* The entry publishes what an application CALLS, and nothing else. Not the machinery
* accessors (`getConfig`, `getStoreRegistryDeps` — internal wiring the surface reaches
* through `shared-wallet/bootstrap`), and not the test resets (`resetConfig`,
* `resetStoreRegistry`, `resetCaps` — the suite reaches them by their internal path,
* which is what they are for). Merging the entries made publishing those a visible
* choice rather than an inherited one; the choice is no.
*
* Earlier removals, each because an application coding against it learns something it
* must unlearn — the one failure this library exists to prevent:
*
* - `getCaps` / `CapRegistry` (2026-08-05) — the emulation's engine room. It has
* neither a successor nor an inert form, so anything built on it must be rewritten.
* - `getCurrentUser` (2026-08-05) — an application knows who it signed in; asking the
* library back is a convenience of the shared wallet, not a brick of the model.
* - `virtualUsers` / `IdentityStore` (2026-08-05) — remembering an identity between
* sessions is the application's job upstream too. The gate persists what IT needs.
* - `hasCap(doc)` (2026-08-06) — it read like "may I read this?", and once a public
* store serves its caps to whoever asks (`emulated-verifier/public-store.ts`) the
* two answers part company: a readable document answers `false` right up until
* something asks. Upstream you open a document and find out.
*/
// ── SDK-SHAPED — a target counterpart for every symbol ──────────────────────
// At migration the build alias is removed and these resolve to the real SDK. The
// per-symbol ruling, with its epistemic label, is in `docs/api-contract.md`.
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";
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";
// SDK type re-exports — so the app imports these from @ng-eventually/sdk 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";
// ── POLYFILL-ERA — THE DELETION LIST ────────────────────────────────────────
// Everything below exists because one shared wallet hosts every user, and nothing
// below has a target counterpart. At migration each call goes, and the imports with
// them. Keep this block short: an addition here is a promise to delete it later.
/**
* 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 the shape of that absence. `docs/api-contract.md` § 1.
*/
export { configure, configureStoreRegistry, setCurrentUser } from "./shared-wallet/bootstrap";
export type { EventuallyConfig, StoreRegistryDeps } from "./shared-wallet/bootstrap";
export type { RegistrySession } from "./shared-wallet/account-registry";
/**
* Await the connection work `setCurrentUser` fires: restore what was shared with this
* 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.
*/
export { connectedUser } from "./emulated-verifier/connect";
// ── 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
// 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";
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();
@@ -25,11 +25,11 @@ export type Nuri = `did:ng:${string}`;
* That confusion, left to runtime, silently turns "naming is not reading" into
* "naming is reading" the exact inversion this model exists to remove.
*
* It constrains the consumer's code the same way, which is the point: an app that
* reads a cap back from storage, a URL or JSON gets a `string` and must pass it
* through {@link isNuri} / {@link hasReadCap} (exported from the SDK entry) to use
* it a validation it should be doing anyway. The runtime guards stay regardless:
* a JavaScript consumer bypasses the compiler entirely.
* A consumer holding a plain `string` (from storage, a URL, JSON, a form) does NOT
* have to narrow it: every public entry takes {@link NuriLike} and validates at the
* door (`toNuri`), which is why no type guard is exported. Permissive in, precise
* out. The runtime checks stay regardless a JavaScript consumer never meets the
* compiler, and a cast bypasses it.
*/
export type ReadCap = `did:ng:${string}:r:${string}`;
@@ -920,7 +920,7 @@ export async function createEntityDoc(id: string, scope: Scope): Promise<Nuri> {
// `indexDoc` anchor scopes it) — the CANONICAL, always-safe shape the
// anchored default-graph read queries (readUserStore below, same as
// read-model.ts). Not a round-trip necessity on the current broker: the e2e
// harness (`packages/client/e2e/`) verified an anchored `GRAPH <plainNuri>`
// harness (`packages/sdk/e2e/`) verified an anchored `GRAPH <plainNuri>`
// write ALSO round-trips here (same repo graph, no phantom graph); no-GRAPH
// is kept as a simplicity/safety convention. entityNuri is a NURI stored as
// a literal → escapeLiteral.
@@ -31,7 +31,7 @@
* (BARE-encoded Rust structs, base64url'd); decoding them to report concrete
* write TARGETS (topics/docs) would mean duplicating the WASM verifier's wire
* format in this polyfill, which is explicitly out of scope (SDK internals live
* in the `@ng-eventually/client`-independent core repo, per this repo's
* in the `@ng-eventually/sdk`-independent core repo, per this repo's
* doctrine) so only the pending COUNT is reported, never fabricated targets.
* `sessionStorage` access itself can throw (sandboxed iframe, disabled storage
* see the exact error string handled in the core repo's `main.ts`
@@ -168,7 +168,7 @@ export async function post(targetInboxLike: NuriLike, opts: PostOptions): Promis
// default graph (same shape as read-model.ts readDoc/readUnion). This is the
// CANONICAL, always-safe shape and the one the anchored default-graph read
// queries. (Not a round-trip necessity on the current broker: the e2e harness
// `packages/client/e2e/` verified that an anchored `GRAPH <plainNuri>` write
// `packages/sdk/e2e/` verified that an anchored `GRAPH <plainNuri>` write
// ALSO round-trips here — it resolves to the same repo graph, no phantom graph.
// The no-GRAPH form is kept as a simplicity/safety convention; re-verify with
// that harness if the broker version changes.)
@@ -1,6 +1,6 @@
/**
* Lifecycle re-exports SDK-shaped forwarders so the app imports `init` /
* `initNg` from `@ng-eventually/client` rather than from `@ng-org/*`. They
* `initNg` from `@ng-eventually/sdk` rather than from `@ng-org/*`. They
* delegate to the REAL functions injected at `configure()`. Passthrough today;
* a hook point later (e.g. opening the shared wallet on `init`).
*/
@@ -7,7 +7,8 @@
*/
import { getCurrentUser } from "../src/shared-wallet/bootstrap";
import { test, expect, afterEach } from "bun:test";
import {configure,configureStoreRegistry,resetConfig,resetStoreRegistry,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { ensureIdentity } from "../src/shared-wallet/access-gate";
const KEY = "ng-eventually:identity";
@@ -21,7 +21,8 @@
import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test";
import { setAccessLog, enabled, shortNuri } from "../src/shared-wallet/access-log";
import { docCreate, sparqlUpdate, sparqlQuery } from "../src/surface/docs";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,setCurrentUser,resetCaps,connectedUser} from "../src/polyfill";
import { configure, configureStoreRegistry, connectedUser, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
// ---------------------------------------------------------------------------
// Helpers
@@ -26,7 +26,8 @@ import {
resetRegistryCache,
} from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig} from "../src/polyfill";
import { configure, configureStoreRegistry } from "../src/index";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetOpenedRepos } from "../src/emulated-verifier/open-repo";
afterAll(() => {
@@ -23,7 +23,8 @@
import { describe, it, expect, mock, afterAll, beforeEach } from "bun:test";
import { ensureAccount, resolveWriteGraph, resetRegistryCache } from "../src/shared-wallet/account-registry";
import { resetOpenedRepos } from "../src/emulated-verifier/open-repo";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetInfrastructure } from "../src/emulated-verifier/reach";
const SESSION = { sessionId: "sid-cold", privateStoreId: "PRIV-COLD" };
@@ -26,7 +26,9 @@ import {
} from "../src/shared-wallet/account-registry";
import { documentInboxAddress, openDocumentInbox } from "../src/emulated-verifier/branch-registers";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser,share,connectedUser} from "../src/polyfill";
import { configure, configureStoreRegistry, connectedUser, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { share } from "../src/surface/inbox";
import { post, postToDocument, read as readInbox } from "../src/surface/inbox";
import { readUnion } from "../src/surface/read-model";
import { sparqlUpdate } from "../src/surface/docs";
@@ -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().
import {configure,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, setCurrentUser } from "../src/index";
import { resetCaps } from "../src/shared-wallet/bootstrap";
function fakeNg() {
return {
@@ -2,7 +2,8 @@ import { test, expect, mock, beforeEach, afterAll } from "bun:test";
import { post, read, materialize, watch } from "../src/surface/inbox";
import { userInbox, resetRegistryCache } from "../src/shared-wallet/account-registry";
import type { Deposit } from "../src/surface/inbox";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
// This suite injects a fake `ng` via configure() and reuses the storeRegistry's
@@ -21,7 +21,9 @@ import { test, expect, mock, afterAll } from "bun:test";
import { createEntityDoc, resetRegistryCache, userInbox, listMyEntityDocs } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
import type { Nuri, ReadCap } from "../src/model/types";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser,share} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { share } from "../src/surface/inbox";
import { read as readInbox } from "../src/surface/inbox";
import { filterReadable } from "../src/emulated-verifier/read-filter";
@@ -1,7 +1,8 @@
import { getCaps } from "../src/shared-wallet/bootstrap";
import { test, expect, mock, afterEach } from "bun:test";
import { makeNg } from "../src/surface/ng-proxy";
import {configure,resetConfig,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig } from "../src/shared-wallet/bootstrap";
// This suite injects a fake `ng` via configure() and declares WRITE caps —
// which stay an authorization list on purpose: only READING is key possession
@@ -20,7 +20,8 @@
import { describe, it, expect, mock, beforeEach, afterAll } from "bun:test";
import { ensureRepoOpen, ensureReposOpen, resetOpenedRepos } from "../src/emulated-verifier/open-repo";
import { readUnion } from "../src/surface/read-model";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetInfrastructure } from "../src/emulated-verifier/reach";
import { resetRegistryCache } from "../src/shared-wallet/account-registry";
@@ -9,14 +9,8 @@ import { test, expect, mock, afterEach } from "bun:test";
import { exposeReadCap, fetchReadCap, resetPublicStoreFetches } from "../src/emulated-verifier/public-store";
import { mintCap } from "../src/emulated-verifier/caps";
import { getCaps } from "../src/shared-wallet/bootstrap";
import {
configure,
configureStoreRegistry,
resetConfig,
resetStoreRegistry,
resetCaps,
setCurrentUser,
} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { Nuri } from "../src/model/types";
const SHIM = "urn:ng-eventually:shim";
@@ -14,7 +14,8 @@ import { test, expect, mock, afterAll } from "bun:test";
import { sparqlQuery, sparqlUpdate, depositInto } from "../src/surface/docs";
import { createEntityDoc, resetRegistryCache, userInbox } from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { mayReach, mustNotAttempt } from "../src/emulated-verifier/reach";
import { hasReadCap } from "../src/model/nuri";
@@ -192,17 +193,18 @@ test("a BARE reference is reachable when the cap is possessed elsewhere", async
// machinery reaches every virtual user's documents.
test("the machinery is NOT part of the package's public surface", async () => {
const entry: Record<string, unknown> = await import("../src/index");
const polyfill: Record<string, unknown> = await import("../src/polyfill");
for (const surface of [entry, polyfill]) {
for (const name of Object.keys(surface)) {
expect(name).not.toMatch(/^physical/);
}
for (const name of Object.keys(entry)) {
expect(name).not.toMatch(/^physical/);
}
// Named explicitly, so adding one and forgetting the rule fails here.
for (const forbidden of ["physicalQuery", "physicalUpdate", "physicalCreate", "subscribePhysicalDoc"]) {
expect(entry[forbidden]).toBeUndefined();
expect(polyfill[forbidden]).toBeUndefined();
}
// …and the machinery accessors the merged entry deliberately stopped publishing
// (2026-08-07): internal wiring and test resets are reached by their internal path.
for (const unpublished of ["getConfig", "getStoreRegistryDeps", "resetConfig", "resetStoreRegistry", "resetCaps", "getCaps", "getCurrentUser"]) {
expect(entry[unpublished]).toBeUndefined();
}
// The cross-account fan-out is gone from the registry entirely.
const registry = entry.storeRegistry as Record<string, unknown>;
@@ -2,7 +2,8 @@ import { getCaps } from "../src/shared-wallet/bootstrap";
import { test, expect, mock, afterAll } from "bun:test";
import { readUnion } from "../src/surface/read-model";
import type { Nuri } from "../src/model/types";
import {configure,configureStoreRegistry,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps } from "../src/shared-wallet/bootstrap";
// The cap registry is process-wide, so each inject() starts from an empty one:
// once ANY cap exists the possession gate is in force for every reader, and a
@@ -10,7 +10,8 @@ import {
resetRegistryCache,
} from "../src/shared-wallet/account-registry";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig} from "../src/polyfill";
import { configure, configureStoreRegistry } from "../src/index";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
// This suite injects a fake `ng` via configure(); bun runs test files in a
// shared process with a single module singleton, and may run this file BEFORE
@@ -1,6 +1,7 @@
import { test, expect, mock, afterAll } from "bun:test";
import { subscribeDoc, subscribeDocs } from "../src/surface/subscribe";
import {configure,configureStoreRegistry,resetConfig,resetStoreRegistry} from "../src/polyfill";
import { configure, configureStoreRegistry } from "../src/index";
import { resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import type { RegistrySession } from "../src/shared-wallet/account-registry";
// subscribeDoc/subscribeDocs wrap the REAL injected `ng.doc_subscribe`. This
@@ -91,10 +91,10 @@ function words(name: string): string[] {
const SRC = path.join(import.meta.dir, "..", "src");
/** Every identifier the two entry points publish, read from the `export` statements. */
/** Every identifier the entry point publishes, read from its `export` statements. */
function publishedNames(): string[] {
const out = new Set<string>();
for (const entry of ["index.ts", "polyfill.ts"]) {
for (const entry of ["index.ts"]) {
const text = fs.readFileSync(path.join(SRC, entry), "utf8");
// `export * as ns from "…"`
for (const m of text.matchAll(/export \* as (\w+) from/g)) out.add(m[1]!);
@@ -156,7 +156,7 @@ test("a reserved-namespace key cannot be produced by a consumer's normalizeId",
// injected by the consumer — so a careless one must be refused, not trusted. A
// collision would key a user onto an infrastructure account: reads and writes on
// documents that are not theirs.
const { configureStoreRegistry, resetStoreRegistry } = await import("../src/polyfill");
const { configureStoreRegistry, resetStoreRegistry } = await import("../src/shared-wallet/bootstrap");
const { ensureAccount, resetRegistryCache } = await import(
"../src/shared-wallet/account-registry"
);
@@ -195,7 +195,7 @@ function contractInventory(): Record<string, string[]> {
return out;
}
test("the api-contract appendix lists exactly what the entries export", () => {
test("the api-contract appendix lists exactly what the entry exports", () => {
// The appendix is the instrument a reader diffs against when the surface moves. It
// went stale once — still naming `storeRegistry`'s shim internals after the entry had
// been narrowed to seven functions — and a stale inventory is worse than none: it
@@ -217,7 +217,7 @@ test("the api-contract appendix lists exactly what the entries export", () => {
/** Names that live inside a re-exported namespace rather than on the entry itself. */
function isNamespaceMember(name: string): boolean {
const src = path.join(import.meta.dir, "..", "src");
for (const entry of ["index.ts", "polyfill.ts"]) {
for (const entry of ["index.ts"]) {
const text = fs.readFileSync(path.join(src, entry), "utf8");
for (const m of text.matchAll(/export \* as \w+ from "\.\/([^"]+)"/g)) {
const file = path.join(src, m[1]! + ".ts");
@@ -25,7 +25,8 @@
import { describe, it, expect, mock, beforeEach, afterEach, afterAll } from "bun:test";
import { watchShape } from "../src/surface/watch-shape";
import {configure,configureStoreRegistry,resetStoreRegistry,resetConfig,resetCaps,setCurrentUser} from "../src/polyfill";
import { configure, configureStoreRegistry, setCurrentUser } from "../src/index";
import { resetCaps, resetConfig, resetStoreRegistry } from "../src/shared-wallet/bootstrap";
import { resetRegistryCache, createEntityDoc } from "../src/shared-wallet/account-registry";
import { resetOpenedRepos, setOpenTimeoutForTests, getSyncState } from "../src/emulated-verifier/open-repo";