docs: dire l'effet et non le canal, et n'exiger le NURI en dur que d'un index global
This commit is contained in:
@@ -62,6 +62,10 @@ export class FakeNextGraph {
|
||||
readonly #documents = new Map<string, StoredDocument>();
|
||||
/** Documents the broker currently cannot answer about. See `breakReadsOf`. */
|
||||
readonly #unreachable = new Map<string, string>();
|
||||
/** Inboxes the broker currently cannot READ. See `breakInboxReadsOf`. */
|
||||
readonly #inboxUnreadable = new Map<string, string>();
|
||||
/** Inboxes the broker currently refuses to WATCH. See `breakWatchingOf`. */
|
||||
readonly #inboxUnwatchable = new Map<string, string>();
|
||||
/** Every live watch, across every identity — a session watching its own inbox. */
|
||||
#watches: Watch[] = [];
|
||||
/** Notifications the broker has not handed over yet. See `deliverNotifications`. */
|
||||
@@ -132,6 +136,43 @@ export class FakeNextGraph {
|
||||
this.#unreachable.delete(asNuri(doc));
|
||||
}
|
||||
|
||||
/**
|
||||
* The broker serves the DOCUMENT but not its INBOX.
|
||||
*
|
||||
* Not a contrivance: upstream an inbox is a repo of its own, reached through an
|
||||
* address `openDocumentInbox` resolves and read with that repo's capability,
|
||||
* while the document itself is read by `readUnion`. Two repos, two reads — so
|
||||
* one answering while the other does not is what a partial failure looks like,
|
||||
* and it is the state that makes a catch-up fail on one index and no other.
|
||||
*/
|
||||
breakInboxReadsOf(doc: NuriLike, reason: string): void {
|
||||
this.#inboxUnreadable.set(asNuri(doc), reason);
|
||||
}
|
||||
|
||||
/** The inbox can be read again. */
|
||||
healInboxReadsOf(doc: NuriLike): void {
|
||||
this.#inboxUnreadable.delete(asNuri(doc));
|
||||
}
|
||||
|
||||
/**
|
||||
* The broker refuses to keep this session posted about that inbox, while
|
||||
* everything else about it still works.
|
||||
*
|
||||
* Watching is a live subscription, set up and held open for as long as the
|
||||
* session lasts; reading an inbox is one question and one answer. A subscription
|
||||
* can be refused where a read succeeds, which is the state that leaves an index
|
||||
* caught up but unwatched — deposits into it going unnoticed until the next
|
||||
* connection, exactly as the failure this models says.
|
||||
*/
|
||||
breakWatchingOf(doc: NuriLike, reason: string): void {
|
||||
this.#inboxUnwatchable.set(asNuri(doc), reason);
|
||||
}
|
||||
|
||||
/** The inbox can be watched again. */
|
||||
healWatchingOf(doc: NuriLike): void {
|
||||
this.#inboxUnwatchable.delete(asNuri(doc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hands over every inbox notification the broker was holding, and waits for the
|
||||
* sessions watching to finish with them — including notifications those very runs
|
||||
@@ -297,6 +338,12 @@ export class FakeNextGraph {
|
||||
"is reading it, and you may only READ your own",
|
||||
);
|
||||
}
|
||||
const unwatchable = this.#inboxUnwatchable.get(doc);
|
||||
// Refused AFTER the owner check: resolving the address is an owner-only act, so
|
||||
// a stranger is turned away before any subscription is ever attempted.
|
||||
if (unwatchable !== undefined) {
|
||||
throw new Error(`cannot watch the inbox of ${doc}: ${unwatchable}`);
|
||||
}
|
||||
// Watching resolves the inbox address, and the call that resolves one opens it
|
||||
// when there is none — the same idempotent call `openInbox` makes.
|
||||
stored.deposits ??= [];
|
||||
@@ -324,6 +371,10 @@ export class FakeNextGraph {
|
||||
"inbox, you may only READ your own",
|
||||
);
|
||||
}
|
||||
const unreadable = this.#inboxUnreadable.get(doc);
|
||||
if (unreadable !== undefined) {
|
||||
throw new Error(`cannot read the inbox of ${doc}: ${unreadable}`);
|
||||
}
|
||||
return [...stored.deposits].sort((a, b) => a.ts - b.ts);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user