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
+130
View File
@@ -0,0 +1,130 @@
/**
* `@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 ────────────────
* 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`.
// A type is published only when a PUBLISHED SIGNATURE uses it. `export *` published
// eight in one gesture (2026-08-10: it was a blanket re-export), of which two named
// nothing a consumer can reach — `ReadCap` (used only by two private helpers of
// `surface/inbox.ts`) and `InboxScope` (used only by the unpublished
// `account-registry.userInbox`). A published type with no published signature is a
// promise about the target that nothing here keeps: it invites a consumer to hold a
// value it has no call to obtain — and for `ReadCap`, the one value the model says a
// caller must never be handed on request. They stay DEFINED in `model/types.ts`, where
// the library uses them; they stop being surface. `docs/api-contract.md` § 10, § 14.
// Each one, and the signature that earns it its place:
// Nuri every reference the surface RETURNS
// NuriLike every reference the surface ACCEPTS
// Scope `storeRegistry.*`, `watchShape`
// PrincipalId `ensureIdentity`, `inbox.Deposit`/`PostOptions`, `EventuallyConfig`
// NgLike `EventuallyConfig.ng`
// UseShapeLike `EventuallyConfig.useShape`
export type { Nuri, NuriLike, Scope, PrincipalId, NgLike, UseShapeLike } 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/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";
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.
*
* **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 } from "./shared-wallet/bootstrap";
export type { EventuallyConfig } from "./shared-wallet/bootstrap";
export type { RegistrySession } from "./shared-wallet/account-registry";
// --- what this block deliberately does NOT contain --------------------------
//
// 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:
//
// - `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 ────────
// 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();