refactor: le paquet s'appelle polyfill, « SDK » désigne celui de NextGraph

Le nom @ng-eventually/sdk entrait en collision avec le SDK de NextGraph, dont
ce paquet est justement un polyfill. Impossible d'écrire « le SDK » sans lever
l'ambiguïté à chaque phrase — et le contrat publié, lu par une application,
était le pire endroit pour laisser traîner ça.

packages/sdk → packages/polyfill, @ng-eventually/sdk → @ng-eventually/polyfill,
contract_sdk-surface → contract_polyfill-surface, e2e/sdk-entry.ts →
e2e/polyfill-entry.ts, docs/sdk-reference.md → docs/polyfill-reference.md.

Les occurrences de « SDK » qui désignent celui de NextGraph restent intactes,
y compris les chemins dans nextgraph-rs (sdk/js/orm, sdk/js/web). Le tri s'est
fait occurrence par occurrence, pas par substitution.

Le contrat énonce désormais son identité en une phrase : « This package is a
polyfill of NextGraph's SDK. »
This commit is contained in:
Sylvain Duchesne
2026-08-10 17:14:25 +02:00
parent 49b046268e
commit 737729c9ce
88 changed files with 122 additions and 106 deletions
@@ -1,4 +1,4 @@
# @ng-eventually/sdk
# @ng-eventually/polyfill
One entry point. Most of what it publishes has the same signature as the future SDK —
`ng`, `useShape`, `watchShape`, `docs`, `inbox`, `storeRegistry`, `readUnion` (+ types) —
@@ -30,7 +30,7 @@ import {
ensureIdentity, storeRegistry, inbox, readUnion, docs,
// Polyfill-era — one call, and it is the whole of what goes away.
configure,
} from "@ng-eventually/sdk";
} from "@ng-eventually/polyfill";
configure({ ng: realNg, useShape: realUseShape, getSession, sharedWallet });
await ensureIdentity(); // who I am (returned), connection work awaited
@@ -72,7 +72,7 @@ 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";
import { storeRegistry, inbox, readUnion } from "@ng-eventually/polyfill";
// 1. CREATE — you hold its cap, with nothing to declare. No identity parameter: a
// session belongs to one user, exactly as the target's own `doc_create` assumes.
@@ -1,10 +1,10 @@
# SDK reference — reading data with `@ng-eventually/sdk`
# Polyfill reference — reading data with `@ng-eventually/polyfill`
**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
**Audience:** anyone using `@ng-eventually/polyfill` (the app that consumes it, and
the lib itself when honoring the contract). This is the reference on the polyfill's
**read/reactivity surface** — how you read data and how a read stays live.
`@ng-eventually/sdk` is written and consumed as if NextGraph were a **finished,
`@ng-eventually/polyfill` 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/sdk";
import { useShape } from "@ng-eventually/polyfill";
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/sdk` re-exports `useShape` from
`@ng-eventually/polyfill` re-exports `useShape` from
[`../src/surface/use-shape.ts`](../src/surface/use-shape.ts); import it from the SDK
(`@ng-eventually/sdk`), never from `@ng-org/orm` directly.
(`@ng-eventually/polyfill`), 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/sdk` the one-shot read is exposed as:
In `@ng-eventually/polyfill` 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,12 +1,12 @@
/**
* Real-broker plumbing for the SDK e2e harness a DEDICATED test wallet for
* `@ng-eventually/sdk`, fully separate from any consumer app's profile.
* `@ng-eventually/polyfill`, 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
* redirect via nextgraph.net, iframe handling. Here it authenticates a wallet
* created FOR THIS LIB (distinct name + distinct profile dir), and loads the
* minimal SDK page (sdk-entry.ts) inside the broker iframe.
* minimal polyfill page (polyfill-entry.ts) inside the broker iframe.
*/
import { chromium, type BrowserContext, type Page, type Frame } from "playwright";
@@ -25,8 +25,8 @@ const WALLET_READY_MARKER = path.join(PROFILE_DIR, ".wallet-ready");
export const WALLET_NAME = "ng-eventually-e2e";
export const WALLET_PASSWORD = "ng-eventually-e2e";
const ENTRY = path.resolve(__dirname, "sdk-entry.ts");
const BUNDLE_OUT = path.resolve(__dirname, ".dist", "sdk-entry.js");
const ENTRY = path.resolve(__dirname, "polyfill-entry.ts");
const BUNDLE_OUT = path.resolve(__dirname, ".dist", "polyfill-entry.js");
const LAUNCH_ARGS = [
"--disable-features=PrivateNetworkAccessRespectPreflightResults,BlockInsecurePrivateNetworkRequests,PrivateNetworkAccessForWorkers,PrivateNetworkAccessForNavigations",
@@ -52,9 +52,9 @@ export function buildBundle(): void {
export function serveHarness(): Promise<{ url: string; close: () => void }> {
const bundle = fs.readFileSync(BUNDLE_OUT, "utf-8");
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>ng-eventually sdk e2e</title></head><body><div id="root"></div><script type="module" src="/sdk-entry.js"></script></body></html>`;
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>ng-eventually polyfill e2e</title></head><body><div id="root"></div><script type="module" src="/polyfill-entry.js"></script></body></html>`;
const server = http.createServer((req, res) => {
if (req.url === "/sdk-entry.js") {
if (req.url === "/polyfill-entry.js") {
res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" });
res.end(bundle);
} else {
@@ -1,7 +1,7 @@
/**
* SDK e2e harness entry the MINIMAL page loaded inside the broker iframe.
* Polyfill e2e harness entry the MINIMAL page loaded inside the broker iframe.
*
* It imports the REAL `@ng-org/web` `ng` + this package (`@ng-eventually/sdk`),
* It imports the REAL `@ng-org/web` `ng` + this package (`@ng-eventually/polyfill`),
* 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
@@ -25,7 +25,7 @@ import {
storeRegistry,
useShape as libUseShape,
watchShape,
} from "@ng-eventually/sdk";
} from "@ng-eventually/polyfill";
// 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.
@@ -42,11 +42,11 @@ import {
} from "../src/shared-wallet/bootstrap";
import { connectedUser } from "../src/emulated-verifier/connect";
import * as virtualUsers from "../src/shared-wallet/virtual-users";
import { ensureIdentity } from "@ng-eventually/sdk";
import { ensureIdentity } from "@ng-eventually/polyfill";
// 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/sdk";
import type { Nuri, ShapeObservable, ShapeQuery } from "@ng-eventually/polyfill";
const { IdentityStore } = virtualUsers;
@@ -21,7 +21,7 @@
*
* Standalone (NOT `bun test`). Run:
* bun run e2e/reactivity-doc-subscribe.ts
* (or `bun run test:e2e:reactivity` from packages/sdk)
* (or `bun run test:e2e:reactivity` from packages/polyfill)
*
* 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/sdk` the polyfill's OWN suite,
* Real-broker e2e runner for `@ng-eventually/polyfill` 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/sdk).
* Run: `bun run e2e/run.ts` (or `bun run test:e2e` from packages/polyfill).
*
* 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,8 +1,8 @@
{
"name": "@ng-eventually/sdk",
"name": "@ng-eventually/polyfill",
"version": "0.0.0",
"type": "module",
"description": "SDK-identical wrapper over @ng-org/web + @ng-org/orm with emulated capabilities and inbox. Drop-in; remove at migration.",
"description": "Polyfill of the NextGraph JS SDK 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": {
@@ -1,5 +1,5 @@
/**
* `@ng-eventually/sdk` the one door. Everything an application imports, it imports
* `@ng-eventually/polyfill` the one door. Everything an application imports, it imports
* from here.
*
* What the single entry costs, and how that cost is paid
@@ -78,7 +78,7 @@ 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
// SDK type re-exports — so the app imports these from @ng-eventually/polyfill 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";
@@ -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/sdk`-independent core repo, per this repo's
* in the `@ng-eventually/polyfill`-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`
@@ -169,7 +169,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/sdk/e2e/` verified that an anchored `GRAPH <plainNuri>` write
// `packages/polyfill/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/sdk` rather than from `@ng-org/*`. They
* `initNg` from `@ng-eventually/polyfill` 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`).
*/
@@ -52,7 +52,7 @@ export type DocChange = unknown;
* `AppResponse` payload (`{ V0: { State | Patch | TabInfo | … } }`). It is NOT a
* closed enum: the platform may push other variants, so this is a bare `string`
* (e.g. `"State"`, `"Patch"`, `"TabInfo"`), or `undefined` when the shape can't
* be read. Verified against the CONTRACT-3 e2e probe (`e2e/sdk-entry.ts`): the
* be read. Verified against the CONTRACT-3 e2e probe (`e2e/polyfill-entry.ts`): the
* variant is `Object.keys(resp.V0)[0]`. Exposed so a caller that needs the SYNC
* BARRIER (the first `State`, per CONTRACT 3) can distinguish it from the earlier
* `TabInfo`/`Patch` pushes see `open-repo.ts`. Most callers ignore it and use