feat(client): per-document reactive subscription (doc_subscribe), drop polling
Expose subscribeDoc(nuri, onChange) / subscribeDocs(nuris, onChange) wrapping the real ng.doc_subscribe — per-document, event-driven (initial State + a Patch per commit, local or broker-synced from a remote peer), returning a sync unsubscribe. subscribeDocs isolates per doc (a failing/unsynced doc never aborts the others), so it sidesteps the ORM fan-out hang (never orm_start_graph(graphs:[…])). Replace the setInterval polling in inbox.watch() and discovery.watchIndex() with doc_subscribe — same public contract, now push not poll. NextGraph is subscription- first; no polling remains. Verified against the real broker (@data harness spike): the doc_subscribe callback marshals across the iframe RPC (@ng-org/web strips the callback and drives it via a MessagePort — no DataCloneError) and fires on the initial push and on a real write. Lib bun test 91 pass, tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,25 @@ function makeFakeNg() {
|
||||
const quads: Quad[] = [];
|
||||
let docCounter = 0;
|
||||
|
||||
// Reactive subscriptions (see inbox.test.ts): doc_subscribe registers a
|
||||
// callback per anchor + fires an initial push; sparql_update pushes a Patch to
|
||||
// that anchor's subscribers, so discovery.watchIndex (now event-driven) works
|
||||
// without a timer.
|
||||
const subs = new Map<string, Set<(r: unknown) => void>>();
|
||||
const doc_subscribe = mock(async (nuri: string, _sid: unknown, cb: (r: unknown) => void) => {
|
||||
let set = subs.get(nuri);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
subs.set(nuri, set);
|
||||
}
|
||||
set.add(cb);
|
||||
queueMicrotask(() => cb({ V0: { State: { doc: nuri } } }));
|
||||
return () => set!.delete(cb);
|
||||
});
|
||||
const pushTo = (anchor: string): void => {
|
||||
for (const cb of subs.get(anchor) ?? []) cb({ V0: { Patch: { doc: anchor } } });
|
||||
};
|
||||
|
||||
const doc_create = mock(async (..._a: unknown[]) => `did:ng:o:doc${++docCounter}`);
|
||||
|
||||
const sparql_update = mock(async (...a: unknown[]) => {
|
||||
@@ -93,6 +112,7 @@ function makeFakeNg() {
|
||||
const o = m[2] !== undefined ? unescapeLiteral(m[2]) : (m[3] ?? "");
|
||||
quads.push({ g, s, p, o });
|
||||
}
|
||||
pushTo(g); // local-push to the written graph's subscribers
|
||||
return undefined;
|
||||
});
|
||||
|
||||
@@ -157,7 +177,7 @@ function makeFakeNg() {
|
||||
return { results: { bindings: [] } };
|
||||
});
|
||||
|
||||
return { doc_create, sparql_update, sparql_query, _quads: quads };
|
||||
return { doc_create, doc_subscribe, sparql_update, sparql_query, _quads: quads };
|
||||
}
|
||||
|
||||
const SESSION: RegistrySession = { sessionId: "sid-1", privateStoreId: "PRIV" };
|
||||
|
||||
Reference in New Issue
Block a user