fix: lire un document par la requête ancrée, readUnion ayant quitté le polyfill

Le contrat polyfill-surface passe de 1.0.0-dev.6 à 1.0.0-dev.15, épinglé
désormais sur le tag polyfill/v1.0.0-dev.15. readUnion et UnionSubject ont
été retirés en dev.8 : l'application e2e lit un document par un SELECT ancré
regroupé par sujet, en ignorant les registres urn:ng-eventually: que la
requête renvoie désormais.

La déclaration usage_ng-helpers retire ce que l'engagement a absorbé
(exemple de forme, initNg, orm_start_graph corrigé en dev.7, comportement
d'un document illisible, lecture d'un document public par sa seule
référence, pin par tag) et garde les demandes encore ouvertes.

contract_indexing-layer 4.0.1 (patch) : la lecture d'un index ne cite plus
readUnion.
This commit is contained in:
Sylvain Duchesne
2026-10-01 16:22:02 +02:00
parent 71667ca14e
commit 8bcea7c5be
12 changed files with 305 additions and 82 deletions
+51 -13
View File
@@ -15,9 +15,9 @@
* Nothing here makes an entry of a deposit, and nothing here can ask for one: that is
* the business of the layer below, and this page is where that shows or does not.
* Everything else here is the polyfill (`configure`, `ensureIdentity`, `init`, `initNg`,
* `ng`, `readUnion`, `docs`, `useShapeQuery`) -- including reading the index, which is an
* ordinary `readUnion` of an ordinary document. If something here is awkward, it is
* awkward for every consumer, which is the second reason to write it this way.
* `ng`, `docs`, `useShapeQuery`) -- including reading the index, which is an ordinary
* anchored `docs.sparqlQuery` of an ordinary document. If something here is awkward, it
* is awkward for every consumer, which is the second reason to write it this way.
*
* This page WIRES THE ORM, because one thing here has no other door: the store enumeration it
* used to discover a leaked document left the engagement, and what replaces it is a shape
@@ -43,13 +43,11 @@ import {
init,
initNg,
ng,
readUnion,
useShapeQuery,
type Scope,
type ShapeObservable,
type ShapeQuery,
type ShapeType,
type UnionSubject,
} from "@ng-eventually/polyfill";
import { ng as realNg, init as realInit } from "@ng-org/web";
import { RdfOrmSubscription, initNg as ormInitNg } from "@ng-org/orm";
@@ -60,7 +58,13 @@ import { ENTRY_VALUE, INDEX_FIELD, indexing, type Indexing } from "../src/index"
import { indexingOn } from "../src/indexing";
import type { NextGraphPort } from "../src/port";
import { polyfillPort } from "../src/polyfill-adapter";
import type { BrokenInboxOutcome, IndexRow, IndexingBridge, SelectOutcome } from "./bridge";
import type {
BrokenInboxOutcome,
DocumentSubject,
IndexRow,
IndexingBridge,
SelectOutcome,
} from "./bridge";
// bootstrap: the one polyfill-era call, then the SDK-shaped ones
//
@@ -82,7 +86,7 @@ configure({
useShape: () => {
throw new Error(
"[e2e] useShape was never wired: this page has no framework adapter to give it, " +
"and it reads through readUnion and useShapeQuery instead",
"and it reads through docs.sparqlQuery and useShapeQuery instead",
);
},
ormSubscription: RdfOrmSubscription.getOrCreate,
@@ -129,8 +133,8 @@ async function boot(): Promise<void> {
// through it, and this package's own `initNg` is the one to CALL -- not the SDK's,
// which was handed to `configure` and is reached from there. The `ng` it is given is
// this package's too, the one the surface says to call instead of the one `configure`
// was passed. Measured: the argument makes no difference either way, because the call
// substitutes its own -- see `usage_ng-helpers`'s `## Frictions`.
// was passed. The engagement states the argument is discarded either way: the call
// substitutes that same published `ng`.
initNg(ng, session);
// The session id, and nothing else — this application never names a port, and never
// awaits anything here: a handle reaches nothing. A reference this application hands
@@ -325,6 +329,40 @@ function rowsOf(result: unknown): Array<Record<string, string>> {
});
}
/**
* Everything a document holds, grouped by subject -- what an application writes in place
* of the `readUnion` the polyfill withdrew in `1.0.0-dev.8`.
*
* One anchored `SELECT ?s ?p ?o`: the anchor makes the document the query's default graph,
* so the body names no graph and reads that document and nothing else. A document this
* identity cannot read REJECTS, and the rejection is left to reach the caller -- an
* unreadable document is "unknown", never "empty". The polyfill's own registers live
* under `urn:ng-eventually:` in the same graph and come back with the rest; they are the
* polyfill's, not the document's, so they are dropped here.
*/
async function readDocument(doc: string): Promise<DocumentSubject[]> {
const session = await sessionReady;
const result = await docs.sparqlQuery(
session.session_id,
"SELECT ?s ?p ?o WHERE { ?s ?p ?o }",
undefined,
doc,
);
const bySubject = new Map<string, Record<string, string[]>>();
for (const row of rowsOf(result)) {
const { s, p, o } = row;
if (s === undefined || p === undefined || o === undefined) continue;
if (s.startsWith(RESERVED_PREFIX)) continue;
const props = bySubject.get(s) ?? {};
(props[p] ??= []).push(o);
bySubject.set(s, props);
}
return [...bySubject].map(([subject, props]) => ({ subject, props }));
}
/** The polyfill's reserved prefix: what it writes into a document under it is its own. */
const RESERVED_PREFIX = "urn:ng-eventually:";
// the acts
const bridge: IndexingBridge = {
@@ -372,12 +410,12 @@ const bridge: IndexingBridge = {
/**
* The index read BY THIS APPLICATION, with nothing the package publishes but
* `ENTRY_VALUE` — the claim "an index is an ordinary document" performed rather than
* repeated. `readUnion([index])` is the same call this page makes on any other
* repeated. `readDocument(index)` is the same read this page makes of any other
* document; the index's own subject is the one that is not an entry, told apart by
* being the document itself.
*/
async read(index: string): Promise<IndexRow[]> {
const subjects = await readUnion([index]);
const subjects = await readDocument(index);
const rows: IndexRow[] = [];
for (const subject of subjects) {
if (subject.subject === index) continue;
@@ -394,8 +432,8 @@ const bridge: IndexingBridge = {
return rows;
},
async readRaw(doc: string): Promise<UnionSubject[]> {
return readUnion([doc]);
async readRaw(doc: string): Promise<DocumentSubject[]> {
return readDocument(doc);
},
/**