--- type: contract summary: The API @ng-eventually/polyfill exposes to an application — signatures, guaranteed behaviour, and what it does not offer --- # contract_polyfill-surface — `@ng-eventually/polyfill` ## Scope This package is a polyfill of NextGraph's SDK. This package covers placement (creating and listing an application's documents by scope), reading (a document's subjects, one-shot or reactive), sharing a document with a named user, and depositing into inboxes. It does not cover user management, display names, transport, or the operation of a deployment. ### Deployment requirements An application using this package must: - serve a wallet file (`.ngw`) from its own bundle, and pass its URL and password to `configure` as `sharedWallet: { fileUrl, password }`; - call `init(…)` — this package's, not the one it passed to `configure` — and then await `ensureIdentity()`, in a browser context, before rendering its interface. `ensureIdentity()` resolves once a session is open, so awaiting it before `init` has been called never resolves; - expect `ensureIdentity()` to mount a full-screen barrier on every top-level load, and the page to reload itself once when a person returns to it: hold no un-persisted state across that call. ## Surface Full typed shape: the package's `types` entry, `@ng-eventually/polyfill`. A type is published only when a published signature uses it. The load-bearing signatures: ```ts // ── bootstrap ──────────────────────────────────────────────────────────── export function configure(c: EventuallyConfig): void; export interface EventuallyConfig { ng: NgLike; // the `ng` object from @ng-org/web useShape: UseShapeLike; // `useShape` from @ng-org/orm sharedWallet?: SharedWalletConfig; // { fileUrl, password, importUrl? } debugAccessLog?: boolean; init?: (...args: any[]) => any; initNg?: (...args: any[]) => any; } // ── identity — one await before the application renders ────────────────── export async function ensureIdentity(): Promise; // returns who you are // ── addressing ─────────────────────────────────────────────────────────── export type Nuri = `did:ng:${string}`; export type NuriLike = Nuri | string; export type Scope = "public" | "protected" | "private"; // ── placement: where an application's documents live ───────────────────── export const storeRegistry: { // no identity parameter — a session is one user's createEntityDoc(scope: Scope): Promise; listMyEntityDocs(scope: Scope): Promise; resolveScopeGraph(scope: Scope): Promise; resolveWriteGraph(scope: Scope): Promise; openDocumentInbox(doc: NuriLike): Promise; }; // ── reading ────────────────────────────────────────────────────────────── export async function readUnion(docs: NuriLike[]): Promise; export interface UnionSubject { subject: string; graph: Nuri; props: Record } export function useShape(shapeType: unknown, scope: unknown): unknown; // read-filtered view export function watchShape(query: ShapeQuery): ShapeObservable; export function subscribeDoc(nuri: NuriLike, onChange: (r: DocChange, t: DocChangeType) => void): Unsubscribe; export function subscribeDocs(nuris: NuriLike[], onChange: (r: DocChange, t: DocChangeType) => void): Unsubscribe; // ── low-level document / SPARQL primitives ─────────────────────────────── export const docs: { docCreate(sessionId: string, crdt: string, cls: string, dest: string, store?: unknown): Promise; sparqlQuery(sessionId: string, query: string, base?: string, anchor?: NuriLike, label?: string): Promise; sparqlUpdate(sessionId: string, query: string, anchor?: NuriLike, label?: string): Promise; }; // ── inbox: giving to read, and depositing ──────────────────────────────── export const inbox: { share(doc: NuriLike, toUser: string): Promise; // give a reader the key post(targetInbox: NuriLike, opts: PostOptions): Promise; postToDocument(doc: NuriLike, opts: PostOptions): Promise; read(targetInbox: NuriLike): Promise; // only your own readForDocument(doc: NuriLike): Promise; readSynced(targetInbox: NuriLike): Promise; processInbox(targetInbox: NuriLike): Promise; watch(targetInbox: NuriLike, onDeposits: (d: Deposit[]) => void): () => void; materialize: typeof read; }; export interface Deposit { from: PrincipalId | null; payload: unknown; ts: number } // ── the wrapped SDK objects ────────────────────────────────────────────── export const ng: Record; // call this instead of the `ng` passed to `configure` export function init(...args: any[]): any; // likewise — not the `init` passed to `configure` export function initNg(...args: any[]): any; ``` ## Guarantees Every entry accepts `NuriLike` and validates at the door; what it returns is a precise `Nuri`. No type guard is published. A returned reference carries no key — not `createEntityDoc`, not `listMyEntityDocs`, not `UnionSubject.subject` / `.graph`. A reference found inside a document yields a name, not a key. You read a document whose key you hold: you created it, it was shared with you, or it sits in a public store, which serves its read key to whoever asks. No call answers "may I read this?". What was shared with you becomes readable after `ensureIdentity()`. `readUnion` returns one entry per distinct subject present in a document. `subject` is that subject's IRI exactly as written, and is a `string`, because a subject may be any IRI; `graph` is the document reference you passed in, and is the `Nuri` to hand back to this surface. Properties of different subjects are never merged, and the same subject IRI found in two documents stays two entries, told apart by `graph`. Several objects in one document are allowed. Recommended placement is one document per business entity: access is granted per document. `urn:ng-eventually:` is reserved. Triples whose **subject** falls under that prefix are dropped on read and never returned by `readUnion`; every other IRI is returned. Only a document's owner writes to it. Holding its read key never grants a write. `inbox.share(doc, toUser)` names the document and the person; the recipient calls nothing. It refuses a recipient nobody has signed in as, rather than creating them. `inbox.post` refuses a target that is not an inbox; to reach a document's owner, use `inbox.postToDocument(doc, …)`. Anyone may deposit into an inbox; only its owner reads it. `ensureIdentity()` settles the identity, completes the connection work it starts, and returns the identity. It takes no identifier, and no other call takes one. **The session is the package's, not yours.** You never build one, and no call takes one. Call this package's `init` (not the one you passed to `configure`): it captures the session the SDK delivers to `init`'s callback and keeps it, then calls your callback with that same event untouched — so an application that wants the `session_id` for the `docs` primitives reads it there, and one that does not may pass no callback at all. Identity normalisation is the package's too: `@Alice`, `alice ` and `ALICE` are one person. `createEntityDoc` throws if the document cannot be recorded in its store. ## Non-guarantees **No display name.** `ensureIdentity()` returns an opaque identifier: do not parse it, split it, or render it as a readable name. **No revocation.** `inbox.share` cannot be undone. **Nothing per reader on a document in a public store.** No grant, no revocation, no audience list. **No delegated writing.** A received key never grants a write, and no call adds a writer to a document. **No mailbox model.** Do not build on the raw deposit list. **No cross-broker reference.** A returned reference resolves for users of the same broker. **No unfiltered read through `useShape`.** Members that yield items are filtered and mutations pass through; anything else throws. A document reached through that view alone, read nowhere else first, does not appear. ## Change policy This surface changes, and shrinks. The package does not offer semantic-version stability. Re-pull this contract at every upgrade.