fc3c129bd3
@ng-org/web fait déjà la redirection vers le broker — même hôte, même forme, même test de cadre (dist/ngweb.js). Le polyfill n'a donc rien à réimplémenter là : ce qui lui revient, c'est la seule chose qu'init() ne peut pas faire, à savoir mettre ?ng-id= dans window.location.href avant qu'il ne le lise. rememberIdentity() s'exécute désormais sur les trois chemins — identité posée par l'appelant, venue du stockage, ou saisie à la barrière. Le cas du stockage était le silencieux : le paramètre restait absent, l'iframe lisait une identité vide et provisionnait un second espace virtuel, sans erreur. L'écriture dans le stockage et celle dans la barre d'adresse sont deux try indépendants : un stockage qui refuse d'écrire ne doit pas emporter avec lui le paramètre, qui est le seul à franchir la frontière de partition. Le contrat retire l'obligation « être ouverte via la redirection du broker » : elle n'a jamais été celle de l'application. Ni broker, ni iframe, ni redirection n'y sont plus nommés.
137 lines
8.1 KiB
Markdown
137 lines
8.1 KiB
Markdown
---
|
|
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 `ensureIdentity()` in a browser context before rendering its interface; it mounts a barrier in the document.
|
|
|
|
## 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
|
|
getSession?: () => Promise<RegistrySession>; // resolve the session (a thunk)
|
|
normalizeId?: (id: string) => string;
|
|
sharedWallet?: SharedWalletConfig; // { fileUrl, password, importUrl? }
|
|
currentUser?: PrincipalId;
|
|
debugAccessLog?: boolean;
|
|
init?: (...args: any[]) => any;
|
|
initNg?: (...args: any[]) => any;
|
|
}
|
|
|
|
// ── identity — one await before the application renders ──────────────────
|
|
export async function ensureIdentity(): Promise<PrincipalId>; // 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<Nuri>;
|
|
listMyEntityDocs(scope: Scope): Promise<Nuri[]>;
|
|
resolveScopeGraph(scope: Scope): Promise<Nuri>;
|
|
resolveWriteGraph(scope: Scope): Promise<Nuri>;
|
|
openDocumentInbox(doc: NuriLike): Promise<Nuri>;
|
|
};
|
|
|
|
// ── reading ──────────────────────────────────────────────────────────────
|
|
export async function readUnion(docs: NuriLike[]): Promise<UnionSubject[]>;
|
|
export interface UnionSubject { subject: string; graph: Nuri; props: Record<string, string[]> }
|
|
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<Nuri>;
|
|
sparqlQuery(sessionId: string, query: string, base?: string, anchor?: NuriLike, label?: string): Promise<unknown>;
|
|
sparqlUpdate(sessionId: string, query: string, anchor?: NuriLike, label?: string): Promise<void>;
|
|
};
|
|
|
|
// ── inbox: giving to read, and depositing ────────────────────────────────
|
|
export const inbox: {
|
|
share(doc: NuriLike, toUser: string): Promise<void>; // give a reader the key
|
|
post(targetInbox: NuriLike, opts: PostOptions): Promise<void>;
|
|
postToDocument(doc: NuriLike, opts: PostOptions): Promise<void>;
|
|
read(targetInbox: NuriLike): Promise<Deposit[]>; // only your own
|
|
readForDocument(doc: NuriLike): Promise<Deposit[]>;
|
|
readSynced(targetInbox: NuriLike): Promise<Deposit[]>;
|
|
processInbox(targetInbox: NuriLike): Promise<Deposit[]>;
|
|
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<string, any>; // call this instead of the `ng` passed to `configure`
|
|
export function init(...args: any[]): any;
|
|
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.
|
|
|
|
`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.
|