docs: l'engagement de la couche d'indexation, et ses deux déclarations
Le dépôt gagne sa doctrine et ses contrats, dans le régime bidirectionnel. Il publie son engagement — ce qu'un consommateur peut attendre de l'indexation — et déclare ce qu'il consomme lui-même, du polyfill et de ng-e2e-helpers. Les deux déclarations sont écrites depuis les appels réels, pas depuis ce que la surface offre : un usage non déclaré est la faute du consommateur en cas de rupture, et une surface offerte mais non déclarée reste librement modifiable. Version 1.0.0, pas 0.1.0 : sous semver, 0.x ne promet rien du tout, donc le majeur ne porte son signal qu'à partir de 1. Ce dépôt étant sur main, c'est une version pleine et non une pré-version.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
type: usage
|
||||
summary: The seven polyfill entries the indexing layer stands on, the constraints it holds itself to, and what it had to build for want of a published helper
|
||||
against: "@ng-eventually/polyfill@1.0.0-dev.1"
|
||||
---
|
||||
|
||||
# usage_ng-helpers — `@ng-helpers/indexing` on `polyfill-surface`
|
||||
|
||||
The consumer is the indexing layer: an index is an ordinary public document, contributions reach it through its inbox, and its owner curates it. NextGraph has no indexing concept, so nothing of what a deposit *means* belongs upstream — the polyfill's inbox stays generic and carries opaque payloads, and this layer decides what they say.
|
||||
|
||||
The whole runtime dependency passes through **one file**, the adapter that builds our `NextGraphPort`. Everything else in the package is written against that port, so a change to the engagement breaks exactly one file and nothing else. The types are imported type-only, so they are literally the published ones rather than a copy that can drift.
|
||||
|
||||
## Consumed surface
|
||||
|
||||
**Placement** — `storeRegistry.createEntityDoc("public")`, for the index document itself; `storeRegistry.openDocumentInbox(doc)`, called once at creation.
|
||||
|
||||
**Reading** — `readUnion([doc])` and the type `UnionSubject`. Used both for the index document and for resolving a deposited reference.
|
||||
|
||||
**Writing** — `docs.sparqlUpdate(sessionId, update, anchor)`, with the document named ONCE as the anchor so the statement carries no `GRAPH <…>` wrapper. It is the only write this package makes, and it is always an `INSERT DATA` of literal triples.
|
||||
|
||||
**Inbox** — `inbox.postToDocument(doc, { payload })` for depositing, `inbox.readForDocument(doc)` for the owner draining it, and the shape of `Deposit` (`from` / `payload` / `ts`), which our `IncomingDeposit` mirrors.
|
||||
|
||||
**Types** — `Nuri`, `NuriLike`, `PrincipalId`, `UnionSubject`.
|
||||
|
||||
**Bootstrap, in the end-to-end application only** — `configure`, this package's `init` (for the `sessionId` its callback delivers), and `ensureIdentity`.
|
||||
|
||||
Everything else on the engagement is offered and NOT consumed: `watchShape`, `useShape`, `subscribeDoc`/`subscribeDocs`, `docs.docCreate`, `docs.sparqlQuery`, `storeRegistry.listMyEntityDocs`/`resolveScopeGraph`/`resolveWriteGraph`, `inbox.share`/`post`/`read`/`readSynced`/`readSyncedForDocument`/`processInbox`/`watch`, `ng`, `initNg`. It is safely evolvable as far as this layer is concerned.
|
||||
|
||||
## Constraints
|
||||
|
||||
**One port is one identity.** The polyfill's session is one user's and no call takes an identifier, so an `Indexing` handle is one person's. Two users mean two handles — which is also what keeps our multi-actor tests honest: a depositor obtains the index NURI the way an application does, never through a shared variable.
|
||||
|
||||
**`sessionId` is relayed, never converted.** We carry it at the engagement's own `string | number` and hand it back untouched.
|
||||
|
||||
**The write is add-only, and structurally so.** There is no delete builder anywhere in this package, and the only statement it can compose is an anchored `INSERT DATA`. Our own tests execute that SPARQL against an engine that refuses anything else, so a removal is unrunnable rather than merely undetected. This constrains what we ask of the engagement: we need exactly one write primitive and no more.
|
||||
|
||||
**The inbox is opened at creation, from one place.** The engagement disclaims coalescing `openDocumentInbox` across pages, so we never open an index's inbox anywhere but in `createIndex`, under its owner, at the moment the document is created.
|
||||
|
||||
**Every payload out of an inbox is untrusted input.** Anyone may deposit anything, so nothing read from a deposit reaches a query before being checked; a non-reference is reported, never thrown on.
|
||||
|
||||
**An empty read is never treated as "empty".** Nothing in this layer reads `[]` from `readUnion` as "a valid index that happens to hold nothing" — the descriptor check refuses a document declaring no field, and that refusal aborts curation before a single write.
|
||||
|
||||
## Frictions
|
||||
|
||||
**The engagement does not say what `readUnion` does with a document it cannot read.** It says what it returns for a document it can, and it says that a rejection means "unknown, never absent" — but not whether an unreadable document inside the list comes back as a rejection or is swallowed into the result. We assume the worst (swallowed, therefore indistinguishable from empty) and code defensively around it. A sentence in `## Guarantees` settling this would replace a guess we are carrying in every read path.
|
||||
|
||||
**No published NURI type guard.** `## Guarantees` states plainly that no type guard is published, so this layer carries its own — and it needs one, because a NURI arrives here from an untrusted inbox deposit and must be checked before it can be written between angle brackets. The check we wrote is a guess at what the engagement considers a valid `Nuri`, and a wrong guess is either a rejected legitimate reference or an injected one.
|
||||
|
||||
**No published escaping helpers.** The engagement lists no `escapeIri`/`escapeLiteral`, and we compose SPARQL against `docs.sparqlUpdate` — so this package carries its own escaping rather than reach into the provider's internals. That is a security-relevant duplication of something the provider certainly already has: two implementations of the same rule, one of which is not the one the provider tests.
|
||||
|
||||
**No published way to write triples above the raw SPARQL primitive.** `docs.sparqlUpdate` is the level we had to align on, which is why the two frictions above exist at all. A published "add these triples to this document" would remove the query composition, the escaping and the NURI validation from this layer in one move.
|
||||
Reference in New Issue
Block a user