@cello-protocol/daemon 0.0.122 → 0.0.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/daemon.js +65 -12
  2. package/dist/daemon.js.map +1 -1
  3. package/dist/document-store.d.ts +0 -88
  4. package/dist/document-store.d.ts.map +1 -1
  5. package/dist/document-store.js +1 -344
  6. package/dist/document-store.js.map +1 -1
  7. package/dist/document-write-path.d.ts.map +1 -1
  8. package/dist/document-write-path.js +43 -10
  9. package/dist/document-write-path.js.map +1 -1
  10. package/dist/reconnect-drain.d.ts +7 -0
  11. package/dist/reconnect-drain.d.ts.map +1 -1
  12. package/dist/reconnect-drain.js +18 -4
  13. package/dist/reconnect-drain.js.map +1 -1
  14. package/dist/retry-queue.d.ts +9 -1
  15. package/dist/retry-queue.d.ts.map +1 -1
  16. package/dist/retry-queue.js +38 -9
  17. package/dist/retry-queue.js.map +1 -1
  18. package/dist/session-content-handlers.d.ts.map +1 -1
  19. package/dist/session-content-handlers.js +7 -1
  20. package/dist/session-content-handlers.js.map +1 -1
  21. package/dist/session-node-manager.d.ts +4 -1
  22. package/dist/session-node-manager.d.ts.map +1 -1
  23. package/dist/session-node-manager.js +64 -8
  24. package/dist/session-node-manager.js.map +1 -1
  25. package/package.json +3 -3
  26. package/dist/document-delivery-transport.d.ts +0 -78
  27. package/dist/document-delivery-transport.d.ts.map +0 -1
  28. package/dist/document-delivery-transport.js +0 -109
  29. package/dist/document-delivery-transport.js.map +0 -1
  30. package/dist/document-delivery.d.ts +0 -130
  31. package/dist/document-delivery.d.ts.map +0 -1
  32. package/dist/document-delivery.js +0 -246
  33. package/dist/document-delivery.js.map +0 -1
  34. package/dist/document-handshake.d.ts +0 -88
  35. package/dist/document-handshake.d.ts.map +0 -1
  36. package/dist/document-handshake.js +0 -239
  37. package/dist/document-handshake.js.map +0 -1
  38. package/dist/document-lifecycle.d.ts +0 -104
  39. package/dist/document-lifecycle.d.ts.map +0 -1
  40. package/dist/document-lifecycle.js +0 -363
  41. package/dist/document-lifecycle.js.map +0 -1
  42. package/dist/document-notify.d.ts +0 -130
  43. package/dist/document-notify.d.ts.map +0 -1
  44. package/dist/document-notify.js +0 -313
  45. package/dist/document-notify.js.map +0 -1
  46. package/dist/document-reachability.d.ts +0 -42
  47. package/dist/document-reachability.d.ts.map +0 -1
  48. package/dist/document-reachability.js +0 -72
  49. package/dist/document-reachability.js.map +0 -1
  50. package/dist/document-rejection.d.ts +0 -224
  51. package/dist/document-rejection.d.ts.map +0 -1
  52. package/dist/document-rejection.js +0 -374
  53. package/dist/document-rejection.js.map +0 -1
  54. package/dist/line-lcs.d.ts +0 -51
  55. package/dist/line-lcs.d.ts.map +0 -1
  56. package/dist/line-lcs.js +0 -71
  57. package/dist/line-lcs.js.map +0 -1
@@ -1,313 +0,0 @@
1
- /**
2
- * DOD-DOC-NOTIFY-1 — passive notification and the two read calls (§16.5, §4.1).
3
- *
4
- * ── WHY NO DOORBELL ───────────────────────────────────────────────────────────────────────────
5
- *
6
- * A document update raises NO doorbell (§11.3 — doorbell-on-update is parked). The notice sits in
7
- * the inbox aggregation and the agent finds it the next time it looks. That is the whole design:
8
- * a collaborator typing produces a stream of updates, and a doorbell per update would interrupt
9
- * the operator's agent continuously for something that has no deadline. The one-line summary the
10
- * agent eventually reads carries the same information at none of the cost.
11
- *
12
- * Following the `contact_rename_notices` precedent: there is no inbox store — the inbox is computed
13
- * per call — so a derived section needs its own table and its own getter.
14
- *
15
- * The notice carries `document_id` and a PENDING COUNT, and nothing else. Not a preview, not the
16
- * first line, not the author's words. Content reaches the agent only through the two read calls
17
- * below, which are screened; a notification that carried content would be an unscreened path into
18
- * the agent's context, which is the whole prompt-injection surface §3.1 exists to close.
19
- *
20
- * ── THE TWO READ CALLS (§4.1) ─────────────────────────────────────────────────────────────────
21
- *
22
- * `diffStats` is STRUCTURAL ONLY — counts, ranges, and the overlap flag. It exists so an agent can
23
- * decide whether to read without reading, which is exactly what a cautious agent should be able to
24
- * do. `diff` is the git-like diff itself and is an ordinary screened read.
25
- *
26
- * ── SUPPORTED TYPES FOR `diff`, DECIDED IN-UNIT ───────────────────────────────────────────────
27
- *
28
- * Markdown, plain text, and JSON. Markdown and plain text share one line-diff implementation (a
29
- * markdown file IS lines); JSON gets a key-path diff because a line diff over re-serialized JSON
30
- * reports the whole document as changed whenever a formatter touches it. Anything else returns a
31
- * REFUSAL naming the type rather than a line diff over bytes that are not lines — a diff that is
32
- * wrong is worse than no diff, because the agent cannot tell.
33
- */
34
- import { lineRuns, toChunks } from "./line-lcs.js";
35
- const CREATE_NOTICES_SQL = `
36
- CREATE TABLE IF NOT EXISTS document_notices (
37
- agent_id TEXT NOT NULL,
38
- document_id TEXT NOT NULL,
39
- -- The count, not the content. See the header.
40
- pending INTEGER NOT NULL,
41
- noticed_at INTEGER NOT NULL,
42
- PRIMARY KEY (agent_id, document_id)
43
- );
44
- `;
45
- /** Types `diff` can render. Decided in-unit; see the header for why the list is closed. */
46
- export const DIFFABLE_DOCUMENT_TYPES = ["markdown", "text", "json"];
47
- export class DocumentNotifications {
48
- #store;
49
- #logger;
50
- constructor(store, logger) {
51
- this.#store = store;
52
- this.#logger = logger;
53
- this.#store.rawDb.exec(CREATE_NOTICES_SQL);
54
- }
55
- /**
56
- * Note that a document has unread updates. Passive — this never pushes anything.
57
- *
58
- * The count is REPLACED rather than incremented, and is passed in by the caller from the log,
59
- * so the notice cannot drift from the document. An incremented counter is a second tally of a
60
- * fact the log already holds, and the two disagree the first time a notice write is missed.
61
- */
62
- notice(agentId, documentId, pending, nowMs) {
63
- if (pending <= 0) {
64
- // Nothing pending is not a notice with zero in it — it is the absence of a notice. A row
65
- // saying "0 unread" is an inbox entry the agent must read to discover it has nothing to read.
66
- //
67
- // Deleted DIRECTLY rather than through `clear()`: that method's event means "the agent
68
- // fetched", and firing it because a count fell to zero reports a fetch that never happened.
69
- this.#store.rawDb
70
- .prepare("DELETE FROM document_notices WHERE agent_id = ? AND document_id = ?")
71
- .run(agentId, documentId);
72
- return;
73
- }
74
- this.#store.rawDb
75
- .prepare(`INSERT INTO document_notices (agent_id, document_id, pending, noticed_at)
76
- VALUES (?, ?, ?, ?)
77
- ON CONFLICT (agent_id, document_id)
78
- DO UPDATE SET pending = excluded.pending, noticed_at = excluded.noticed_at`)
79
- .run(agentId, documentId, pending, nowMs);
80
- }
81
- /** The derived inbox section. Read-only — fetching is what clears, and that is explicit. */
82
- pending(agentId) {
83
- const rows = this.#store.rawDb
84
- .prepare(`SELECT document_id, pending, noticed_at FROM document_notices
85
- WHERE agent_id = ? ORDER BY noticed_at ASC, document_id ASC`)
86
- .all(agentId);
87
- return rows.map((r) => ({
88
- documentId: r["document_id"],
89
- pending: r["pending"],
90
- noticedAtMs: r["noticed_at"],
91
- }));
92
- }
93
- /**
94
- * Cleared by the agent's EXPLICIT fetch, never by the aggregation that displays it.
95
- *
96
- * Reading the inbox and clearing the notice are different acts: an inbox render that cleared
97
- * would drop the notice for an agent that merely glanced at a summary, and the update would then
98
- * be unread and unannounced — invisible in both directions.
99
- */
100
- clear(agentId, documentId) {
101
- const info = this.#store.rawDb
102
- .prepare("DELETE FROM document_notices WHERE agent_id = ? AND document_id = ?")
103
- .run(agentId, documentId);
104
- const cleared = Number(info.changes) > 0;
105
- // agentId included: this daemon attends several agents, and an event that names only the
106
- // document cannot be attributed to one of them.
107
- if (cleared)
108
- this.#logger.info("document.notice.cleared", { agentId, documentId });
109
- return cleared;
110
- }
111
- /**
112
- * Structural counts and ranges — NO content. Lets an agent decide whether to read.
113
- *
114
- * `before` and `after` are the materialized text of the document at the two points; the caller
115
- * holds the engine and therefore the materialization.
116
- *
117
- * Uses the SHARED line LCS, not a positional walk. The first version compared line i to line i,
118
- * which is correct only while the line count is unchanged: inserting one line at the top of a
119
- * three-line file reported `+4 -3` with a single range covering the whole document. The counts
120
- * being wrong is the smaller half — `overlap` is derived from those ranges, so it became
121
- * permanently true, and a flag that always says yes trains the agent to ignore it. Same defect
122
- * class WRITE-1 measured, which is why there is now one LCS rather than two.
123
- */
124
- diffStats(documentId, before, after,
125
- /**
126
- * The operator's own edited line numbers. REQUIRED — pass `null` to say "not computed".
127
- * Defaulting to `[]` made a caller that simply forgot receive `overlap: false`, the reassuring
128
- * answer, indistinguishable from "checked, and there is no conflict". This is the one
129
- * judgement field in an otherwise structural result.
130
- */
131
- myEdits, documentType) {
132
- const a = toChunks(before);
133
- const b = toChunks(after);
134
- const runs = lineRuns(a, b);
135
- if (runs === null) {
136
- // Above the LCS limit. Said plainly rather than degraded silently into a whole-file range
137
- // that would read as a measurement.
138
- return {
139
- documentId,
140
- linesAdded: b.length,
141
- linesRemoved: a.length,
142
- ranges: [{ start: 1, end: Math.max(a.length, b.length) }],
143
- keyPaths: [],
144
- keyPathsComputed: false,
145
- overlap: myEdits === null ? null : myEdits.length > 0,
146
- truncated: true,
147
- };
148
- }
149
- // G3: for a JSON document the DoD asks for KEY ranges, not line ranges. Produced here rather
150
- // than left as a permanently-empty array, which reads as "no key paths changed" instead of
151
- // "not computed" — the same absent-is-not-fine trap as the overlap default above.
152
- let keyPaths = [];
153
- let keyPathsComputed = false;
154
- if (documentType === "json") {
155
- const paths = changedKeyPaths(before, after);
156
- if (paths !== null) {
157
- keyPaths = paths;
158
- keyPathsComputed = true;
159
- }
160
- }
161
- let added = 0;
162
- let removed = 0;
163
- const ranges = [];
164
- for (const r of runs) {
165
- added += r.bEnd - r.bStart;
166
- removed += r.aEnd - r.aStart;
167
- // 1-based inclusive, over the BEFORE text — which is the text the operator's own edit line
168
- // numbers refer to, and therefore the only coordinate space in which the overlap comparison
169
- // means anything. A pure insertion has an empty before-range, so it is reported at the line
170
- // it precedes rather than as a zero-width range nobody can act on.
171
- ranges.push({ start: r.aStart + 1, end: r.aEnd === r.aStart ? r.aStart + 1 : r.aEnd });
172
- }
173
- const touched = new Set();
174
- for (const r of ranges)
175
- for (let n = r.start; n <= r.end; n++)
176
- touched.add(n);
177
- return {
178
- documentId,
179
- linesAdded: added,
180
- linesRemoved: removed,
181
- ranges,
182
- keyPaths,
183
- keyPathsComputed,
184
- overlap: myEdits === null ? null : myEdits.some((line) => touched.has(line)),
185
- truncated: false,
186
- };
187
- }
188
- /**
189
- * The git-like diff. An ordinary screened read — the caller screens it; this renders it.
190
- *
191
- * An unsupported type is REFUSED by name rather than line-diffed anyway. A diff that is silently
192
- * wrong is worse than no diff, because the agent has no way to tell and will act on it.
193
- */
194
- diff(documentType, before, after, documentId) {
195
- if (!DIFFABLE_DOCUMENT_TYPES.includes(documentType)) {
196
- return {
197
- ok: false,
198
- reason: "document_type_not_diffable",
199
- detail: `this build renders diffs for ${DIFFABLE_DOCUMENT_TYPES.join(", ")} and this document is ` +
200
- `"${documentType}" — read the document instead of a diff that would be guesswork`,
201
- };
202
- }
203
- void documentId;
204
- if (documentType === "json") {
205
- // A key-path diff, not a line diff. Re-serialized JSON changes shape whenever a formatter
206
- // touches it, so a line diff reports the entire document as rewritten for a change of one
207
- // value — which is the false "everything changed" alarm that makes an agent stop reading
208
- // diffs at all.
209
- const parsedPaths = changedKeyPaths(before, after);
210
- if (parsedPaths === null) {
211
- return {
212
- ok: true,
213
- documentType: "json",
214
- diff: `! this document does not parse as JSON; showing a line diff instead\n${lineDiff(before, after)}`,
215
- fallback: "unparseable_json_line_diff",
216
- };
217
- }
218
- return { ok: true, documentType: "json", diff: jsonDiff(before, after) };
219
- }
220
- return { ok: true, documentType: documentType, diff: lineDiff(before, after) };
221
- }
222
- }
223
- function lineDiff(before, after) {
224
- // The SHARED LCS, same as diffStats. This renderer kept the positional walk after diffStats moved
225
- // off it, which is the worse half to leave behind: inserting one line into a ten-line file
226
- // rendered eighteen +/- lines, reporting the entire remainder as rewritten. That is the false
227
- // "everything changed" alarm that makes an agent stop reading diffs at all — the exact reasoning
228
- // that gave JSON a key-path diff, applied to markdown at last.
229
- const a = toChunks(before);
230
- const b = toChunks(after);
231
- const runs = lineRuns(a, b);
232
- if (runs === null)
233
- return "! this document is too large to diff line-by-line";
234
- const out = [];
235
- let ai = 0;
236
- for (const r of runs) {
237
- for (; ai < r.aStart; ai++)
238
- out.push(` ${strip(a[ai])}`);
239
- for (let i = r.aStart; i < r.aEnd; i++)
240
- out.push(`- ${strip(a[i])}`);
241
- for (let j = r.bStart; j < r.bEnd; j++)
242
- out.push(`+ ${strip(b[j])}`);
243
- ai = r.aEnd;
244
- }
245
- for (; ai < a.length; ai++)
246
- out.push(` ${strip(a[ai])}`);
247
- return out.join("\n");
248
- }
249
- /** Chunks carry their own trailing newline; the rendered diff supplies its own line breaks. */
250
- function strip(chunk) {
251
- return chunk.endsWith("\n") ? chunk.slice(0, -1) : chunk;
252
- }
253
- function flatten(value, prefix = "") {
254
- const out = new Map();
255
- if (value === null || typeof value !== "object") {
256
- out.set(prefix || "$", JSON.stringify(value));
257
- return out;
258
- }
259
- for (const [k, v] of Object.entries(value)) {
260
- // The separator is ESCAPED in the key. Unescaped, {"a.b": 1} and {"a": {"b": 1}} flatten to the
261
- // same path, so a document using dotted keys reports no change where there was one — silently,
262
- // which is the exact behaviour this unit refuses everywhere else.
263
- const path = prefix ? `${prefix}.${k.replace(/\\/g, "\\\\").replace(/\./g, "\\.")}` : k.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
264
- for (const [p, s] of flatten(v, path))
265
- out.set(p, s);
266
- }
267
- return out;
268
- }
269
- /** The changed key paths, or null when either side does not parse. */
270
- function changedKeyPaths(before, after) {
271
- let a;
272
- let b;
273
- try {
274
- a = flatten(JSON.parse(before));
275
- b = flatten(JSON.parse(after));
276
- }
277
- catch {
278
- return null;
279
- }
280
- const paths = new Set();
281
- for (const [path, value] of a)
282
- if (!b.has(path) || b.get(path) !== value)
283
- paths.add(path);
284
- for (const [path] of b)
285
- if (!a.has(path))
286
- paths.add(path);
287
- return [...paths].sort();
288
- }
289
- function jsonDiff(before, after) {
290
- let a;
291
- let b;
292
- try {
293
- a = flatten(JSON.parse(before));
294
- b = flatten(JSON.parse(after));
295
- }
296
- catch {
297
- // Deliberately explicit: unparseable JSON gets a stated fallback, not a silent line diff
298
- // pretending to be a key diff. The caller sees which it got.
299
- return `! this document does not parse as JSON; showing a line diff instead\n${lineDiff(before, after)}`;
300
- }
301
- const out = [];
302
- for (const [path, value] of a) {
303
- if (!b.has(path))
304
- out.push(`- ${path}: ${value}`);
305
- else if (b.get(path) !== value)
306
- out.push(`~ ${path}: ${value} -> ${b.get(path)}`);
307
- }
308
- for (const [path, value] of b)
309
- if (!a.has(path))
310
- out.push(`+ ${path}: ${value}`);
311
- return out.join("\n");
312
- }
313
- //# sourceMappingURL=document-notify.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-notify.js","sourceRoot":"","sources":["../src/document-notify.ts"],"names":[],"mappings":"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"}
@@ -1,42 +0,0 @@
1
- /**
2
- * DOD-DOC-DELIVERY-2 — binding the delivery worker's reachability check to `discovery_lookup`.
3
- *
4
- * The worker asks one question — "is this peer reachable right now?" — and its whole retry
5
- * behaviour turns on the answer being about the PEER. `runDiscoveryLookup` deliberately returns
6
- * five outcomes, only one of which is an answer about the peer at all, and this maps them:
7
- *
8
- * result / offline | unknown_agent -> false the peer is not reachable. A fact about them.
9
- * result / online -> true
10
- * error | malformed | timeout | send_failed
11
- * -> THROW the lookup did not happen. A fact about US.
12
- *
13
- * The throw is the point. `isPeerReachable` returning false for a directory fault would record a
14
- * directory outage — or our own signaling stream being down — as the operator's collaborator being
15
- * absent, and send them to ask the wrong person why nothing is syncing. The worker already treats
16
- * a throw as `lookup_failed` and keeps that distinction in the log; this is what feeds it.
17
- *
18
- * `unknown_agent` maps to false rather than throwing on purpose. It IS an answer from the
19
- * directory about the peer — the address does not resolve — and the honest consequence is the same
20
- * as offline: do not dial, retry later. It is a bad address rather than a transient absence, so it
21
- * is surfaced distinctly by the caller rather than being collapsed into the same log line.
22
- */
23
- import type { DiscoveryOutcome } from "./cross-node-negotiation.js";
24
- /** Why a lookup could not be performed. Carries the upstream reason verbatim. */
25
- export declare class DiscoveryUnavailableError extends Error {
26
- readonly kind: string;
27
- constructor(kind: string, reason: string);
28
- }
29
- export interface Reachability {
30
- reachable: boolean;
31
- /** Present when the directory answered but the address does not resolve to a known agent. */
32
- unknownAgent: boolean;
33
- }
34
- /**
35
- * Map a discovery outcome onto the worker's reachability question.
36
- *
37
- * Pure, so the mapping is testable without a directory — which matters, because the expensive way
38
- * to discover this mapping is wrong is in production, where the symptom is an operator chasing a
39
- * collaborator who was never offline.
40
- */
41
- export declare function reachabilityFromDiscovery(outcome: DiscoveryOutcome): Reachability;
42
- //# sourceMappingURL=document-reachability.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-reachability.d.ts","sourceRoot":"","sources":["../src/document-reachability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,iFAAiF;AACjF,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAKzC;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,6FAA6F;IAC7F,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAsCjF"}
@@ -1,72 +0,0 @@
1
- /**
2
- * DOD-DOC-DELIVERY-2 — binding the delivery worker's reachability check to `discovery_lookup`.
3
- *
4
- * The worker asks one question — "is this peer reachable right now?" — and its whole retry
5
- * behaviour turns on the answer being about the PEER. `runDiscoveryLookup` deliberately returns
6
- * five outcomes, only one of which is an answer about the peer at all, and this maps them:
7
- *
8
- * result / offline | unknown_agent -> false the peer is not reachable. A fact about them.
9
- * result / online -> true
10
- * error | malformed | timeout | send_failed
11
- * -> THROW the lookup did not happen. A fact about US.
12
- *
13
- * The throw is the point. `isPeerReachable` returning false for a directory fault would record a
14
- * directory outage — or our own signaling stream being down — as the operator's collaborator being
15
- * absent, and send them to ask the wrong person why nothing is syncing. The worker already treats
16
- * a throw as `lookup_failed` and keeps that distinction in the log; this is what feeds it.
17
- *
18
- * `unknown_agent` maps to false rather than throwing on purpose. It IS an answer from the
19
- * directory about the peer — the address does not resolve — and the honest consequence is the same
20
- * as offline: do not dial, retry later. It is a bad address rather than a transient absence, so it
21
- * is surfaced distinctly by the caller rather than being collapsed into the same log line.
22
- */
23
- /** Why a lookup could not be performed. Carries the upstream reason verbatim. */
24
- export class DiscoveryUnavailableError extends Error {
25
- kind;
26
- constructor(kind, reason) {
27
- super(`document_discovery_unavailable: ${kind}${reason ? ` (${reason})` : ""}`);
28
- this.name = "DiscoveryUnavailableError";
29
- this.kind = kind;
30
- }
31
- }
32
- /**
33
- * Map a discovery outcome onto the worker's reachability question.
34
- *
35
- * Pure, so the mapping is testable without a directory — which matters, because the expensive way
36
- * to discover this mapping is wrong is in production, where the symptom is an operator chasing a
37
- * collaborator who was never offline.
38
- */
39
- export function reachabilityFromDiscovery(outcome) {
40
- switch (outcome.kind) {
41
- case "result":
42
- if (outcome.state === "online")
43
- return { reachable: true, unknownAgent: false };
44
- if (outcome.state === "unknown_agent")
45
- return { reachable: false, unknownAgent: true };
46
- return { reachable: false, unknownAgent: false };
47
- case "error":
48
- // A directory DB fault. Retryable, and emphatically not the counterparty being offline —
49
- // the outcome type's own comment says so, and collapsing it here would undo that care.
50
- throw new DiscoveryUnavailableError("directory_error", outcome.reason);
51
- case "malformed":
52
- // A reply that did not parse: a protocol or version anomaly on a directory that DID respond.
53
- // Distinct from a clean directory error, and never availability.
54
- throw new DiscoveryUnavailableError("malformed_reply", "");
55
- case "timeout":
56
- // No reply in the window — an old directory, or a slow or dropped reply on a new one. Either
57
- // way we learned nothing about the peer.
58
- throw new DiscoveryUnavailableError("timeout", "");
59
- case "send_failed":
60
- // Our OWN signaling stream is down. The most important one to keep separate: reporting our
61
- // transport fault as the peer's absence points the operator at the wrong machine entirely.
62
- throw new DiscoveryUnavailableError("signaling_unavailable", outcome.reason);
63
- default: {
64
- // Exhaustiveness, enforced by the compiler. A new outcome kind must be classified
65
- // deliberately rather than defaulting into "reachable" or "offline", which are the two
66
- // answers a silent default would have to invent.
67
- const never = outcome;
68
- throw new DiscoveryUnavailableError("unrecognized_outcome", `unhandled discovery outcome ${JSON.stringify(never)}`);
69
- }
70
- }
71
- }
72
- //# sourceMappingURL=document-reachability.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-reachability.js","sourceRoot":"","sources":["../src/document-reachability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,iFAAiF;AACjF,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IACzC,IAAI,CAAS;IACtB,YAAY,IAAY,EAAE,MAAc;QACtC,KAAK,CAAC,mCAAmC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAyB;IACjE,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,QAAQ;YACX,IAAI,OAAO,CAAC,KAAK,KAAK,QAAQ;gBAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;YAChF,IAAI,OAAO,CAAC,KAAK,KAAK,eAAe;gBAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YACvF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAEnD,KAAK,OAAO;YACV,yFAAyF;YACzF,uFAAuF;YACvF,MAAM,IAAI,yBAAyB,CAAC,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAEzE,KAAK,WAAW;YACd,6FAA6F;YAC7F,iEAAiE;YACjE,MAAM,IAAI,yBAAyB,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;QAE7D,KAAK,SAAS;YACZ,6FAA6F;YAC7F,yCAAyC;YACzC,MAAM,IAAI,yBAAyB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAErD,KAAK,aAAa;YAChB,2FAA2F;YAC3F,2FAA2F;YAC3F,MAAM,IAAI,yBAAyB,CAAC,uBAAuB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAE/E,OAAO,CAAC,CAAC,CAAC;YACR,kFAAkF;YAClF,uFAAuF;YACvF,iDAAiD;YACjD,MAAM,KAAK,GAAU,OAAO,CAAC;YAC7B,MAAM,IAAI,yBAAyB,CACjC,sBAAsB,EACtB,+BAA+B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACvD,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}