doxum 0.1.23 → 0.1.26

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 (66) hide show
  1. package/README.md +208 -102
  2. package/dist/advanced.cjs +237 -2
  3. package/dist/advanced.cjs.map +1 -0
  4. package/dist/advanced.d.cts +90 -2
  5. package/dist/advanced.d.ts +90 -2
  6. package/dist/advanced.js +236 -1
  7. package/dist/advanced.js.map +1 -0
  8. package/dist/{contract-ULPQmiEs.d.ts → contract-CNR6BS75.d.cts} +216 -30
  9. package/dist/{contract-SYJFP0lM.d.cts → contract-COgxO5sv.d.ts} +216 -30
  10. package/dist/definition-CDOuvb5C.d.cts +111 -0
  11. package/dist/definition-xgk3YNBp.d.ts +111 -0
  12. package/dist/{driver-yFfvDMX5.js → driver-B5vdPohL.js} +59 -3
  13. package/dist/driver-B5vdPohL.js.map +1 -0
  14. package/dist/{driver-BD3DQOW6.cjs → driver-BL91gn7_.cjs} +107 -9
  15. package/dist/driver-BL91gn7_.cjs.map +1 -0
  16. package/dist/index.cjs +1637 -703
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +65 -33
  19. package/dist/index.d.ts +65 -33
  20. package/dist/index.js +1432 -497
  21. package/dist/index.js.map +1 -1
  22. package/dist/local-sync.cjs +69 -103
  23. package/dist/local-sync.cjs.map +1 -1
  24. package/dist/local-sync.d.cts +14 -26
  25. package/dist/local-sync.d.ts +14 -26
  26. package/dist/local-sync.js +68 -96
  27. package/dist/local-sync.js.map +1 -1
  28. package/dist/react.cjs +4 -5
  29. package/dist/react.cjs.map +1 -1
  30. package/dist/react.d.cts +6 -8
  31. package/dist/react.d.ts +6 -8
  32. package/dist/react.js +4 -5
  33. package/dist/react.js.map +1 -1
  34. package/dist/record-B2yRhYsc.js +28 -0
  35. package/dist/record-B2yRhYsc.js.map +1 -0
  36. package/dist/record-ChYUi8PP.cjs +45 -0
  37. package/dist/record-ChYUi8PP.cjs.map +1 -0
  38. package/dist/scheduler--137L3j-.js +500 -0
  39. package/dist/scheduler--137L3j-.js.map +1 -0
  40. package/dist/scheduler-D4VthaaK.cjs +589 -0
  41. package/dist/scheduler-D4VthaaK.cjs.map +1 -0
  42. package/package.json +6 -3
  43. package/skills/doxum-runtime/SKILL.md +32 -7
  44. package/skills/doxum-runtime/agents/openai.yaml +1 -1
  45. package/skills/doxum-runtime/references/api.en.md +249 -0
  46. package/skills/doxum-runtime/references/api.zh-CN.md +242 -0
  47. package/skills/doxum-runtime/references/guide.en.md +18 -8
  48. package/skills/doxum-runtime/references/guide.zh-CN.md +14 -6
  49. package/skills/doxum-runtime/references/invariants.en.md +29 -5
  50. package/skills/doxum-runtime/references/invariants.zh-CN.md +24 -6
  51. package/skills/doxum-runtime/references/patterns.en.md +78 -18
  52. package/skills/doxum-runtime/references/patterns.zh-CN.md +76 -18
  53. package/skills/doxum-runtime/references/projections.en.md +209 -119
  54. package/skills/doxum-runtime/references/projections.zh-CN.md +200 -101
  55. package/dist/advanced-CLJCWkHa.d.cts +0 -176
  56. package/dist/advanced-CRNHfJBu.js +0 -1224
  57. package/dist/advanced-CRNHfJBu.js.map +0 -1
  58. package/dist/advanced-Cle5g3B4.cjs +0 -1517
  59. package/dist/advanced-Cle5g3B4.cjs.map +0 -1
  60. package/dist/advanced-d6J61er9.d.ts +0 -176
  61. package/dist/context-BcLKGfq8.cjs +0 -149
  62. package/dist/context-BcLKGfq8.cjs.map +0 -1
  63. package/dist/context-DFEaDUv5.js +0 -84
  64. package/dist/context-DFEaDUv5.js.map +0 -1
  65. package/dist/driver-BD3DQOW6.cjs.map +0 -1
  66. package/dist/driver-yFfvDMX5.js.map +0 -1
@@ -1,43 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_context = require("./context-BcLKGfq8.cjs");
3
- const require_driver = require("./driver-BD3DQOW6.cjs");
4
- //#region core/src/local-sync/contract.ts
5
- var LocalSyncUnavailableError = class extends Error {
6
- constructor(capability) {
7
- super(`${capability} is required by doxum/local-sync in this environment.`);
8
- this.name = "LocalSyncUnavailableError";
9
- }
10
- };
11
- var LocalSyncSchemaError = class extends Error {
12
- constructor(documentId, expected, actual) {
13
- super(`Local document '${documentId}' uses schema version ${actual}, but version ${expected} was requested.`);
14
- this.name = "LocalSyncSchemaError";
15
- }
16
- };
17
- var LocalSyncConsistencyError = class extends Error {
18
- constructor(message) {
19
- super(message);
20
- this.name = "LocalSyncConsistencyError";
21
- }
22
- };
23
- var LocalSyncReadOnlyError = class extends Error {
24
- constructor() {
25
- super("This tab is following the local document and cannot write until it becomes the leader.");
26
- this.name = "LocalSyncReadOnlyError";
27
- }
28
- };
29
- var LocalSyncUnsupportedOperationError = class extends Error {
30
- constructor() {
31
- super("Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.");
32
- this.name = "LocalSyncUnsupportedOperationError";
33
- }
34
- };
35
- var LocalSyncDisposedError = class extends Error {
36
- constructor() {
37
- super("Local sync has been disposed.");
38
- this.name = "LocalSyncDisposedError";
2
+ const require_record = require("./record-ChYUi8PP.cjs");
3
+ const require_driver = require("./driver-BL91gn7_.cjs");
4
+ //#region core/src/local-sync/error.ts
5
+ var LocalSyncError = class extends Error {
6
+ code;
7
+ constructor(code, message, options) {
8
+ super(message, options);
9
+ this.name = "LocalSyncError";
10
+ this.code = code;
39
11
  }
40
12
  };
13
+ const normalizeLocalSyncError = (error, code, message) => error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });
41
14
  //#endregion
42
15
  //#region core/src/local-sync/json.ts
43
16
  const defaultJsonChangeLimits = Object.freeze({
@@ -46,12 +19,6 @@ const defaultJsonChangeLimits = Object.freeze({
46
19
  maxDepth: 64,
47
20
  maxStringLength: 256e3
48
21
  });
49
- var LocalSyncDataError = class extends Error {
50
- constructor(message) {
51
- super(message);
52
- this.name = "LocalSyncDataError";
53
- }
54
- };
55
22
  const describePath = (path) => path.length === 0 ? "value" : path.join(".");
56
23
  const limit = (value, fallback, label) => {
57
24
  if (value === void 0) return fallback;
@@ -65,18 +32,18 @@ const resolveLimits = (input) => ({
65
32
  maxStringLength: limit(input?.maxStringLength, defaultJsonChangeLimits.maxStringLength, "maxStringLength")
66
33
  });
67
34
  const validate = (value, path, limits, depth, ancestors) => {
68
- if (limits && depth > limits.maxDepth) throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum JSON depth.`);
35
+ if (limits && depth > limits.maxDepth) throw new LocalSyncError("invalid-data", `${describePath(path)} exceeds the maximum JSON depth.`);
69
36
  if (value === null || typeof value === "boolean") return value;
70
37
  if (typeof value === "string") {
71
38
  if (!limits || value.length <= limits.maxStringLength) return value;
72
- throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum string length.`);
39
+ throw new LocalSyncError("invalid-data", `${describePath(path)} exceeds the maximum string length.`);
73
40
  }
74
41
  if (typeof value === "number") {
75
42
  if (Number.isFinite(value)) return value;
76
- throw new LocalSyncDataError(`${describePath(path)} contains a non-finite number.`);
43
+ throw new LocalSyncError("invalid-data", `${describePath(path)} contains a non-finite number.`);
77
44
  }
78
45
  if (Array.isArray(value)) {
79
- if (ancestors.has(value)) throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);
46
+ if (ancestors.has(value)) throw new LocalSyncError("invalid-data", `${describePath(path)} contains a cycle.`);
80
47
  ancestors.add(value);
81
48
  try {
82
49
  for (let index = 0; index < value.length; index += 1) {
@@ -89,8 +56,8 @@ const validate = (value, path, limits, depth, ancestors) => {
89
56
  }
90
57
  return value;
91
58
  }
92
- if (require_context.isPlainObject(value)) {
93
- if (ancestors.has(value)) throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);
59
+ if (require_record.isPlainObject(value)) {
60
+ if (ancestors.has(value)) throw new LocalSyncError("invalid-data", `${describePath(path)} contains a cycle.`);
94
61
  ancestors.add(value);
95
62
  try {
96
63
  for (const key of Object.keys(value)) {
@@ -103,7 +70,7 @@ const validate = (value, path, limits, depth, ancestors) => {
103
70
  }
104
71
  return value;
105
72
  }
106
- throw new LocalSyncDataError(`${describePath(path)} must be JSON data.`);
73
+ throw new LocalSyncError("invalid-data", `${describePath(path)} must be JSON data.`);
107
74
  };
108
75
  const json = (value, label) => validate(value, [label], void 0, 0, /* @__PURE__ */ new WeakSet());
109
76
  const jsonChanges = (value, label, input) => {
@@ -112,13 +79,13 @@ const jsonChanges = (value, label, input) => {
112
79
  try {
113
80
  changes = require_driver.decodeChanges(value);
114
81
  } catch {
115
- throw new LocalSyncDataError(`${label} contains an invalid ChangeSet.`);
82
+ throw new LocalSyncError("invalid-data", `${label} contains an invalid ChangeSet.`);
116
83
  }
117
- if (limits && require_driver.changeCount(changes) > limits.maxChanges) throw new LocalSyncDataError(`${label} exceeds the maximum change count.`);
84
+ if (limits && require_driver.changeCount(changes) > limits.maxChanges) throw new LocalSyncError("invalid-data", `${label} exceeds the maximum change count.`);
118
85
  validate(changes.changes, [label], limits, 0, /* @__PURE__ */ new WeakSet());
119
86
  if (limits) {
120
87
  const serialized = JSON.stringify(changes.changes);
121
- if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes) throw new LocalSyncDataError(`${label} exceeds the maximum ChangeSet size.`);
88
+ if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes) throw new LocalSyncError("invalid-data", `${label} exceeds the maximum ChangeSet size.`);
122
89
  }
123
90
  return changes;
124
91
  };
@@ -130,39 +97,39 @@ const DOCUMENTS = "documents";
130
97
  const COMMITS = "commits";
131
98
  const request = (value) => new Promise((resolve, reject) => {
132
99
  value.onsuccess = () => resolve(value.result);
133
- value.onerror = () => reject(value.error ?? /* @__PURE__ */ new Error("IndexedDB request failed."));
100
+ value.onerror = () => reject(normalizeLocalSyncError(value.error, "unavailable", "IndexedDB request failed."));
134
101
  });
135
102
  const complete = (transaction) => new Promise((resolve, reject) => {
136
103
  transaction.oncomplete = () => resolve();
137
- transaction.onabort = () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction was aborted."));
138
- transaction.onerror = () => reject(transaction.error ?? /* @__PURE__ */ new Error("IndexedDB transaction failed."));
104
+ transaction.onabort = () => reject(normalizeLocalSyncError(transaction.error, "unavailable", "IndexedDB transaction was aborted."));
105
+ transaction.onerror = () => reject(normalizeLocalSyncError(transaction.error, "unavailable", "IndexedDB transaction failed."));
139
106
  });
140
107
  const factory = () => {
141
- if (!globalThis.indexedDB) throw new LocalSyncUnavailableError("IndexedDB");
108
+ if (!globalThis.indexedDB) throw new LocalSyncError("unavailable", "IndexedDB is required by doxum/local-sync in this environment.");
142
109
  return globalThis.indexedDB;
143
110
  };
144
111
  const keyRange = () => {
145
- if (!globalThis.IDBKeyRange) throw new LocalSyncUnavailableError("IndexedDB");
112
+ if (!globalThis.IDBKeyRange) throw new LocalSyncError("unavailable", "IndexedDB is required by doxum/local-sync in this environment.");
146
113
  return globalThis.IDBKeyRange;
147
114
  };
148
115
  const string = (value, label) => {
149
116
  if (typeof value === "string") return value;
150
- throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);
117
+ throw new LocalSyncError("consistency", `${label} is malformed in IndexedDB.`);
151
118
  };
152
119
  const nonNegativeInteger = (value, label) => {
153
120
  if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) return value;
154
- throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);
121
+ throw new LocalSyncError("consistency", `${label} is malformed in IndexedDB.`);
155
122
  };
156
123
  const positiveInteger$1 = (value, label) => {
157
124
  const parsed = nonNegativeInteger(value, label);
158
125
  if (parsed > 0) return parsed;
159
- throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);
126
+ throw new LocalSyncError("consistency", `${label} is malformed in IndexedDB.`);
160
127
  };
161
128
  const documentRecord = (value) => {
162
- if (!require_context.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncConsistencyError("Document record is malformed in IndexedDB.");
129
+ if (!require_record.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncError("consistency", "Document record is malformed in IndexedDB.");
163
130
  const checkpointSeq = nonNegativeInteger(value.checkpointSeq, "document.checkpointSeq");
164
131
  const headSeq = nonNegativeInteger(value.headSeq, "document.headSeq");
165
- if (checkpointSeq > headSeq) throw new LocalSyncConsistencyError("Document checkpoint exceeds its head sequence.");
132
+ if (checkpointSeq > headSeq) throw new LocalSyncError("consistency", "Document checkpoint exceeds its head sequence.");
166
133
  return Object.freeze({
167
134
  formatVersion: FORMAT_VERSION,
168
135
  documentId: string(value.documentId, "document.documentId"),
@@ -173,7 +140,7 @@ const documentRecord = (value) => {
173
140
  });
174
141
  };
175
142
  const commitRecord = (value) => {
176
- if (!require_context.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncConsistencyError("Commit record is malformed in IndexedDB.");
143
+ if (!require_record.isRecord(value) || value.formatVersion !== FORMAT_VERSION) throw new LocalSyncError("consistency", "Commit record is malformed in IndexedDB.");
177
144
  return Object.freeze({
178
145
  formatVersion: FORMAT_VERSION,
179
146
  documentId: string(value.documentId, "commit.documentId"),
@@ -186,7 +153,7 @@ const openIndexedDbTimeline = async (databaseName) => {
186
153
  const database = await new Promise((resolve, reject) => {
187
154
  let incompatible = false;
188
155
  const open = factory().open(databaseName, DATABASE_VERSION);
189
- open.onerror = () => reject(incompatible ? new LocalSyncConsistencyError("Unsupported Doxum storage format. Existing data was left unchanged.") : open.error ?? /* @__PURE__ */ new Error("Unable to open IndexedDB."));
156
+ open.onerror = () => reject(incompatible ? new LocalSyncError("consistency", "Unsupported Doxum storage format. Existing data was left unchanged.") : open.error ?? /* @__PURE__ */ new Error("Unable to open IndexedDB."));
190
157
  open.onblocked = () => reject(/* @__PURE__ */ new Error(`IndexedDB database '${databaseName}' is blocked.`));
191
158
  open.onupgradeneeded = (event) => {
192
159
  if (event.oldVersion !== 0) {
@@ -202,7 +169,7 @@ const openIndexedDbTimeline = async (databaseName) => {
202
169
  });
203
170
  const readDocument = async (transaction, documentId) => {
204
171
  const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));
205
- if (value === void 0) throw new LocalSyncConsistencyError(`Local document '${documentId}' does not exist.`);
172
+ if (value === void 0) throw new LocalSyncError("consistency", `Local document '${documentId}' does not exist.`);
206
173
  return documentRecord(value);
207
174
  };
208
175
  return {
@@ -212,7 +179,7 @@ const openIndexedDbTimeline = async (databaseName) => {
212
179
  const existing = await request(store.get(documentId));
213
180
  if (existing !== void 0) {
214
181
  const record = documentRecord(existing);
215
- if (record.schemaVersion !== schemaVersion) throw new LocalSyncSchemaError(documentId, schemaVersion, record.schemaVersion);
182
+ if (record.schemaVersion !== schemaVersion) throw new LocalSyncError("schema-mismatch", `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`);
216
183
  await complete(transaction);
217
184
  return record;
218
185
  }
@@ -245,7 +212,7 @@ const openIndexedDbTimeline = async (databaseName) => {
245
212
  const transaction = database.transaction([DOCUMENTS, COMMITS], "readwrite");
246
213
  const documents = transaction.objectStore(DOCUMENTS);
247
214
  const current = await readDocument(transaction, input.documentId);
248
- if (current.headSeq !== input.expectedHeadSeq) throw new LocalSyncConsistencyError("Local document advanced before the leader could append its commit.");
215
+ if (current.headSeq !== input.expectedHeadSeq) throw new LocalSyncError("consistency", "Local document advanced before the leader could append its commit.");
249
216
  const commit = {
250
217
  formatVersion: FORMAT_VERSION,
251
218
  documentId: input.documentId,
@@ -271,12 +238,12 @@ const openIndexedDbTimeline = async (databaseName) => {
271
238
  //#region core/src/local-sync/session.ts
272
239
  const locks = () => {
273
240
  const value = globalThis.navigator?.locks;
274
- if (typeof value !== "object" || value === null || !("request" in value) || typeof value.request !== "function") throw new LocalSyncUnavailableError("Web Locks");
241
+ if (typeof value !== "object" || value === null || !("request" in value) || typeof value.request !== "function") throw new LocalSyncError("unavailable", "Web Locks is required by doxum/local-sync in this environment.");
275
242
  return value;
276
243
  };
277
244
  const channelConstructor = () => {
278
245
  const value = globalThis.BroadcastChannel;
279
- if (typeof value !== "function") throw new LocalSyncUnavailableError("BroadcastChannel");
246
+ if (typeof value !== "function") throw new LocalSyncError("unavailable", "BroadcastChannel is required by doxum/local-sync in this environment.");
280
247
  return value;
281
248
  };
282
249
  const requiredString = (value, label) => {
@@ -300,7 +267,9 @@ const attachLocalSync = async (input) => {
300
267
  const schemaVersion = positiveInteger(input.schemaVersion ?? 1, "schemaVersion");
301
268
  const lockManager = locks();
302
269
  const BroadcastChannel = channelConstructor();
303
- const timeline = await openIndexedDbTimeline(databaseName);
270
+ const timeline = await openIndexedDbTimeline(databaseName).catch((error) => {
271
+ throw normalizeLocalSyncError(error, "unavailable", "Unable to open local sync storage.");
272
+ });
304
273
  let channel;
305
274
  let unsubscribe;
306
275
  let driver;
@@ -340,51 +309,55 @@ const attachLocalSync = async (input) => {
340
309
  if (!stateListeners.has(listener)) continue;
341
310
  try {
342
311
  listener();
343
- } catch (error) {
344
- try {
345
- input.onError?.(error);
346
- } catch {}
347
- }
312
+ } catch {}
348
313
  }
349
314
  };
350
315
  const fail = (error) => {
351
- if (fault || disposed) return;
352
- fault = error;
316
+ const failure = normalizeLocalSyncError(error, "unavailable", "Local synchronization failed.");
317
+ if (fault || disposed) return fault ?? failure;
318
+ fault = failure;
353
319
  publishState();
354
320
  try {
355
- input.onError?.(error);
321
+ input.onError?.(failure);
356
322
  } catch {}
323
+ return failure;
357
324
  };
358
325
  const assertOpen = () => {
359
- if (closing || disposed) throw new LocalSyncDisposedError();
326
+ if (closing || disposed) throw new LocalSyncError("disposed", "Local sync has been disposed.");
360
327
  if (fault) throw fault;
361
328
  };
362
329
  const assertLeader = (intent) => {
363
330
  assertOpen();
364
- if (role !== "leader") throw new LocalSyncReadOnlyError();
365
- if (intent.kind === "replace" || intent.kind === "apply" && intent.source === "remote") throw new LocalSyncUnsupportedOperationError();
331
+ if (role !== "leader") throw new LocalSyncError("read-only", "This tab is following the local document and cannot write until it becomes the leader.");
332
+ if (intent.kind === "replace" || intent.kind === "apply" && intent.source === "remote") throw new LocalSyncError("unsupported-operation", "Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.");
366
333
  };
367
334
  const runRuntime = (run) => driver?.run(run) ?? run();
335
+ const replayRuntime = (run, message) => {
336
+ try {
337
+ return runRuntime(run);
338
+ } catch (error) {
339
+ throw normalizeLocalSyncError(error, "consistency", message);
340
+ }
341
+ };
368
342
  const applyStored = (commit) => {
369
- if (commit.seq !== headSeq + 1) throw new LocalSyncConsistencyError("Local commit log contains a sequence gap.");
370
- if (runRuntime(() => runtime.apply(commit.changes, {
343
+ if (commit.seq !== headSeq + 1) throw new LocalSyncError("consistency", "Local commit log contains a sequence gap.");
344
+ if (replayRuntime(() => runtime.apply(commit.changes, {
371
345
  expectedRevision: runtime.revision(),
372
- source: "remote",
373
- history: false
374
- })).status !== "committed") throw new LocalSyncConsistencyError("A stored local ChangeSet could not be applied.");
346
+ source: "remote"
347
+ }), "A stored local ChangeSet could not be replayed.").status !== "committed") throw new LocalSyncError("consistency", "A stored local ChangeSet could not be applied.");
375
348
  headSeq = commit.seq;
376
349
  };
377
350
  const restore = async (reset = false) => {
378
351
  const current = await timeline.read(documentId);
379
- if (current.schemaVersion !== schemaVersion) throw new LocalSyncConsistencyError("Local document schema changed while this attachment was open.");
380
- if (headSeq > current.headSeq) throw new LocalSyncConsistencyError("The local timeline moved behind this attachment.");
352
+ if (current.schemaVersion !== schemaVersion) throw new LocalSyncError("consistency", "Local document schema changed while this attachment was open.");
353
+ if (headSeq > current.headSeq) throw new LocalSyncError("consistency", "The local timeline moved behind this attachment.");
381
354
  if (reset || headSeq < current.checkpointSeq) {
382
- if (runRuntime(() => runtime.replace(current.checkpoint, { source: "remote" })).status === "rejected") throw new LocalSyncConsistencyError("The local checkpoint could not be restored.");
355
+ if (replayRuntime(() => runtime.replace(current.checkpoint, { source: "remote" }), "The local checkpoint could not be restored.").status === "rejected") throw new LocalSyncError("consistency", "The local checkpoint could not be restored.");
383
356
  headSeq = current.checkpointSeq;
384
357
  }
385
358
  const tail = await timeline.tail(documentId, headSeq);
386
359
  for (const commit of tail) applyStored(commit);
387
- if (headSeq !== current.headSeq) throw new LocalSyncConsistencyError("Local commit log does not reach its recorded head sequence.");
360
+ if (headSeq !== current.headSeq) throw new LocalSyncError("consistency", "Local commit log does not reach its recorded head sequence.");
388
361
  checkpointSeq = current.checkpointSeq;
389
362
  publishState();
390
363
  };
@@ -401,7 +374,7 @@ const attachLocalSync = async (input) => {
401
374
  expectedHeadSeq: headSeq,
402
375
  changes
403
376
  });
404
- if (storedCommit.seq !== headSeq + 1) throw new LocalSyncConsistencyError("A local commit was assigned an unexpected sequence.");
377
+ if (storedCommit.seq !== headSeq + 1) throw new LocalSyncError("consistency", "A local commit was assigned an unexpected sequence.");
405
378
  headSeq = storedCommit.seq;
406
379
  publishState();
407
380
  channel?.postMessage({
@@ -493,7 +466,7 @@ const attachLocalSync = async (input) => {
493
466
  current: () => snapshot,
494
467
  revision: () => stateRevision,
495
468
  subscribe: (listener) => {
496
- if (disposed) throw new LocalSyncDisposedError();
469
+ if (disposed) throw new LocalSyncError("disposed", "Local sync has been disposed.");
497
470
  stateListeners.add(listener);
498
471
  return () => {
499
472
  stateListeners.delete(listener);
@@ -505,8 +478,7 @@ const attachLocalSync = async (input) => {
505
478
  try {
506
479
  await enqueue(restore);
507
480
  } catch (error) {
508
- fail(error);
509
- throw error;
481
+ throw fail(error);
510
482
  }
511
483
  assertOpen();
512
484
  },
@@ -535,17 +507,11 @@ const attachLocalSync = async (input) => {
535
507
  driver?.dispose();
536
508
  channel?.close();
537
509
  timeline.close();
538
- throw error;
510
+ throw normalizeLocalSyncError(error, "unavailable", "Unable to attach local sync.");
539
511
  }
540
512
  };
541
513
  //#endregion
542
- exports.LocalSyncConsistencyError = LocalSyncConsistencyError;
543
- exports.LocalSyncDataError = LocalSyncDataError;
544
- exports.LocalSyncDisposedError = LocalSyncDisposedError;
545
- exports.LocalSyncReadOnlyError = LocalSyncReadOnlyError;
546
- exports.LocalSyncSchemaError = LocalSyncSchemaError;
547
- exports.LocalSyncUnavailableError = LocalSyncUnavailableError;
548
- exports.LocalSyncUnsupportedOperationError = LocalSyncUnsupportedOperationError;
514
+ exports.LocalSyncError = LocalSyncError;
549
515
  exports.attachLocalSync = attachLocalSync;
550
516
  exports.defaultJsonChangeLimits = defaultJsonChangeLimits;
551
517
 
@@ -1 +1 @@
1
- {"version":3,"file":"local-sync.cjs","names":["isPlainObject","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/contract.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["import type { ObjectNode } from '../schema';\nimport type { DocumentRuntime } from '../runtime/contract';\nimport type { JsonChangeLimits } from './json';\nimport type { Readable } from '../readable';\n\nexport class LocalSyncUnavailableError extends Error {\n constructor(capability: 'IndexedDB' | 'Web Locks' | 'BroadcastChannel') {\n super(`${capability} is required by doxum/local-sync in this environment.`);\n this.name = 'LocalSyncUnavailableError';\n }\n}\n\nexport class LocalSyncSchemaError extends Error {\n constructor(documentId: string, expected: number, actual: number) {\n super(\n `Local document '${documentId}' uses schema version ${actual}, but version ${expected} was requested.`\n );\n this.name = 'LocalSyncSchemaError';\n }\n}\n\nexport class LocalSyncConsistencyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncConsistencyError';\n }\n}\n\nexport class LocalSyncReadOnlyError extends Error {\n constructor() {\n super('This tab is following the local document and cannot write until it becomes the leader.');\n this.name = 'LocalSyncReadOnlyError';\n }\n}\n\nexport class LocalSyncUnsupportedOperationError extends Error {\n constructor() {\n super(\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n this.name = 'LocalSyncUnsupportedOperationError';\n }\n}\n\nexport class LocalSyncDisposedError extends Error {\n constructor() {\n super('Local sync has been disposed.');\n this.name = 'LocalSyncDisposedError';\n }\n}\n\nexport type LocalSyncState =\n | {\n readonly status: 'leader' | 'follower';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n }\n | {\n readonly status: 'error';\n readonly headSeq: number;\n readonly checkpointSeq: number;\n readonly error: unknown;\n }\n | { readonly status: 'disposed' };\n\nexport type LocalSync = {\n readonly state: Readable<LocalSyncState>;\n flush(): Promise<void>;\n dispose(): Promise<void>;\n};\n\nexport type AttachLocalSyncOptions<TSchema extends ObjectNode> = {\n readonly runtime: DocumentRuntime<TSchema>;\n readonly database: string;\n readonly documentId: string;\n readonly schemaVersion?: number;\n /** Admission limits for new local commits; durable replay does not reapply them. */\n readonly changeLimits?: JsonChangeLimits;\n readonly onError?: (error: unknown) => void;\n};\n","import { isPlainObject } from '../value/record';\nimport * as changeSet from '../mutation/changes';\nimport type { ChangeSet } from '../changes';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nexport class LocalSyncDataError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LocalSyncDataError';\n }\n}\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum JSON depth.`);\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncDataError(`${describePath(path)} exceeds the maximum string length.`);\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncDataError(`${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncDataError(`${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncDataError(`${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncDataError(`${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncDataError(`${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncDataError(`${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import {\n LocalSyncConsistencyError,\n LocalSyncSchemaError,\n LocalSyncUnavailableError,\n} from './contract';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '../changes';\nimport { isRecord } from '../value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () => reject(value.error ?? new Error('IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction was aborted.'));\n transaction.onerror = () =>\n reject(transaction.error ?? new Error('IndexedDB transaction failed.'));\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange) throw new LocalSyncUnavailableError('IndexedDB');\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncConsistencyError(`${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncConsistencyError('Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncConsistencyError('Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncConsistencyError('Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncConsistencyError(\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncConsistencyError(`Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncSchemaError(documentId, schemaVersion, record.schemaVersion);\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncConsistencyError(\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '../runtime/driver';\nimport type { DocumentCommit } from '../runtime/contract';\nimport type { ObjectNode, Infer } from '../schema';\nimport {\n LocalSyncConsistencyError,\n LocalSyncDisposedError,\n LocalSyncReadOnlyError,\n LocalSyncUnsupportedOperationError,\n LocalSyncUnavailableError,\n type AttachLocalSyncOptions,\n type LocalSync,\n type LocalSyncState,\n} from './contract';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '../changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncUnavailableError('Web Locks');\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function') throw new LocalSyncUnavailableError('BroadcastChannel');\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectNode>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName);\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: unknown;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch (error) {\n try {\n input.onError?.(error);\n } catch {\n /* A listener cannot fail persistence. */\n }\n }\n }\n };\n\n const fail = (error: unknown): void => {\n if (fault || disposed) return;\n fault = error;\n publishState();\n try {\n input.onError?.(error);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n };\n\n const assertOpen = (): void => {\n if (closing || disposed) throw new LocalSyncDisposedError();\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader') throw new LocalSyncReadOnlyError();\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncUnsupportedOperationError();\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncConsistencyError('Local commit log contains a sequence gap.');\n const result = runRuntime(() =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n history: false,\n })\n );\n if (result.status !== 'committed')\n throw new LocalSyncConsistencyError('A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncConsistencyError(\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncConsistencyError('The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = runRuntime(() =>\n runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' })\n );\n if (result.status === 'rejected')\n throw new LocalSyncConsistencyError('The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncConsistencyError(\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncConsistencyError('A local commit was assigned an unexpected sequence.');\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncDisposedError();\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n fail(error);\n throw error;\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw error;\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"local-sync.cjs","names":["isPlainObject","positiveInteger","isRecord","installRuntimeWriteDriver"],"sources":["../core/src/local-sync/error.ts","../core/src/local-sync/json.ts","../core/src/local-sync/timeline.ts","../core/src/local-sync/session.ts"],"sourcesContent":["export type LocalSyncErrorCode =\n | 'unavailable'\n | 'schema-mismatch'\n | 'consistency'\n | 'read-only'\n | 'unsupported-operation'\n | 'disposed'\n | 'invalid-data';\n\nexport class LocalSyncError extends Error {\n readonly code: LocalSyncErrorCode;\n\n constructor(code: LocalSyncErrorCode, message: string, options?: { readonly cause?: unknown }) {\n super(message, options);\n this.name = 'LocalSyncError';\n this.code = code;\n }\n}\n\nexport const normalizeLocalSyncError = (\n error: unknown,\n code: LocalSyncErrorCode,\n message: string\n): LocalSyncError =>\n error instanceof LocalSyncError ? error : new LocalSyncError(code, message, { cause: error });\n","import { isPlainObject } from '../value/record';\nimport * as changeSet from '../mutation/changes';\nimport type { ChangeSet } from '../changes';\nimport { LocalSyncError } from './error';\n\nexport type JsonPrimitive = null | boolean | number | string;\nexport type JsonValue =\n JsonPrimitive | readonly JsonValue[] | { readonly [key: string]: JsonValue };\n\nexport type JsonChangeLimits = {\n readonly maxChanges?: number;\n readonly maxBytes?: number;\n readonly maxDepth?: number;\n readonly maxStringLength?: number;\n};\n\ntype ResolvedJsonLimits = {\n readonly maxChanges: number;\n readonly maxBytes: number;\n readonly maxDepth: number;\n readonly maxStringLength: number;\n};\n\nexport const defaultJsonChangeLimits: Readonly<ResolvedJsonLimits> = Object.freeze({\n maxChanges: 1_000,\n maxBytes: 1_000_000,\n maxDepth: 64,\n maxStringLength: 256_000,\n});\n\nconst describePath = (path: readonly string[]): string =>\n path.length === 0 ? 'value' : path.join('.');\n\nconst limit = (value: number | undefined, fallback: number, label: string): number => {\n if (value === undefined) return fallback;\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst resolveLimits = (input: JsonChangeLimits | undefined): ResolvedJsonLimits => ({\n maxChanges: limit(input?.maxChanges, defaultJsonChangeLimits.maxChanges, 'maxChanges'),\n maxBytes: limit(input?.maxBytes, defaultJsonChangeLimits.maxBytes, 'maxBytes'),\n maxDepth: limit(input?.maxDepth, defaultJsonChangeLimits.maxDepth, 'maxDepth'),\n maxStringLength: limit(\n input?.maxStringLength,\n defaultJsonChangeLimits.maxStringLength,\n 'maxStringLength'\n ),\n});\n\nconst validate = (\n value: unknown,\n path: string[],\n limits: ResolvedJsonLimits | undefined,\n depth: number,\n ancestors: WeakSet<object>\n): JsonValue => {\n if (limits && depth > limits.maxDepth)\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum JSON depth.`\n );\n if (value === null || typeof value === 'boolean') return value;\n if (typeof value === 'string') {\n if (!limits || value.length <= limits.maxStringLength) return value;\n throw new LocalSyncError(\n 'invalid-data',\n `${describePath(path)} exceeds the maximum string length.`\n );\n }\n if (typeof value === 'number') {\n if (Number.isFinite(value)) return value;\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a non-finite number.`);\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (let index = 0; index < value.length; index += 1) {\n path.push(String(index));\n validate(value[index], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as readonly JsonValue[];\n }\n if (isPlainObject(value)) {\n if (ancestors.has(value))\n throw new LocalSyncError('invalid-data', `${describePath(path)} contains a cycle.`);\n ancestors.add(value);\n try {\n for (const key of Object.keys(value)) {\n path.push(key);\n validate(value[key], path, limits, depth + 1, ancestors);\n path.pop();\n }\n } finally {\n ancestors.delete(value);\n }\n return value as { readonly [key: string]: JsonValue };\n }\n throw new LocalSyncError('invalid-data', `${describePath(path)} must be JSON data.`);\n};\n\nexport const json = (value: unknown, label: string): JsonValue =>\n validate(value, [label], undefined, 0, new WeakSet());\n\nexport const jsonChanges = (value: unknown, label: string, input?: JsonChangeLimits): ChangeSet => {\n const limits = input === undefined ? undefined : resolveLimits(input);\n let changes: ChangeSet;\n try {\n changes = changeSet.decodeChanges(value);\n } catch {\n throw new LocalSyncError('invalid-data', `${label} contains an invalid ChangeSet.`);\n }\n if (limits && changeSet.changeCount(changes) > limits.maxChanges)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum change count.`);\n validate(changes.changes, [label], limits, 0, new WeakSet());\n if (limits) {\n const serialized = JSON.stringify(changes.changes);\n if (new TextEncoder().encode(serialized).byteLength > limits.maxBytes)\n throw new LocalSyncError('invalid-data', `${label} exceeds the maximum ChangeSet size.`);\n }\n return changes;\n};\n","import { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges, type JsonValue } from './json';\nimport type { ChangeSet } from '../changes';\nimport { isRecord } from '../value/record';\n\nconst DATABASE_VERSION = 5;\nconst FORMAT_VERSION = 3;\nconst DOCUMENTS = 'documents';\nconst COMMITS = 'commits';\n\nexport type StoredDocument = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly schemaVersion: number;\n readonly checkpointSeq: number;\n readonly headSeq: number;\n readonly checkpoint: JsonValue;\n};\n\nexport type StoredCommit = {\n readonly formatVersion: number;\n readonly documentId: string;\n readonly seq: number;\n readonly changes: ChangeSet;\n readonly createdAt: number;\n};\n\nconst request = <T>(value: IDBRequest<T>): Promise<T> =>\n new Promise((resolve, reject) => {\n value.onsuccess = () => resolve(value.result);\n value.onerror = () =>\n reject(normalizeLocalSyncError(value.error, 'unavailable', 'IndexedDB request failed.'));\n });\n\nconst complete = (transaction: IDBTransaction): Promise<void> =>\n new Promise((resolve, reject) => {\n transaction.oncomplete = () => resolve();\n transaction.onabort = () =>\n reject(\n normalizeLocalSyncError(\n transaction.error,\n 'unavailable',\n 'IndexedDB transaction was aborted.'\n )\n );\n transaction.onerror = () =>\n reject(\n normalizeLocalSyncError(transaction.error, 'unavailable', 'IndexedDB transaction failed.')\n );\n });\n\nconst factory = (): IDBFactory => {\n if (!globalThis.indexedDB)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.indexedDB;\n};\n\nconst keyRange = (): typeof IDBKeyRange => {\n if (!globalThis.IDBKeyRange)\n throw new LocalSyncError(\n 'unavailable',\n 'IndexedDB is required by doxum/local-sync in this environment.'\n );\n return globalThis.IDBKeyRange;\n};\n\nconst string = (value: unknown, label: string): string => {\n if (typeof value === 'string') return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst nonNegativeInteger = (value: unknown, label: string): number => {\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst positiveInteger = (value: unknown, label: string): number => {\n const parsed = nonNegativeInteger(value, label);\n if (parsed > 0) return parsed;\n throw new LocalSyncError('consistency', `${label} is malformed in IndexedDB.`);\n};\n\nconst documentRecord = (value: unknown): StoredDocument => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Document record is malformed in IndexedDB.');\n const checkpointSeq = nonNegativeInteger(value.checkpointSeq, 'document.checkpointSeq');\n const headSeq = nonNegativeInteger(value.headSeq, 'document.headSeq');\n if (checkpointSeq > headSeq)\n throw new LocalSyncError('consistency', 'Document checkpoint exceeds its head sequence.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'document.documentId'),\n schemaVersion: positiveInteger(value.schemaVersion, 'document.schemaVersion'),\n checkpointSeq,\n headSeq,\n checkpoint: json(value.checkpoint, 'document.checkpoint'),\n });\n};\n\nconst commitRecord = (value: unknown): StoredCommit => {\n if (!isRecord(value) || value.formatVersion !== FORMAT_VERSION)\n throw new LocalSyncError('consistency', 'Commit record is malformed in IndexedDB.');\n return Object.freeze({\n formatVersion: FORMAT_VERSION,\n documentId: string(value.documentId, 'commit.documentId'),\n seq: positiveInteger(value.seq, 'commit.seq'),\n changes: jsonChanges({ changes: value.changes }, 'commit.changes'),\n createdAt: nonNegativeInteger(value.createdAt, 'commit.createdAt'),\n });\n};\n\nexport type IndexedDbTimeline = {\n readonly initialize: (\n documentId: string,\n schemaVersion: number,\n checkpoint: JsonValue\n ) => Promise<StoredDocument>;\n readonly read: (documentId: string) => Promise<StoredDocument>;\n readonly tail: (documentId: string, afterSeq: number) => Promise<readonly StoredCommit[]>;\n readonly append: (input: {\n readonly documentId: string;\n readonly expectedHeadSeq: number;\n readonly changes: ChangeSet;\n }) => Promise<StoredCommit>;\n readonly close: () => void;\n};\n\nexport const openIndexedDbTimeline = async (databaseName: string): Promise<IndexedDbTimeline> => {\n const database = await new Promise<IDBDatabase>((resolve, reject) => {\n let incompatible = false;\n const open = factory().open(databaseName, DATABASE_VERSION);\n open.onerror = () =>\n reject(\n incompatible\n ? new LocalSyncError(\n 'consistency',\n 'Unsupported Doxum storage format. Existing data was left unchanged.'\n )\n : (open.error ?? new Error('Unable to open IndexedDB.'))\n );\n open.onblocked = () => reject(new Error(`IndexedDB database '${databaseName}' is blocked.`));\n open.onupgradeneeded = event => {\n if (event.oldVersion !== 0) {\n incompatible = true;\n open.transaction?.abort();\n return;\n }\n const db = open.result;\n if (!db.objectStoreNames.contains(DOCUMENTS))\n db.createObjectStore(DOCUMENTS, { keyPath: 'documentId' });\n if (!db.objectStoreNames.contains(COMMITS))\n db.createObjectStore(COMMITS, { keyPath: ['documentId', 'seq'] });\n };\n open.onsuccess = () => resolve(open.result);\n });\n\n const readDocument = async (\n transaction: IDBTransaction,\n documentId: string\n ): Promise<StoredDocument> => {\n const value = await request(transaction.objectStore(DOCUMENTS).get(documentId));\n if (value === undefined)\n throw new LocalSyncError('consistency', `Local document '${documentId}' does not exist.`);\n return documentRecord(value);\n };\n\n return {\n initialize: async (documentId, schemaVersion, checkpoint) => {\n const transaction = database.transaction(DOCUMENTS, 'readwrite');\n const store = transaction.objectStore(DOCUMENTS);\n const existing = await request(store.get(documentId));\n if (existing !== undefined) {\n const record = documentRecord(existing);\n if (record.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'schema-mismatch',\n `Local document '${documentId}' uses schema version ${record.schemaVersion}, but version ${schemaVersion} was requested.`\n );\n await complete(transaction);\n return record;\n }\n const record: StoredDocument = {\n formatVersion: FORMAT_VERSION,\n documentId,\n schemaVersion,\n checkpointSeq: 0,\n headSeq: 0,\n checkpoint,\n };\n store.put(record);\n await complete(transaction);\n return Object.freeze(record);\n },\n read: async documentId => {\n const transaction = database.transaction(DOCUMENTS, 'readonly');\n const record = await readDocument(transaction, documentId);\n await complete(transaction);\n return record;\n },\n tail: async (documentId, afterSeq) => {\n const transaction = database.transaction(COMMITS, 'readonly');\n const range = keyRange().bound(\n [documentId, afterSeq + 1],\n [documentId, Number.MAX_SAFE_INTEGER]\n );\n const values = await request(transaction.objectStore(COMMITS).getAll(range));\n await complete(transaction);\n return Object.freeze(values.map(commitRecord).sort((left, right) => left.seq - right.seq));\n },\n append: async input => {\n const transaction = database.transaction([DOCUMENTS, COMMITS], 'readwrite');\n const documents = transaction.objectStore(DOCUMENTS);\n const current = await readDocument(transaction, input.documentId);\n if (current.headSeq !== input.expectedHeadSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local document advanced before the leader could append its commit.'\n );\n const commit: StoredCommit = {\n formatVersion: FORMAT_VERSION,\n documentId: input.documentId,\n seq: current.headSeq + 1,\n changes: input.changes,\n createdAt: Date.now(),\n };\n documents.put({ ...current, headSeq: commit.seq });\n transaction.objectStore(COMMITS).put({ ...commit, changes: commit.changes.changes });\n await complete(transaction);\n return Object.freeze(commit);\n },\n close: () => database.close(),\n };\n};\n","import {\n installRuntimeWriteDriver,\n type RuntimeWriteDriverLease,\n type RuntimeWriteIntent,\n} from '../runtime/driver';\nimport type { DocumentCommit } from '../runtime/contract';\nimport type { Infer, ObjectSchema } from '../schema/model';\nimport { type AttachLocalSyncOptions, type LocalSync, type LocalSyncState } from './contract';\nimport { LocalSyncError, normalizeLocalSyncError } from './error';\nimport { json, jsonChanges } from './json';\nimport type { ChangeSet } from '../changes';\nimport { openIndexedDbTimeline, type StoredCommit } from './timeline';\n\ntype LockOptions = {\n readonly mode: 'exclusive';\n readonly ifAvailable?: boolean;\n};\n\ntype LockManager = {\n readonly request: <TResult>(\n name: string,\n options: LockOptions,\n callback: (lock: unknown | null) => TResult | PromiseLike<TResult>\n ) => Promise<TResult>;\n};\n\ntype Channel = {\n onmessage: ((event: MessageEvent<unknown>) => void) | null;\n postMessage(message: unknown): void;\n close(): void;\n};\n\ntype ChannelConstructor = new (name: string) => Channel;\n\ntype CommitNotification = {\n readonly kind: 'commit';\n readonly documentId: string;\n readonly headSeq: number;\n};\n\nconst locks = (): LockManager => {\n const value: unknown = globalThis.navigator?.locks;\n if (\n typeof value !== 'object' ||\n value === null ||\n !('request' in value) ||\n typeof value.request !== 'function'\n )\n throw new LocalSyncError(\n 'unavailable',\n 'Web Locks is required by doxum/local-sync in this environment.'\n );\n return value as LockManager;\n};\n\nconst channelConstructor = (): ChannelConstructor => {\n const value: unknown = globalThis.BroadcastChannel;\n if (typeof value !== 'function')\n throw new LocalSyncError(\n 'unavailable',\n 'BroadcastChannel is required by doxum/local-sync in this environment.'\n );\n return value as ChannelConstructor;\n};\n\nconst requiredString = (value: string, label: string): string => {\n if (value.length > 0) return value;\n throw new TypeError(`${label} must not be empty.`);\n};\n\nconst positiveInteger = (value: number, label: string): number => {\n if (Number.isSafeInteger(value) && value > 0) return value;\n throw new TypeError(`${label} must be a positive safe integer.`);\n};\n\nconst notification = (value: unknown): CommitNotification | undefined => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n const record = value as Record<string, unknown>;\n if (\n record.kind !== 'commit' ||\n typeof record.documentId !== 'string' ||\n typeof record.headSeq !== 'number' ||\n !Number.isSafeInteger(record.headSeq) ||\n record.headSeq < 0\n )\n return undefined;\n return record as CommitNotification;\n};\n\nexport const attachLocalSync = async <TSchema extends ObjectSchema<object>>(\n input: AttachLocalSyncOptions<TSchema>\n): Promise<LocalSync> => {\n const runtime = input.runtime;\n const databaseName = requiredString(input.database, 'database');\n const documentId = requiredString(input.documentId, 'documentId');\n const schemaVersion = positiveInteger(input.schemaVersion ?? 1, 'schemaVersion');\n const lockManager = locks();\n const BroadcastChannel = channelConstructor();\n const timeline = await openIndexedDbTimeline(databaseName).catch(error => {\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to open local sync storage.');\n });\n\n let channel: Channel | undefined;\n let unsubscribe: (() => void) | undefined;\n let driver: RuntimeWriteDriverLease | undefined;\n try {\n const stored = await timeline.initialize(\n documentId,\n schemaVersion,\n json(runtime.snapshot(), 'runtime snapshot')\n );\n let headSeq = stored.checkpointSeq;\n let checkpointSeq = stored.checkpointSeq;\n let role: 'leader' | 'follower' = 'follower';\n let fault: LocalSyncError | undefined;\n let closing = false;\n let disposed = false;\n let releaseLeadership: (() => void) | undefined;\n let leadershipLease: Promise<void> | undefined;\n let queued: Promise<void> = Promise.resolve();\n let snapshot: LocalSyncState = Object.freeze({ status: role, headSeq, checkpointSeq });\n let stateRevision = 0;\n const stateListeners = new Set<() => void>();\n const publishState = (): void => {\n const next: LocalSyncState = disposed\n ? { status: 'disposed' }\n : fault\n ? { status: 'error', headSeq, checkpointSeq, error: fault }\n : { status: role, headSeq, checkpointSeq };\n if (\n snapshot.status === next.status &&\n (snapshot.status === 'disposed' ||\n (next.status !== 'disposed' &&\n snapshot.headSeq === next.headSeq &&\n snapshot.checkpointSeq === next.checkpointSeq &&\n (snapshot.status !== 'error' ||\n (next.status === 'error' && snapshot.error === next.error))))\n )\n return;\n snapshot = Object.freeze(next);\n stateRevision++;\n for (const listener of [...stateListeners]) {\n if (!stateListeners.has(listener)) continue;\n try {\n listener();\n } catch {\n // Consumer state listeners are isolated from synchronization state.\n }\n }\n };\n\n const fail = (error: unknown): LocalSyncError => {\n const failure = normalizeLocalSyncError(\n error,\n 'unavailable',\n 'Local synchronization failed.'\n );\n if (fault || disposed) return fault ?? failure;\n fault = failure;\n publishState();\n try {\n input.onError?.(failure);\n } catch {\n // Error reporting cannot repair or replace the original synchronization failure.\n }\n return failure;\n };\n\n const assertOpen = (): void => {\n if (closing || disposed)\n throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n if (fault) throw fault;\n };\n\n const assertLeader = (intent: RuntimeWriteIntent): void => {\n assertOpen();\n if (role !== 'leader')\n throw new LocalSyncError(\n 'read-only',\n 'This tab is following the local document and cannot write until it becomes the leader.'\n );\n if (intent.kind === 'replace' || (intent.kind === 'apply' && intent.source === 'remote'))\n throw new LocalSyncError(\n 'unsupported-operation',\n 'Local sync appends committed changes. runtime.replace() and externally supplied remote changes are unavailable while it is attached.'\n );\n };\n\n const runRuntime = <TResult>(run: () => TResult): TResult => driver?.run(run) ?? run();\n const replayRuntime = <TResult>(run: () => TResult, message: string): TResult => {\n try {\n return runRuntime(run);\n } catch (error) {\n throw normalizeLocalSyncError(error, 'consistency', message);\n }\n };\n\n const applyStored = (commit: StoredCommit): void => {\n if (commit.seq !== headSeq + 1)\n throw new LocalSyncError('consistency', 'Local commit log contains a sequence gap.');\n const result = replayRuntime(\n () =>\n runtime.apply(commit.changes, {\n expectedRevision: runtime.revision(),\n source: 'remote',\n }),\n 'A stored local ChangeSet could not be replayed.'\n );\n if (result.status !== 'committed')\n throw new LocalSyncError('consistency', 'A stored local ChangeSet could not be applied.');\n headSeq = commit.seq;\n };\n\n const restore = async (reset = false): Promise<void> => {\n const current = await timeline.read(documentId);\n if (current.schemaVersion !== schemaVersion)\n throw new LocalSyncError(\n 'consistency',\n 'Local document schema changed while this attachment was open.'\n );\n if (headSeq > current.headSeq)\n throw new LocalSyncError('consistency', 'The local timeline moved behind this attachment.');\n if (reset || headSeq < current.checkpointSeq) {\n const result = replayRuntime(\n () => runtime.replace(current.checkpoint as Infer<TSchema>, { source: 'remote' }),\n 'The local checkpoint could not be restored.'\n );\n if (result.status === 'rejected')\n throw new LocalSyncError('consistency', 'The local checkpoint could not be restored.');\n headSeq = current.checkpointSeq;\n }\n const tail = await timeline.tail(documentId, headSeq);\n for (const commit of tail) applyStored(commit);\n if (headSeq !== current.headSeq)\n throw new LocalSyncError(\n 'consistency',\n 'Local commit log does not reach its recorded head sequence.'\n );\n checkpointSeq = current.checkpointSeq;\n publishState();\n };\n\n const enqueue = (run: () => Promise<void>): Promise<void> => {\n const next = queued.then(run, run);\n queued = next.then(\n () => undefined,\n error => {\n fail(error);\n }\n );\n return next;\n };\n\n const persist = async (changes: ChangeSet): Promise<void> => {\n const storedCommit = await timeline.append({\n documentId,\n expectedHeadSeq: headSeq,\n changes,\n });\n if (storedCommit.seq !== headSeq + 1)\n throw new LocalSyncError(\n 'consistency',\n 'A local commit was assigned an unexpected sequence.'\n );\n headSeq = storedCommit.seq;\n publishState();\n channel?.postMessage({\n kind: 'commit',\n documentId,\n headSeq,\n } satisfies CommitNotification);\n };\n\n const record = (commit: DocumentCommit<TSchema>): void => {\n try {\n const changes = jsonChanges(\n commit.changes,\n 'local commit changes',\n input.changeLimits ?? {}\n );\n void enqueue(() => persist(changes)).catch(() => undefined);\n } catch (error) {\n fail(error);\n }\n };\n\n const holdLeadership = (): Promise<void> =>\n new Promise(resolve => {\n releaseLeadership = resolve;\n });\n\n const lead = async (activated?: () => void): Promise<void> => {\n if (closing || disposed || fault) return;\n await restore();\n if (closing || disposed || fault) return;\n role = 'leader';\n publishState();\n activated?.();\n try {\n await holdLeadership();\n } finally {\n releaseLeadership = undefined;\n if (!closing && !disposed && !fault) {\n role = 'follower';\n publishState();\n }\n }\n };\n\n const watchLeadership = (): void => {\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive' }, async () => {\n try {\n await lead();\n } catch (error) {\n fail(error);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n });\n };\n\n const claimInitialLeadership = async (): Promise<void> => {\n let resolve!: (value: boolean) => void;\n const claimed = new Promise<boolean>(done => {\n resolve = done;\n });\n leadershipLease = lockManager\n .request(`doxum:${documentId}`, { mode: 'exclusive', ifAvailable: true }, async lock => {\n if (lock === null) {\n resolve(false);\n return;\n }\n try {\n await lead(() => resolve(true));\n } catch (error) {\n fail(error);\n resolve(false);\n }\n })\n .catch(error => {\n if (!closing && !disposed) fail(error);\n resolve(false);\n });\n if (!(await claimed) && !fault && !closing && !disposed) watchLeadership();\n };\n\n await restore(true);\n // The timeline, not a runtime constructed before attachment, is the baseline\n // for local undo. Even an identical restored snapshot must discard prior history.\n runtime.history.clear();\n driver = installRuntimeWriteDriver(runtime, { assertWritable: assertLeader });\n channel = new BroadcastChannel(`doxum:${databaseName}:${documentId}`);\n unsubscribe = runtime.subscribe(commit => {\n if (\n (commit.source === 'local' || commit.source === 'system' || commit.source === 'history') &&\n !closing &&\n !disposed\n )\n record(commit);\n });\n channel.onmessage = event => {\n const message = notification(event.data);\n if (\n !message ||\n message.documentId !== documentId ||\n message.headSeq <= headSeq ||\n closing ||\n disposed ||\n fault\n )\n return;\n void enqueue(restore).catch(() => undefined);\n };\n await claimInitialLeadership();\n if (fault) throw fault;\n\n const localSync: LocalSync = {\n state: Object.freeze({\n current: () => snapshot,\n revision: () => stateRevision,\n subscribe: (listener: () => void) => {\n if (disposed) throw new LocalSyncError('disposed', 'Local sync has been disposed.');\n stateListeners.add(listener);\n return () => {\n stateListeners.delete(listener);\n };\n },\n }),\n flush: async (): Promise<void> => {\n assertOpen();\n try {\n await enqueue(restore);\n } catch (error) {\n throw fail(error);\n }\n assertOpen();\n },\n dispose: async (): Promise<void> => {\n if (disposed) return;\n if (closing) {\n await queued;\n return;\n }\n closing = true;\n unsubscribe?.();\n await queued;\n releaseLeadership?.();\n if (role === 'leader') await leadershipLease;\n disposed = true;\n driver?.dispose();\n channel?.close();\n timeline.close();\n publishState();\n stateListeners.clear();\n },\n };\n return Object.freeze(localSync);\n } catch (error) {\n unsubscribe?.();\n driver?.dispose();\n channel?.close();\n timeline.close();\n throw normalizeLocalSyncError(error, 'unavailable', 'Unable to attach local sync.');\n }\n};\n"],"mappings":"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"}