@affectively/dash 5.4.1 → 5.4.5

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 (92) hide show
  1. package/README.md +8 -189
  2. package/dist/automerge_wasm_bg-4hg5vg2g.wasm +0 -0
  3. package/dist/engine/sqlite.d.ts +30 -0
  4. package/dist/engine/vec_extension.d.ts +2 -0
  5. package/dist/index.d.ts +73 -0
  6. package/dist/index.js +53895 -0
  7. package/dist/middleware/errorHandler.d.ts +60 -0
  8. package/dist/{src/sync → sync}/AeonDurableSync.d.ts +7 -9
  9. package/dist/sync/AeonDurableSync.js +1984 -0
  10. package/dist/{src/sync → sync}/AutomergeProvider.d.ts +8 -8
  11. package/dist/sync/AutomergeProvider.js +4421 -0
  12. package/dist/sync/HybridProvider.d.ts +124 -0
  13. package/dist/sync/HybridProvider.js +8328 -0
  14. package/dist/sync/connection/WebRTCConnection.d.ts +23 -0
  15. package/dist/sync/connection/WebRTCConnection.js +59 -0
  16. package/dist/sync/index.d.ts +13 -0
  17. package/dist/sync/index.js +12773 -0
  18. package/dist/sync/provider/YjsSqliteProvider.d.ts +17 -0
  19. package/dist/sync/provider/YjsSqliteProvider.js +54 -0
  20. package/dist/sync/types.d.ts +74 -0
  21. package/dist/sync/webtransport/WebTransportProvider.d.ts +16 -0
  22. package/dist/sync/webtransport/WebTransportProvider.js +55 -0
  23. package/package.json +62 -70
  24. package/dist/src/api/firebase/auth/index.d.ts +0 -137
  25. package/dist/src/api/firebase/auth/index.js +0 -352
  26. package/dist/src/api/firebase/auth/providers.d.ts +0 -254
  27. package/dist/src/api/firebase/auth/providers.js +0 -518
  28. package/dist/src/api/firebase/database/index.d.ts +0 -108
  29. package/dist/src/api/firebase/database/index.js +0 -368
  30. package/dist/src/api/firebase/errors.d.ts +0 -15
  31. package/dist/src/api/firebase/errors.js +0 -215
  32. package/dist/src/api/firebase/firestore/data-types.d.ts +0 -116
  33. package/dist/src/api/firebase/firestore/data-types.js +0 -280
  34. package/dist/src/api/firebase/firestore/index.d.ts +0 -7
  35. package/dist/src/api/firebase/firestore/index.js +0 -13
  36. package/dist/src/api/firebase/firestore/listeners.d.ts +0 -20
  37. package/dist/src/api/firebase/firestore/listeners.js +0 -50
  38. package/dist/src/api/firebase/firestore/operations.d.ts +0 -123
  39. package/dist/src/api/firebase/firestore/operations.js +0 -490
  40. package/dist/src/api/firebase/firestore/query.d.ts +0 -118
  41. package/dist/src/api/firebase/firestore/query.js +0 -418
  42. package/dist/src/api/firebase/index.d.ts +0 -11
  43. package/dist/src/api/firebase/index.js +0 -17
  44. package/dist/src/api/firebase/storage/index.d.ts +0 -100
  45. package/dist/src/api/firebase/storage/index.js +0 -286
  46. package/dist/src/api/firebase/types.d.ts +0 -341
  47. package/dist/src/api/firebase/types.js +0 -4
  48. package/dist/src/auth/manager.d.ts +0 -182
  49. package/dist/src/auth/manager.js +0 -598
  50. package/dist/src/engine/ai.js +0 -76
  51. package/dist/src/engine/sqlite.d.ts +0 -353
  52. package/dist/src/engine/sqlite.js +0 -1328
  53. package/dist/src/engine/vec_extension.d.ts +0 -5
  54. package/dist/src/engine/vec_extension.js +0 -10
  55. package/dist/src/index.d.ts +0 -21
  56. package/dist/src/index.js +0 -26
  57. package/dist/src/mcp/server.js +0 -87
  58. package/dist/src/reactivity/signal.js +0 -31
  59. package/dist/src/schema/lens.d.ts +0 -29
  60. package/dist/src/schema/lens.js +0 -122
  61. package/dist/src/sync/AeonDurableSync.js +0 -133
  62. package/dist/src/sync/AutomergeProvider.js +0 -153
  63. package/dist/src/sync/aeon/config.d.ts +0 -21
  64. package/dist/src/sync/aeon/config.js +0 -14
  65. package/dist/src/sync/aeon/delta-adapter.d.ts +0 -62
  66. package/dist/src/sync/aeon/delta-adapter.js +0 -98
  67. package/dist/src/sync/aeon/index.d.ts +0 -18
  68. package/dist/src/sync/aeon/index.js +0 -19
  69. package/dist/src/sync/aeon/offline-adapter.d.ts +0 -110
  70. package/dist/src/sync/aeon/offline-adapter.js +0 -227
  71. package/dist/src/sync/aeon/presence-adapter.d.ts +0 -114
  72. package/dist/src/sync/aeon/presence-adapter.js +0 -157
  73. package/dist/src/sync/aeon/schema-adapter.d.ts +0 -95
  74. package/dist/src/sync/aeon/schema-adapter.js +0 -163
  75. package/dist/src/sync/backup.d.ts +0 -12
  76. package/dist/src/sync/backup.js +0 -44
  77. package/dist/src/sync/connection.d.ts +0 -20
  78. package/dist/src/sync/connection.js +0 -50
  79. package/dist/src/sync/d1-provider.d.ts +0 -103
  80. package/dist/src/sync/d1-provider.js +0 -418
  81. package/dist/src/sync/hybrid-provider.d.ts +0 -307
  82. package/dist/src/sync/hybrid-provider.js +0 -1353
  83. package/dist/src/sync/provider.d.ts +0 -11
  84. package/dist/src/sync/provider.js +0 -67
  85. package/dist/src/sync/types.d.ts +0 -32
  86. package/dist/src/sync/types.js +0 -4
  87. package/dist/src/sync/verify.d.ts +0 -1
  88. package/dist/src/sync/verify.js +0 -23
  89. package/dist/tsconfig.tsbuildinfo +0 -1
  90. /package/dist/{src/engine → engine}/ai.d.ts +0 -0
  91. /package/dist/{src/mcp → mcp}/server.d.ts +0 -0
  92. /package/dist/{src/reactivity → reactivity}/signal.d.ts +0 -0
@@ -0,0 +1,1984 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __toESM = (mod, isNodeMode, target) => {
7
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
8
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
9
+ for (let key of __getOwnPropNames(mod))
10
+ if (!__hasOwnProp.call(to, key))
11
+ __defProp(to, key, {
12
+ get: () => mod[key],
13
+ enumerable: true
14
+ });
15
+ return to;
16
+ };
17
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
18
+ var __export = (target, all) => {
19
+ for (var name in all)
20
+ __defProp(target, name, {
21
+ get: all[name],
22
+ enumerable: true,
23
+ configurable: true,
24
+ set: (newValue) => all[name] = () => newValue
25
+ });
26
+ };
27
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
28
+
29
+ // ../../node_modules/.bun/eventemitter3@5.0.4/node_modules/eventemitter3/index.js
30
+ var require_eventemitter3 = __commonJS((exports, module) => {
31
+ var has = Object.prototype.hasOwnProperty;
32
+ var prefix = "~";
33
+ function Events() {}
34
+ if (Object.create) {
35
+ Events.prototype = Object.create(null);
36
+ if (!new Events().__proto__)
37
+ prefix = false;
38
+ }
39
+ function EE(fn, context, once) {
40
+ this.fn = fn;
41
+ this.context = context;
42
+ this.once = once || false;
43
+ }
44
+ function addListener(emitter, event, fn, context, once) {
45
+ if (typeof fn !== "function") {
46
+ throw new TypeError("The listener must be a function");
47
+ }
48
+ var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
49
+ if (!emitter._events[evt])
50
+ emitter._events[evt] = listener, emitter._eventsCount++;
51
+ else if (!emitter._events[evt].fn)
52
+ emitter._events[evt].push(listener);
53
+ else
54
+ emitter._events[evt] = [emitter._events[evt], listener];
55
+ return emitter;
56
+ }
57
+ function clearEvent(emitter, evt) {
58
+ if (--emitter._eventsCount === 0)
59
+ emitter._events = new Events;
60
+ else
61
+ delete emitter._events[evt];
62
+ }
63
+ function EventEmitter() {
64
+ this._events = new Events;
65
+ this._eventsCount = 0;
66
+ }
67
+ EventEmitter.prototype.eventNames = function eventNames() {
68
+ var names = [], events, name;
69
+ if (this._eventsCount === 0)
70
+ return names;
71
+ for (name in events = this._events) {
72
+ if (has.call(events, name))
73
+ names.push(prefix ? name.slice(1) : name);
74
+ }
75
+ if (Object.getOwnPropertySymbols) {
76
+ return names.concat(Object.getOwnPropertySymbols(events));
77
+ }
78
+ return names;
79
+ };
80
+ EventEmitter.prototype.listeners = function listeners(event) {
81
+ var evt = prefix ? prefix + event : event, handlers = this._events[evt];
82
+ if (!handlers)
83
+ return [];
84
+ if (handlers.fn)
85
+ return [handlers.fn];
86
+ for (var i = 0, l = handlers.length, ee = new Array(l);i < l; i++) {
87
+ ee[i] = handlers[i].fn;
88
+ }
89
+ return ee;
90
+ };
91
+ EventEmitter.prototype.listenerCount = function listenerCount(event) {
92
+ var evt = prefix ? prefix + event : event, listeners = this._events[evt];
93
+ if (!listeners)
94
+ return 0;
95
+ if (listeners.fn)
96
+ return 1;
97
+ return listeners.length;
98
+ };
99
+ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
100
+ var evt = prefix ? prefix + event : event;
101
+ if (!this._events[evt])
102
+ return false;
103
+ var listeners = this._events[evt], len = arguments.length, args, i;
104
+ if (listeners.fn) {
105
+ if (listeners.once)
106
+ this.removeListener(event, listeners.fn, undefined, true);
107
+ switch (len) {
108
+ case 1:
109
+ return listeners.fn.call(listeners.context), true;
110
+ case 2:
111
+ return listeners.fn.call(listeners.context, a1), true;
112
+ case 3:
113
+ return listeners.fn.call(listeners.context, a1, a2), true;
114
+ case 4:
115
+ return listeners.fn.call(listeners.context, a1, a2, a3), true;
116
+ case 5:
117
+ return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
118
+ case 6:
119
+ return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
120
+ }
121
+ for (i = 1, args = new Array(len - 1);i < len; i++) {
122
+ args[i - 1] = arguments[i];
123
+ }
124
+ listeners.fn.apply(listeners.context, args);
125
+ } else {
126
+ var length = listeners.length, j;
127
+ for (i = 0;i < length; i++) {
128
+ if (listeners[i].once)
129
+ this.removeListener(event, listeners[i].fn, undefined, true);
130
+ switch (len) {
131
+ case 1:
132
+ listeners[i].fn.call(listeners[i].context);
133
+ break;
134
+ case 2:
135
+ listeners[i].fn.call(listeners[i].context, a1);
136
+ break;
137
+ case 3:
138
+ listeners[i].fn.call(listeners[i].context, a1, a2);
139
+ break;
140
+ case 4:
141
+ listeners[i].fn.call(listeners[i].context, a1, a2, a3);
142
+ break;
143
+ default:
144
+ if (!args)
145
+ for (j = 1, args = new Array(len - 1);j < len; j++) {
146
+ args[j - 1] = arguments[j];
147
+ }
148
+ listeners[i].fn.apply(listeners[i].context, args);
149
+ }
150
+ }
151
+ }
152
+ return true;
153
+ };
154
+ EventEmitter.prototype.on = function on(event, fn, context) {
155
+ return addListener(this, event, fn, context, false);
156
+ };
157
+ EventEmitter.prototype.once = function once(event, fn, context) {
158
+ return addListener(this, event, fn, context, true);
159
+ };
160
+ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
161
+ var evt = prefix ? prefix + event : event;
162
+ if (!this._events[evt])
163
+ return this;
164
+ if (!fn) {
165
+ clearEvent(this, evt);
166
+ return this;
167
+ }
168
+ var listeners = this._events[evt];
169
+ if (listeners.fn) {
170
+ if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
171
+ clearEvent(this, evt);
172
+ }
173
+ } else {
174
+ for (var i = 0, events = [], length = listeners.length;i < length; i++) {
175
+ if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
176
+ events.push(listeners[i]);
177
+ }
178
+ }
179
+ if (events.length)
180
+ this._events[evt] = events.length === 1 ? events[0] : events;
181
+ else
182
+ clearEvent(this, evt);
183
+ }
184
+ return this;
185
+ };
186
+ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
187
+ var evt;
188
+ if (event) {
189
+ evt = prefix ? prefix + event : event;
190
+ if (this._events[evt])
191
+ clearEvent(this, evt);
192
+ } else {
193
+ this._events = new Events;
194
+ this._eventsCount = 0;
195
+ }
196
+ return this;
197
+ };
198
+ EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
199
+ EventEmitter.prototype.addListener = EventEmitter.prototype.on;
200
+ EventEmitter.prefixed = prefix;
201
+ EventEmitter.EventEmitter = EventEmitter;
202
+ if (typeof module !== "undefined") {
203
+ module.exports = EventEmitter;
204
+ }
205
+ });
206
+
207
+ // ../../node_modules/.bun/eventemitter3@5.0.4/node_modules/eventemitter3/index.mjs
208
+ var import__ = __toESM(require_eventemitter3(), 1);
209
+
210
+ // ../aeon/dist/offline/index.js
211
+ var consoleLogger = {
212
+ debug: (...args) => {
213
+ console.debug("[AEON:DEBUG]", ...args);
214
+ },
215
+ info: (...args) => {
216
+ console.info("[AEON:INFO]", ...args);
217
+ },
218
+ warn: (...args) => {
219
+ console.warn("[AEON:WARN]", ...args);
220
+ },
221
+ error: (...args) => {
222
+ console.error("[AEON:ERROR]", ...args);
223
+ }
224
+ };
225
+ var currentLogger = consoleLogger;
226
+ function getLogger() {
227
+ return currentLogger;
228
+ }
229
+ var logger = getLogger();
230
+ var OfflineOperationQueue = class _OfflineOperationQueue extends import__.default {
231
+ static DEFAULT_PERSIST_KEY = "aeon:offline-queue:v1";
232
+ queue = /* @__PURE__ */ new Map;
233
+ syncingIds = /* @__PURE__ */ new Set;
234
+ maxQueueSize = 1000;
235
+ defaultMaxRetries = 3;
236
+ persistence = null;
237
+ persistTimer = null;
238
+ persistInFlight = false;
239
+ persistPending = false;
240
+ constructor(maxQueueSizeOrOptions = 1000, defaultMaxRetries = 3) {
241
+ super();
242
+ if (typeof maxQueueSizeOrOptions === "number") {
243
+ this.maxQueueSize = maxQueueSizeOrOptions;
244
+ this.defaultMaxRetries = defaultMaxRetries;
245
+ } else {
246
+ this.maxQueueSize = maxQueueSizeOrOptions.maxQueueSize ?? 1000;
247
+ this.defaultMaxRetries = maxQueueSizeOrOptions.defaultMaxRetries ?? 3;
248
+ if (maxQueueSizeOrOptions.persistence) {
249
+ this.persistence = {
250
+ ...maxQueueSizeOrOptions.persistence,
251
+ key: maxQueueSizeOrOptions.persistence.key ?? _OfflineOperationQueue.DEFAULT_PERSIST_KEY,
252
+ autoPersist: maxQueueSizeOrOptions.persistence.autoPersist ?? true,
253
+ autoLoad: maxQueueSizeOrOptions.persistence.autoLoad ?? false,
254
+ persistDebounceMs: maxQueueSizeOrOptions.persistence.persistDebounceMs ?? 25
255
+ };
256
+ if (this.persistence.autoLoad) {
257
+ this.loadFromPersistence().catch((error) => {
258
+ logger.error("[OfflineOperationQueue] Failed to load persistence", {
259
+ key: this.persistence?.key,
260
+ error: error instanceof Error ? error.message : String(error)
261
+ });
262
+ });
263
+ }
264
+ }
265
+ }
266
+ logger.debug("[OfflineOperationQueue] Initialized", {
267
+ maxQueueSize: this.maxQueueSize,
268
+ defaultMaxRetries: this.defaultMaxRetries,
269
+ persistenceEnabled: this.persistence !== null
270
+ });
271
+ }
272
+ enqueue(type, data, sessionId, priority = "normal", maxRetries) {
273
+ if (this.queue.size >= this.maxQueueSize) {
274
+ const oldest = this.findOldestLowPriority();
275
+ if (oldest) {
276
+ this.queue.delete(oldest.id);
277
+ logger.warn("[OfflineOperationQueue] Queue full, removed oldest", {
278
+ removedId: oldest.id
279
+ });
280
+ }
281
+ }
282
+ const operation = {
283
+ id: `op-${Date.now()}-${Math.random().toString(36).slice(2)}`,
284
+ type,
285
+ data,
286
+ sessionId,
287
+ priority,
288
+ createdAt: Date.now(),
289
+ retryCount: 0,
290
+ maxRetries: maxRetries ?? this.defaultMaxRetries,
291
+ status: "pending"
292
+ };
293
+ this.queue.set(operation.id, operation);
294
+ this.emit("operation-added", operation);
295
+ this.schedulePersist();
296
+ logger.debug("[OfflineOperationQueue] Operation enqueued", {
297
+ id: operation.id,
298
+ type,
299
+ priority,
300
+ queueSize: this.queue.size
301
+ });
302
+ return operation;
303
+ }
304
+ getNextBatch(batchSize = 10) {
305
+ const pending = Array.from(this.queue.values()).filter((op) => op.status === "pending" && !this.syncingIds.has(op.id)).sort((a, b) => {
306
+ const priorityOrder = { high: 0, normal: 1, low: 2 };
307
+ const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
308
+ if (priorityDiff !== 0)
309
+ return priorityDiff;
310
+ return a.createdAt - b.createdAt;
311
+ });
312
+ return pending.slice(0, batchSize);
313
+ }
314
+ markSyncing(operationIds) {
315
+ let changed = false;
316
+ for (const id of operationIds) {
317
+ const op = this.queue.get(id);
318
+ if (op) {
319
+ op.status = "syncing";
320
+ this.syncingIds.add(id);
321
+ changed = true;
322
+ }
323
+ }
324
+ if (changed) {
325
+ this.schedulePersist();
326
+ }
327
+ }
328
+ markSynced(operationId) {
329
+ const op = this.queue.get(operationId);
330
+ if (op) {
331
+ op.status = "synced";
332
+ this.syncingIds.delete(operationId);
333
+ this.emit("operation-synced", op);
334
+ this.schedulePersist();
335
+ setTimeout(() => {
336
+ this.queue.delete(operationId);
337
+ this.schedulePersist();
338
+ if (this.getPendingCount() === 0) {
339
+ this.emit("queue-empty");
340
+ }
341
+ }, 1000);
342
+ }
343
+ }
344
+ markFailed(operationId, error) {
345
+ const op = this.queue.get(operationId);
346
+ if (op) {
347
+ op.retryCount++;
348
+ op.lastError = error.message;
349
+ this.syncingIds.delete(operationId);
350
+ if (op.retryCount >= op.maxRetries) {
351
+ op.status = "failed";
352
+ this.emit("operation-failed", op, error);
353
+ logger.error("[OfflineOperationQueue] Operation permanently failed", {
354
+ id: operationId,
355
+ retries: op.retryCount,
356
+ error: error.message
357
+ });
358
+ } else {
359
+ op.status = "pending";
360
+ logger.warn("[OfflineOperationQueue] Operation failed, will retry", {
361
+ id: operationId,
362
+ retryCount: op.retryCount,
363
+ maxRetries: op.maxRetries
364
+ });
365
+ }
366
+ this.schedulePersist();
367
+ }
368
+ }
369
+ getOperation(operationId) {
370
+ return this.queue.get(operationId);
371
+ }
372
+ getPendingOperations() {
373
+ return Array.from(this.queue.values()).filter((op) => op.status === "pending");
374
+ }
375
+ getPendingCount() {
376
+ return Array.from(this.queue.values()).filter((op) => op.status === "pending").length;
377
+ }
378
+ getStats() {
379
+ const operations = Array.from(this.queue.values());
380
+ const pending = operations.filter((op) => op.status === "pending").length;
381
+ const syncing = operations.filter((op) => op.status === "syncing").length;
382
+ const failed = operations.filter((op) => op.status === "failed").length;
383
+ const synced = operations.filter((op) => op.status === "synced").length;
384
+ const pendingOps = operations.filter((op) => op.status === "pending");
385
+ const oldestPendingMs = pendingOps.length > 0 ? Date.now() - Math.min(...pendingOps.map((op) => op.createdAt)) : 0;
386
+ const averageRetries = operations.length > 0 ? operations.reduce((sum, op) => sum + op.retryCount, 0) / operations.length : 0;
387
+ return {
388
+ pending,
389
+ syncing,
390
+ failed,
391
+ synced,
392
+ totalOperations: operations.length,
393
+ oldestPendingMs,
394
+ averageRetries
395
+ };
396
+ }
397
+ clear() {
398
+ this.queue.clear();
399
+ this.syncingIds.clear();
400
+ this.schedulePersist();
401
+ logger.debug("[OfflineOperationQueue] Queue cleared");
402
+ }
403
+ clearFailed() {
404
+ let changed = false;
405
+ for (const [id, op] of this.queue.entries()) {
406
+ if (op.status === "failed") {
407
+ this.queue.delete(id);
408
+ changed = true;
409
+ }
410
+ }
411
+ if (changed) {
412
+ this.schedulePersist();
413
+ }
414
+ }
415
+ retryFailed() {
416
+ let changed = false;
417
+ for (const op of this.queue.values()) {
418
+ if (op.status === "failed") {
419
+ op.status = "pending";
420
+ op.retryCount = 0;
421
+ changed = true;
422
+ }
423
+ }
424
+ if (changed) {
425
+ this.schedulePersist();
426
+ }
427
+ }
428
+ findOldestLowPriority() {
429
+ const lowPriority = Array.from(this.queue.values()).filter((op) => op.priority === "low" && op.status === "pending").sort((a, b) => a.createdAt - b.createdAt);
430
+ return lowPriority[0] ?? null;
431
+ }
432
+ export() {
433
+ return Array.from(this.queue.values());
434
+ }
435
+ import(operations) {
436
+ this.queue.clear();
437
+ this.syncingIds.clear();
438
+ for (const op of operations) {
439
+ if (this.isValidOfflineOperation(op)) {
440
+ this.queue.set(op.id, {
441
+ ...op,
442
+ status: op.status === "syncing" ? "pending" : op.status
443
+ });
444
+ }
445
+ }
446
+ this.schedulePersist();
447
+ logger.debug("[OfflineOperationQueue] Imported operations", {
448
+ count: this.queue.size
449
+ });
450
+ }
451
+ async saveToPersistence() {
452
+ if (!this.persistence) {
453
+ return;
454
+ }
455
+ const envelope = {
456
+ version: 1,
457
+ updatedAt: Date.now(),
458
+ data: this.export()
459
+ };
460
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
461
+ const raw = serialize(envelope);
462
+ await this.persistence.adapter.setItem(this.persistence.key, raw);
463
+ }
464
+ async loadFromPersistence() {
465
+ if (!this.persistence) {
466
+ return 0;
467
+ }
468
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
469
+ if (!raw) {
470
+ return 0;
471
+ }
472
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
473
+ const envelope = deserialize(raw);
474
+ if (envelope.version !== 1 || !Array.isArray(envelope.data)) {
475
+ throw new Error("Invalid offline queue persistence payload");
476
+ }
477
+ this.queue.clear();
478
+ this.syncingIds.clear();
479
+ let imported = 0;
480
+ for (const operation of envelope.data) {
481
+ if (this.isValidOfflineOperation(operation)) {
482
+ this.queue.set(operation.id, {
483
+ ...operation,
484
+ status: operation.status === "syncing" ? "pending" : operation.status
485
+ });
486
+ imported++;
487
+ }
488
+ }
489
+ logger.debug("[OfflineOperationQueue] Loaded from persistence", {
490
+ key: this.persistence.key,
491
+ imported
492
+ });
493
+ return imported;
494
+ }
495
+ async clearPersistence() {
496
+ if (!this.persistence) {
497
+ return;
498
+ }
499
+ await this.persistence.adapter.removeItem(this.persistence.key);
500
+ }
501
+ schedulePersist() {
502
+ if (!this.persistence || this.persistence.autoPersist === false) {
503
+ return;
504
+ }
505
+ if (this.persistTimer) {
506
+ clearTimeout(this.persistTimer);
507
+ }
508
+ this.persistTimer = setTimeout(() => {
509
+ this.persistSafely();
510
+ }, this.persistence.persistDebounceMs ?? 25);
511
+ }
512
+ async persistSafely() {
513
+ if (!this.persistence) {
514
+ return;
515
+ }
516
+ if (this.persistInFlight) {
517
+ this.persistPending = true;
518
+ return;
519
+ }
520
+ this.persistInFlight = true;
521
+ try {
522
+ await this.saveToPersistence();
523
+ } catch (error) {
524
+ logger.error("[OfflineOperationQueue] Persistence write failed", {
525
+ key: this.persistence.key,
526
+ error: error instanceof Error ? error.message : String(error)
527
+ });
528
+ } finally {
529
+ this.persistInFlight = false;
530
+ const shouldRunAgain = this.persistPending;
531
+ this.persistPending = false;
532
+ if (shouldRunAgain) {
533
+ this.persistSafely();
534
+ }
535
+ }
536
+ }
537
+ isValidOfflineOperation(value) {
538
+ if (typeof value !== "object" || value === null) {
539
+ return false;
540
+ }
541
+ const candidate = value;
542
+ const validType = candidate.type === "create" || candidate.type === "update" || candidate.type === "delete" || candidate.type === "sync" || candidate.type === "batch";
543
+ const validPriority = candidate.priority === "high" || candidate.priority === "normal" || candidate.priority === "low";
544
+ const validStatus = candidate.status === "pending" || candidate.status === "syncing" || candidate.status === "failed" || candidate.status === "synced";
545
+ return typeof candidate.id === "string" && validType && typeof candidate.data === "object" && candidate.data !== null && !Array.isArray(candidate.data) && typeof candidate.sessionId === "string" && validPriority && typeof candidate.createdAt === "number" && typeof candidate.retryCount === "number" && typeof candidate.maxRetries === "number" && validStatus;
546
+ }
547
+ };
548
+
549
+ // ../aeon/dist/distributed/index.js
550
+ var consoleLogger2 = {
551
+ debug: (...args) => {
552
+ console.debug("[AEON:DEBUG]", ...args);
553
+ },
554
+ info: (...args) => {
555
+ console.info("[AEON:INFO]", ...args);
556
+ },
557
+ warn: (...args) => {
558
+ console.warn("[AEON:WARN]", ...args);
559
+ },
560
+ error: (...args) => {
561
+ console.error("[AEON:ERROR]", ...args);
562
+ }
563
+ };
564
+ var currentLogger2 = consoleLogger2;
565
+ function getLogger2() {
566
+ return currentLogger2;
567
+ }
568
+ var logger2 = {
569
+ debug: (...args) => getLogger2().debug(...args),
570
+ info: (...args) => getLogger2().info(...args),
571
+ warn: (...args) => getLogger2().warn(...args),
572
+ error: (...args) => getLogger2().error(...args)
573
+ };
574
+ var ReplicationManager = class _ReplicationManager {
575
+ static DEFAULT_PERSIST_KEY = "aeon:replication-state:v1";
576
+ replicas = /* @__PURE__ */ new Map;
577
+ policies = /* @__PURE__ */ new Map;
578
+ replicationEvents = [];
579
+ syncStatus = /* @__PURE__ */ new Map;
580
+ cryptoProvider = null;
581
+ replicasByDID = /* @__PURE__ */ new Map;
582
+ persistence = null;
583
+ persistTimer = null;
584
+ persistInFlight = false;
585
+ persistPending = false;
586
+ constructor(options) {
587
+ if (options?.persistence) {
588
+ this.persistence = {
589
+ ...options.persistence,
590
+ key: options.persistence.key ?? _ReplicationManager.DEFAULT_PERSIST_KEY,
591
+ autoPersist: options.persistence.autoPersist ?? true,
592
+ autoLoad: options.persistence.autoLoad ?? false,
593
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
594
+ };
595
+ }
596
+ if (this.persistence?.autoLoad) {
597
+ this.loadFromPersistence().catch((error) => {
598
+ logger2.error("[ReplicationManager] Failed to load persistence", {
599
+ key: this.persistence?.key,
600
+ error: error instanceof Error ? error.message : String(error)
601
+ });
602
+ });
603
+ }
604
+ }
605
+ configureCrypto(provider) {
606
+ this.cryptoProvider = provider;
607
+ logger2.debug("[ReplicationManager] Crypto configured", {
608
+ initialized: provider.isInitialized()
609
+ });
610
+ }
611
+ isCryptoEnabled() {
612
+ return this.cryptoProvider !== null && this.cryptoProvider.isInitialized();
613
+ }
614
+ async registerAuthenticatedReplica(replica, encrypted = false) {
615
+ const authenticatedReplica = {
616
+ ...replica,
617
+ encrypted
618
+ };
619
+ this.replicas.set(replica.id, authenticatedReplica);
620
+ this.replicasByDID.set(replica.did, replica.id);
621
+ if (!this.syncStatus.has(replica.nodeId)) {
622
+ this.syncStatus.set(replica.nodeId, { synced: 0, failed: 0 });
623
+ }
624
+ if (this.cryptoProvider && replica.publicSigningKey) {
625
+ await this.cryptoProvider.registerRemoteNode({
626
+ id: replica.nodeId,
627
+ did: replica.did,
628
+ publicSigningKey: replica.publicSigningKey,
629
+ publicEncryptionKey: replica.publicEncryptionKey
630
+ });
631
+ }
632
+ const event = {
633
+ type: "replica-added",
634
+ replicaId: replica.id,
635
+ nodeId: replica.nodeId,
636
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
637
+ details: { did: replica.did, encrypted, authenticated: true }
638
+ };
639
+ this.replicationEvents.push(event);
640
+ this.schedulePersist();
641
+ logger2.debug("[ReplicationManager] Authenticated replica registered", {
642
+ replicaId: replica.id,
643
+ did: replica.did,
644
+ encrypted
645
+ });
646
+ return authenticatedReplica;
647
+ }
648
+ getReplicaByDID(did) {
649
+ const replicaId = this.replicasByDID.get(did);
650
+ if (!replicaId)
651
+ return;
652
+ return this.replicas.get(replicaId);
653
+ }
654
+ getEncryptedReplicas() {
655
+ return Array.from(this.replicas.values()).filter((r) => r.encrypted);
656
+ }
657
+ async encryptForReplica(data, targetReplicaDID) {
658
+ if (!this.cryptoProvider || !this.cryptoProvider.isInitialized()) {
659
+ throw new Error("Crypto provider not initialized");
660
+ }
661
+ const dataBytes = new TextEncoder().encode(JSON.stringify(data));
662
+ const encrypted = await this.cryptoProvider.encrypt(dataBytes, targetReplicaDID);
663
+ const localDID = this.cryptoProvider.getLocalDID();
664
+ return {
665
+ ct: encrypted.ct,
666
+ iv: encrypted.iv,
667
+ tag: encrypted.tag,
668
+ epk: encrypted.epk,
669
+ senderDID: localDID || undefined,
670
+ targetDID: targetReplicaDID,
671
+ encryptedAt: encrypted.encryptedAt
672
+ };
673
+ }
674
+ async decryptReplicationData(encrypted) {
675
+ if (!this.cryptoProvider || !this.cryptoProvider.isInitialized()) {
676
+ throw new Error("Crypto provider not initialized");
677
+ }
678
+ const decrypted = await this.cryptoProvider.decrypt({
679
+ alg: "ECIES-P256",
680
+ ct: encrypted.ct,
681
+ iv: encrypted.iv,
682
+ tag: encrypted.tag,
683
+ epk: encrypted.epk
684
+ }, encrypted.senderDID);
685
+ return JSON.parse(new TextDecoder().decode(decrypted));
686
+ }
687
+ createEncryptedPolicy(name, replicationFactor, consistencyLevel, encryptionMode, options) {
688
+ const policy = {
689
+ id: `policy-${Date.now()}-${Math.random().toString(36).slice(2)}`,
690
+ name,
691
+ replicationFactor,
692
+ consistencyLevel,
693
+ syncInterval: options?.syncInterval || 1000,
694
+ maxReplicationLag: options?.maxReplicationLag || 1e4,
695
+ encryptionMode,
696
+ requiredCapabilities: options?.requiredCapabilities
697
+ };
698
+ this.policies.set(policy.id, policy);
699
+ logger2.debug("[ReplicationManager] Encrypted policy created", {
700
+ policyId: policy.id,
701
+ name,
702
+ replicationFactor,
703
+ encryptionMode
704
+ });
705
+ return policy;
706
+ }
707
+ async verifyReplicaCapabilities(replicaDID, token, policyId) {
708
+ if (!this.cryptoProvider) {
709
+ return { authorized: true };
710
+ }
711
+ const policy = policyId ? this.policies.get(policyId) : undefined;
712
+ const result = await this.cryptoProvider.verifyUCAN(token, {
713
+ requiredCapabilities: policy?.requiredCapabilities?.map((cap) => ({
714
+ can: cap,
715
+ with: "*"
716
+ }))
717
+ });
718
+ if (!result.authorized) {
719
+ logger2.warn("[ReplicationManager] Replica capability verification failed", {
720
+ replicaDID,
721
+ error: result.error
722
+ });
723
+ }
724
+ return result;
725
+ }
726
+ registerReplica(replica) {
727
+ this.replicas.set(replica.id, replica);
728
+ if (!this.syncStatus.has(replica.nodeId)) {
729
+ this.syncStatus.set(replica.nodeId, { synced: 0, failed: 0 });
730
+ }
731
+ const event = {
732
+ type: "replica-added",
733
+ replicaId: replica.id,
734
+ nodeId: replica.nodeId,
735
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
736
+ };
737
+ this.replicationEvents.push(event);
738
+ this.schedulePersist();
739
+ logger2.debug("[ReplicationManager] Replica registered", {
740
+ replicaId: replica.id,
741
+ nodeId: replica.nodeId,
742
+ status: replica.status
743
+ });
744
+ }
745
+ removeReplica(replicaId) {
746
+ const replica = this.replicas.get(replicaId);
747
+ if (!replica) {
748
+ throw new Error(`Replica ${replicaId} not found`);
749
+ }
750
+ this.replicas.delete(replicaId);
751
+ const event = {
752
+ type: "replica-removed",
753
+ replicaId,
754
+ nodeId: replica.nodeId,
755
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
756
+ };
757
+ this.replicationEvents.push(event);
758
+ this.schedulePersist();
759
+ logger2.debug("[ReplicationManager] Replica removed", { replicaId });
760
+ }
761
+ createPolicy(name, replicationFactor, consistencyLevel, syncInterval = 1000, maxReplicationLag = 1e4) {
762
+ const policy = {
763
+ id: `policy-${Date.now()}-${Math.random().toString(36).slice(2)}`,
764
+ name,
765
+ replicationFactor,
766
+ consistencyLevel,
767
+ syncInterval,
768
+ maxReplicationLag
769
+ };
770
+ this.policies.set(policy.id, policy);
771
+ this.schedulePersist();
772
+ logger2.debug("[ReplicationManager] Policy created", {
773
+ policyId: policy.id,
774
+ name,
775
+ replicationFactor,
776
+ consistencyLevel
777
+ });
778
+ return policy;
779
+ }
780
+ updateReplicaStatus(replicaId, status, lagBytes = 0, lagMillis = 0) {
781
+ const replica = this.replicas.get(replicaId);
782
+ if (!replica) {
783
+ throw new Error(`Replica ${replicaId} not found`);
784
+ }
785
+ replica.status = status;
786
+ replica.lagBytes = lagBytes;
787
+ replica.lagMillis = lagMillis;
788
+ replica.lastSyncTime = (/* @__PURE__ */ new Date()).toISOString();
789
+ const event = {
790
+ type: status === "syncing" ? "replica-synced" : "sync-failed",
791
+ replicaId,
792
+ nodeId: replica.nodeId,
793
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
794
+ details: { status, lagBytes, lagMillis }
795
+ };
796
+ this.replicationEvents.push(event);
797
+ const syncStatus = this.syncStatus.get(replica.nodeId);
798
+ if (syncStatus) {
799
+ if (status === "syncing" || status === "secondary") {
800
+ syncStatus.synced++;
801
+ } else if (status === "failed") {
802
+ syncStatus.failed++;
803
+ }
804
+ }
805
+ logger2.debug("[ReplicationManager] Replica status updated", {
806
+ replicaId,
807
+ status,
808
+ lagBytes,
809
+ lagMillis
810
+ });
811
+ this.schedulePersist();
812
+ }
813
+ getReplicasForNode(nodeId) {
814
+ return Array.from(this.replicas.values()).filter((r) => r.nodeId === nodeId);
815
+ }
816
+ getHealthyReplicas() {
817
+ return Array.from(this.replicas.values()).filter((r) => r.status === "secondary" || r.status === "primary");
818
+ }
819
+ getSyncingReplicas() {
820
+ return Array.from(this.replicas.values()).filter((r) => r.status === "syncing");
821
+ }
822
+ getFailedReplicas() {
823
+ return Array.from(this.replicas.values()).filter((r) => r.status === "failed");
824
+ }
825
+ checkReplicationHealth(policyId) {
826
+ const policy = this.policies.get(policyId);
827
+ if (!policy) {
828
+ throw new Error(`Policy ${policyId} not found`);
829
+ }
830
+ const healthy = this.getHealthyReplicas();
831
+ const maxLag = Math.max(0, ...healthy.map((r) => r.lagMillis));
832
+ return {
833
+ healthy: healthy.length >= policy.replicationFactor && maxLag <= policy.maxReplicationLag,
834
+ replicasInPolicy: policy.replicationFactor,
835
+ healthyReplicas: healthy.length,
836
+ replicationLag: maxLag
837
+ };
838
+ }
839
+ getConsistencyLevel(policyId) {
840
+ const policy = this.policies.get(policyId);
841
+ if (!policy) {
842
+ return "eventual";
843
+ }
844
+ return policy.consistencyLevel;
845
+ }
846
+ getReplica(replicaId) {
847
+ return this.replicas.get(replicaId);
848
+ }
849
+ getAllReplicas() {
850
+ return Array.from(this.replicas.values());
851
+ }
852
+ getPolicy(policyId) {
853
+ return this.policies.get(policyId);
854
+ }
855
+ getAllPolicies() {
856
+ return Array.from(this.policies.values());
857
+ }
858
+ getStatistics() {
859
+ const healthy = this.getHealthyReplicas().length;
860
+ const syncing = this.getSyncingReplicas().length;
861
+ const failed = this.getFailedReplicas().length;
862
+ const total = this.replicas.size;
863
+ const replicationLags = Array.from(this.replicas.values()).map((r) => r.lagMillis);
864
+ const avgLag = replicationLags.length > 0 ? replicationLags.reduce((a, b) => a + b) / replicationLags.length : 0;
865
+ const maxLag = replicationLags.length > 0 ? Math.max(...replicationLags) : 0;
866
+ return {
867
+ totalReplicas: total,
868
+ healthyReplicas: healthy,
869
+ syncingReplicas: syncing,
870
+ failedReplicas: failed,
871
+ healthiness: total > 0 ? healthy / total * 100 : 0,
872
+ averageReplicationLagMs: avgLag,
873
+ maxReplicationLagMs: maxLag,
874
+ totalPolicies: this.policies.size
875
+ };
876
+ }
877
+ getReplicationEvents(limit) {
878
+ const events = [...this.replicationEvents];
879
+ if (limit) {
880
+ return events.slice(-limit);
881
+ }
882
+ return events;
883
+ }
884
+ getSyncStatus(nodeId) {
885
+ return this.syncStatus.get(nodeId) || { synced: 0, failed: 0 };
886
+ }
887
+ getReplicationLagDistribution() {
888
+ const distribution = {
889
+ "0-100ms": 0,
890
+ "100-500ms": 0,
891
+ "500-1000ms": 0,
892
+ "1000+ms": 0
893
+ };
894
+ for (const replica of this.replicas.values()) {
895
+ if (replica.lagMillis <= 100) {
896
+ distribution["0-100ms"]++;
897
+ } else if (replica.lagMillis <= 500) {
898
+ distribution["100-500ms"]++;
899
+ } else if (replica.lagMillis <= 1000) {
900
+ distribution["500-1000ms"]++;
901
+ } else {
902
+ distribution["1000+ms"]++;
903
+ }
904
+ }
905
+ return distribution;
906
+ }
907
+ canSatisfyConsistency(policyId, _requiredAcks) {
908
+ const policy = this.policies.get(policyId);
909
+ if (!policy) {
910
+ return false;
911
+ }
912
+ const healthyCount = this.getHealthyReplicas().length;
913
+ switch (policy.consistencyLevel) {
914
+ case "eventual":
915
+ return true;
916
+ case "read-after-write":
917
+ return healthyCount >= 1;
918
+ case "strong":
919
+ return healthyCount >= policy.replicationFactor;
920
+ default:
921
+ return false;
922
+ }
923
+ }
924
+ async saveToPersistence() {
925
+ if (!this.persistence) {
926
+ return;
927
+ }
928
+ const data = {
929
+ replicas: this.getAllReplicas(),
930
+ policies: this.getAllPolicies(),
931
+ syncStatus: Array.from(this.syncStatus.entries()).map(([nodeId, state]) => ({
932
+ nodeId,
933
+ synced: state.synced,
934
+ failed: state.failed
935
+ }))
936
+ };
937
+ const envelope = {
938
+ version: 1,
939
+ updatedAt: Date.now(),
940
+ data
941
+ };
942
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
943
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
944
+ }
945
+ async loadFromPersistence() {
946
+ if (!this.persistence) {
947
+ return { replicas: 0, policies: 0, syncStatus: 0 };
948
+ }
949
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
950
+ if (!raw) {
951
+ return { replicas: 0, policies: 0, syncStatus: 0 };
952
+ }
953
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
954
+ const envelope = deserialize(raw);
955
+ if (envelope.version !== 1 || !envelope.data) {
956
+ throw new Error("Invalid replication persistence payload");
957
+ }
958
+ if (!Array.isArray(envelope.data.replicas) || !Array.isArray(envelope.data.policies) || !Array.isArray(envelope.data.syncStatus)) {
959
+ throw new Error("Invalid replication persistence structure");
960
+ }
961
+ this.replicas.clear();
962
+ this.policies.clear();
963
+ this.syncStatus.clear();
964
+ this.replicasByDID.clear();
965
+ let importedReplicas = 0;
966
+ for (const replica of envelope.data.replicas) {
967
+ if (this.isValidReplica(replica)) {
968
+ this.replicas.set(replica.id, replica);
969
+ if (replica.did) {
970
+ this.replicasByDID.set(replica.did, replica.id);
971
+ }
972
+ importedReplicas++;
973
+ }
974
+ }
975
+ let importedPolicies = 0;
976
+ for (const policy of envelope.data.policies) {
977
+ if (this.isValidPolicy(policy)) {
978
+ this.policies.set(policy.id, policy);
979
+ importedPolicies++;
980
+ }
981
+ }
982
+ let importedSyncStatus = 0;
983
+ for (const status of envelope.data.syncStatus) {
984
+ if (typeof status.nodeId === "string" && typeof status.synced === "number" && typeof status.failed === "number") {
985
+ this.syncStatus.set(status.nodeId, {
986
+ synced: status.synced,
987
+ failed: status.failed
988
+ });
989
+ importedSyncStatus++;
990
+ }
991
+ }
992
+ logger2.debug("[ReplicationManager] Loaded from persistence", {
993
+ key: this.persistence.key,
994
+ replicas: importedReplicas,
995
+ policies: importedPolicies,
996
+ syncStatus: importedSyncStatus
997
+ });
998
+ return {
999
+ replicas: importedReplicas,
1000
+ policies: importedPolicies,
1001
+ syncStatus: importedSyncStatus
1002
+ };
1003
+ }
1004
+ async clearPersistence() {
1005
+ if (!this.persistence) {
1006
+ return;
1007
+ }
1008
+ await this.persistence.adapter.removeItem(this.persistence.key);
1009
+ }
1010
+ schedulePersist() {
1011
+ if (!this.persistence || this.persistence.autoPersist === false) {
1012
+ return;
1013
+ }
1014
+ if (this.persistTimer) {
1015
+ clearTimeout(this.persistTimer);
1016
+ }
1017
+ this.persistTimer = setTimeout(() => {
1018
+ this.persistSafely();
1019
+ }, this.persistence.persistDebounceMs ?? 25);
1020
+ }
1021
+ async persistSafely() {
1022
+ if (!this.persistence) {
1023
+ return;
1024
+ }
1025
+ if (this.persistInFlight) {
1026
+ this.persistPending = true;
1027
+ return;
1028
+ }
1029
+ this.persistInFlight = true;
1030
+ try {
1031
+ await this.saveToPersistence();
1032
+ } catch (error) {
1033
+ logger2.error("[ReplicationManager] Persistence write failed", {
1034
+ key: this.persistence.key,
1035
+ error: error instanceof Error ? error.message : String(error)
1036
+ });
1037
+ } finally {
1038
+ this.persistInFlight = false;
1039
+ const shouldRunAgain = this.persistPending;
1040
+ this.persistPending = false;
1041
+ if (shouldRunAgain) {
1042
+ this.persistSafely();
1043
+ }
1044
+ }
1045
+ }
1046
+ isValidReplica(value) {
1047
+ if (typeof value !== "object" || value === null) {
1048
+ return false;
1049
+ }
1050
+ const candidate = value;
1051
+ const validStatus = candidate.status === "primary" || candidate.status === "secondary" || candidate.status === "syncing" || candidate.status === "failed";
1052
+ return typeof candidate.id === "string" && typeof candidate.nodeId === "string" && validStatus && typeof candidate.lastSyncTime === "string" && typeof candidate.lagBytes === "number" && typeof candidate.lagMillis === "number";
1053
+ }
1054
+ isValidPolicy(value) {
1055
+ if (typeof value !== "object" || value === null) {
1056
+ return false;
1057
+ }
1058
+ const candidate = value;
1059
+ const validConsistency = candidate.consistencyLevel === "eventual" || candidate.consistencyLevel === "read-after-write" || candidate.consistencyLevel === "strong";
1060
+ return typeof candidate.id === "string" && typeof candidate.name === "string" && typeof candidate.replicationFactor === "number" && validConsistency && typeof candidate.syncInterval === "number" && typeof candidate.maxReplicationLag === "number";
1061
+ }
1062
+ clear() {
1063
+ this.replicas.clear();
1064
+ this.policies.clear();
1065
+ this.replicationEvents = [];
1066
+ this.syncStatus.clear();
1067
+ this.replicasByDID.clear();
1068
+ this.cryptoProvider = null;
1069
+ this.schedulePersist();
1070
+ }
1071
+ getCryptoProvider() {
1072
+ return this.cryptoProvider;
1073
+ }
1074
+ };
1075
+ var SyncProtocol = class _SyncProtocol {
1076
+ static DEFAULT_PERSIST_KEY = "aeon:sync-protocol:v1";
1077
+ version = "1.0.0";
1078
+ messageQueue = [];
1079
+ messageMap = /* @__PURE__ */ new Map;
1080
+ handshakes = /* @__PURE__ */ new Map;
1081
+ protocolErrors = [];
1082
+ messageCounter = 0;
1083
+ cryptoProvider = null;
1084
+ cryptoConfig = null;
1085
+ persistence = null;
1086
+ persistTimer = null;
1087
+ persistInFlight = false;
1088
+ persistPending = false;
1089
+ constructor(options) {
1090
+ if (options?.persistence) {
1091
+ this.persistence = {
1092
+ ...options.persistence,
1093
+ key: options.persistence.key ?? _SyncProtocol.DEFAULT_PERSIST_KEY,
1094
+ autoPersist: options.persistence.autoPersist ?? true,
1095
+ autoLoad: options.persistence.autoLoad ?? false,
1096
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
1097
+ };
1098
+ }
1099
+ if (this.persistence?.autoLoad) {
1100
+ this.loadFromPersistence().catch((error) => {
1101
+ logger2.error("[SyncProtocol] Failed to load persistence", {
1102
+ key: this.persistence?.key,
1103
+ error: error instanceof Error ? error.message : String(error)
1104
+ });
1105
+ });
1106
+ }
1107
+ }
1108
+ configureCrypto(provider, config) {
1109
+ this.cryptoProvider = provider;
1110
+ this.cryptoConfig = {
1111
+ encryptionMode: config?.encryptionMode ?? "none",
1112
+ requireSignatures: config?.requireSignatures ?? false,
1113
+ requireCapabilities: config?.requireCapabilities ?? false,
1114
+ requiredCapabilities: config?.requiredCapabilities
1115
+ };
1116
+ logger2.debug("[SyncProtocol] Crypto configured", {
1117
+ encryptionMode: this.cryptoConfig.encryptionMode,
1118
+ requireSignatures: this.cryptoConfig.requireSignatures,
1119
+ requireCapabilities: this.cryptoConfig.requireCapabilities
1120
+ });
1121
+ }
1122
+ isCryptoEnabled() {
1123
+ return this.cryptoProvider !== null && this.cryptoProvider.isInitialized();
1124
+ }
1125
+ getCryptoConfig() {
1126
+ return this.cryptoConfig ? { ...this.cryptoConfig } : null;
1127
+ }
1128
+ getVersion() {
1129
+ return this.version;
1130
+ }
1131
+ async createAuthenticatedHandshake(capabilities, targetDID) {
1132
+ if (!this.cryptoProvider || !this.cryptoProvider.isInitialized()) {
1133
+ throw new Error("Crypto provider not initialized");
1134
+ }
1135
+ const localDID = this.cryptoProvider.getLocalDID();
1136
+ if (!localDID) {
1137
+ throw new Error("Local DID not available");
1138
+ }
1139
+ const publicInfo = await this.cryptoProvider.exportPublicIdentity();
1140
+ if (!publicInfo) {
1141
+ throw new Error("Cannot export public identity");
1142
+ }
1143
+ let ucan;
1144
+ if (targetDID && this.cryptoConfig?.requireCapabilities) {
1145
+ const caps = this.cryptoConfig.requiredCapabilities || [
1146
+ { can: "aeon:sync:read", with: "*" },
1147
+ { can: "aeon:sync:write", with: "*" }
1148
+ ];
1149
+ ucan = await this.cryptoProvider.createUCAN(targetDID, caps);
1150
+ }
1151
+ const handshakePayload = {
1152
+ protocolVersion: this.version,
1153
+ nodeId: localDID,
1154
+ capabilities,
1155
+ state: "initiating",
1156
+ did: localDID,
1157
+ publicSigningKey: publicInfo.publicSigningKey,
1158
+ publicEncryptionKey: publicInfo.publicEncryptionKey,
1159
+ ucan
1160
+ };
1161
+ const message = {
1162
+ type: "handshake",
1163
+ version: this.version,
1164
+ sender: localDID,
1165
+ receiver: targetDID || "",
1166
+ messageId: this.generateMessageId(),
1167
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1168
+ payload: handshakePayload
1169
+ };
1170
+ if (this.cryptoConfig?.requireSignatures) {
1171
+ const signed = await this.cryptoProvider.signData(handshakePayload);
1172
+ message.auth = {
1173
+ senderDID: localDID,
1174
+ receiverDID: targetDID,
1175
+ signature: signed.signature
1176
+ };
1177
+ }
1178
+ this.messageMap.set(message.messageId, message);
1179
+ this.messageQueue.push(message);
1180
+ this.schedulePersist();
1181
+ logger2.debug("[SyncProtocol] Authenticated handshake created", {
1182
+ messageId: message.messageId,
1183
+ did: localDID,
1184
+ capabilities: capabilities.length,
1185
+ hasUCAN: !!ucan
1186
+ });
1187
+ return message;
1188
+ }
1189
+ async verifyAuthenticatedHandshake(message) {
1190
+ if (message.type !== "handshake") {
1191
+ return { valid: false, error: "Message is not a handshake" };
1192
+ }
1193
+ const handshake = message.payload;
1194
+ if (!this.cryptoProvider || !this.cryptoConfig) {
1195
+ this.handshakes.set(message.sender, handshake);
1196
+ this.schedulePersist();
1197
+ return { valid: true, handshake };
1198
+ }
1199
+ if (handshake.did && handshake.publicSigningKey) {
1200
+ await this.cryptoProvider.registerRemoteNode({
1201
+ id: handshake.nodeId,
1202
+ did: handshake.did,
1203
+ publicSigningKey: handshake.publicSigningKey,
1204
+ publicEncryptionKey: handshake.publicEncryptionKey
1205
+ });
1206
+ }
1207
+ if (this.cryptoConfig.requireSignatures && message.auth?.signature) {
1208
+ const signed = {
1209
+ payload: handshake,
1210
+ signature: message.auth.signature,
1211
+ signer: message.auth.senderDID || message.sender,
1212
+ algorithm: "ES256",
1213
+ signedAt: Date.now()
1214
+ };
1215
+ const isValid = await this.cryptoProvider.verifySignedData(signed);
1216
+ if (!isValid) {
1217
+ logger2.warn("[SyncProtocol] Handshake signature verification failed", {
1218
+ messageId: message.messageId,
1219
+ sender: message.sender
1220
+ });
1221
+ return { valid: false, error: "Invalid signature" };
1222
+ }
1223
+ }
1224
+ if (this.cryptoConfig.requireCapabilities && handshake.ucan) {
1225
+ const localDID = this.cryptoProvider.getLocalDID();
1226
+ const result = await this.cryptoProvider.verifyUCAN(handshake.ucan, {
1227
+ expectedAudience: localDID || undefined,
1228
+ requiredCapabilities: this.cryptoConfig.requiredCapabilities
1229
+ });
1230
+ if (!result.authorized) {
1231
+ logger2.warn("[SyncProtocol] Handshake UCAN verification failed", {
1232
+ messageId: message.messageId,
1233
+ error: result.error
1234
+ });
1235
+ return { valid: false, error: result.error || "Unauthorized" };
1236
+ }
1237
+ }
1238
+ this.handshakes.set(message.sender, handshake);
1239
+ this.schedulePersist();
1240
+ logger2.debug("[SyncProtocol] Authenticated handshake verified", {
1241
+ messageId: message.messageId,
1242
+ did: handshake.did
1243
+ });
1244
+ return { valid: true, handshake };
1245
+ }
1246
+ async signMessage(message, payload, encrypt = false) {
1247
+ if (!this.cryptoProvider || !this.cryptoProvider.isInitialized()) {
1248
+ throw new Error("Crypto provider not initialized");
1249
+ }
1250
+ const localDID = this.cryptoProvider.getLocalDID();
1251
+ const signed = await this.cryptoProvider.signData(payload);
1252
+ message.auth = {
1253
+ senderDID: localDID || undefined,
1254
+ receiverDID: message.receiver || undefined,
1255
+ signature: signed.signature,
1256
+ encrypted: false
1257
+ };
1258
+ if (encrypt && message.receiver && this.cryptoConfig?.encryptionMode !== "none") {
1259
+ const payloadBytes = new TextEncoder().encode(JSON.stringify(payload));
1260
+ const encrypted = await this.cryptoProvider.encrypt(payloadBytes, message.receiver);
1261
+ message.payload = encrypted;
1262
+ message.auth.encrypted = true;
1263
+ logger2.debug("[SyncProtocol] Message encrypted", {
1264
+ messageId: message.messageId,
1265
+ recipient: message.receiver
1266
+ });
1267
+ } else {
1268
+ message.payload = payload;
1269
+ }
1270
+ return message;
1271
+ }
1272
+ async verifyMessage(message) {
1273
+ if (!this.cryptoProvider || !message.auth) {
1274
+ return { valid: true, payload: message.payload };
1275
+ }
1276
+ let payload = message.payload;
1277
+ if (message.auth.encrypted && message.payload) {
1278
+ try {
1279
+ const encrypted = message.payload;
1280
+ const decrypted = await this.cryptoProvider.decrypt(encrypted, message.auth.senderDID);
1281
+ payload = JSON.parse(new TextDecoder().decode(decrypted));
1282
+ logger2.debug("[SyncProtocol] Message decrypted", {
1283
+ messageId: message.messageId
1284
+ });
1285
+ } catch (error) {
1286
+ return {
1287
+ valid: false,
1288
+ error: `Decryption failed: ${error instanceof Error ? error.message : String(error)}`
1289
+ };
1290
+ }
1291
+ }
1292
+ if (message.auth.signature && message.auth.senderDID) {
1293
+ const signed = {
1294
+ payload,
1295
+ signature: message.auth.signature,
1296
+ signer: message.auth.senderDID,
1297
+ algorithm: "ES256",
1298
+ signedAt: Date.now()
1299
+ };
1300
+ const isValid = await this.cryptoProvider.verifySignedData(signed);
1301
+ if (!isValid) {
1302
+ return { valid: false, error: "Invalid signature" };
1303
+ }
1304
+ }
1305
+ return { valid: true, payload };
1306
+ }
1307
+ createHandshakeMessage(nodeId, capabilities) {
1308
+ const message = {
1309
+ type: "handshake",
1310
+ version: this.version,
1311
+ sender: nodeId,
1312
+ receiver: "",
1313
+ messageId: this.generateMessageId(),
1314
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1315
+ payload: {
1316
+ protocolVersion: this.version,
1317
+ nodeId,
1318
+ capabilities,
1319
+ state: "initiating"
1320
+ }
1321
+ };
1322
+ this.messageMap.set(message.messageId, message);
1323
+ this.messageQueue.push(message);
1324
+ this.schedulePersist();
1325
+ logger2.debug("[SyncProtocol] Handshake message created", {
1326
+ messageId: message.messageId,
1327
+ nodeId,
1328
+ capabilities: capabilities.length
1329
+ });
1330
+ return message;
1331
+ }
1332
+ createSyncRequestMessage(sender, receiver, sessionId, fromVersion, toVersion, filter) {
1333
+ const message = {
1334
+ type: "sync-request",
1335
+ version: this.version,
1336
+ sender,
1337
+ receiver,
1338
+ messageId: this.generateMessageId(),
1339
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1340
+ payload: {
1341
+ sessionId,
1342
+ fromVersion,
1343
+ toVersion,
1344
+ filter
1345
+ }
1346
+ };
1347
+ this.messageMap.set(message.messageId, message);
1348
+ this.messageQueue.push(message);
1349
+ this.schedulePersist();
1350
+ logger2.debug("[SyncProtocol] Sync request created", {
1351
+ messageId: message.messageId,
1352
+ sessionId,
1353
+ fromVersion,
1354
+ toVersion
1355
+ });
1356
+ return message;
1357
+ }
1358
+ createSyncResponseMessage(sender, receiver, sessionId, fromVersion, toVersion, data, hasMore = false, offset = 0) {
1359
+ const message = {
1360
+ type: "sync-response",
1361
+ version: this.version,
1362
+ sender,
1363
+ receiver,
1364
+ messageId: this.generateMessageId(),
1365
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1366
+ payload: {
1367
+ sessionId,
1368
+ fromVersion,
1369
+ toVersion,
1370
+ data,
1371
+ hasMore,
1372
+ offset
1373
+ }
1374
+ };
1375
+ this.messageMap.set(message.messageId, message);
1376
+ this.messageQueue.push(message);
1377
+ this.schedulePersist();
1378
+ logger2.debug("[SyncProtocol] Sync response created", {
1379
+ messageId: message.messageId,
1380
+ sessionId,
1381
+ itemCount: data.length,
1382
+ hasMore
1383
+ });
1384
+ return message;
1385
+ }
1386
+ createAckMessage(sender, receiver, messageId) {
1387
+ const message = {
1388
+ type: "ack",
1389
+ version: this.version,
1390
+ sender,
1391
+ receiver,
1392
+ messageId: this.generateMessageId(),
1393
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1394
+ payload: { acknowledgedMessageId: messageId }
1395
+ };
1396
+ this.messageMap.set(message.messageId, message);
1397
+ this.messageQueue.push(message);
1398
+ this.schedulePersist();
1399
+ return message;
1400
+ }
1401
+ createErrorMessage(sender, receiver, error, relatedMessageId) {
1402
+ const message = {
1403
+ type: "error",
1404
+ version: this.version,
1405
+ sender,
1406
+ receiver,
1407
+ messageId: this.generateMessageId(),
1408
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1409
+ payload: {
1410
+ error,
1411
+ relatedMessageId
1412
+ }
1413
+ };
1414
+ this.messageMap.set(message.messageId, message);
1415
+ this.messageQueue.push(message);
1416
+ this.protocolErrors.push({
1417
+ error,
1418
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1419
+ });
1420
+ this.schedulePersist();
1421
+ logger2.error("[SyncProtocol] Error message created", {
1422
+ messageId: message.messageId,
1423
+ errorCode: error.code,
1424
+ recoverable: error.recoverable
1425
+ });
1426
+ return message;
1427
+ }
1428
+ validateMessage(message) {
1429
+ const errors = [];
1430
+ if (!message.type) {
1431
+ errors.push("Message type is required");
1432
+ }
1433
+ if (!message.sender) {
1434
+ errors.push("Sender is required");
1435
+ }
1436
+ if (!message.messageId) {
1437
+ errors.push("Message ID is required");
1438
+ }
1439
+ if (!message.timestamp) {
1440
+ errors.push("Timestamp is required");
1441
+ }
1442
+ const timestampValue = new Date(message.timestamp);
1443
+ if (Number.isNaN(timestampValue.getTime())) {
1444
+ errors.push("Invalid timestamp format");
1445
+ }
1446
+ return {
1447
+ valid: errors.length === 0,
1448
+ errors
1449
+ };
1450
+ }
1451
+ serializeMessage(message) {
1452
+ try {
1453
+ return JSON.stringify(message);
1454
+ } catch (error) {
1455
+ logger2.error("[SyncProtocol] Message serialization failed", {
1456
+ messageId: message.messageId,
1457
+ error: error instanceof Error ? error.message : String(error)
1458
+ });
1459
+ throw new Error(`Failed to serialize message: ${error instanceof Error ? error.message : String(error)}`);
1460
+ }
1461
+ }
1462
+ deserializeMessage(data) {
1463
+ try {
1464
+ const message = JSON.parse(data);
1465
+ const validation = this.validateMessage(message);
1466
+ if (!validation.valid) {
1467
+ throw new Error(`Invalid message: ${validation.errors.join(", ")}`);
1468
+ }
1469
+ return message;
1470
+ } catch (error) {
1471
+ logger2.error("[SyncProtocol] Message deserialization failed", {
1472
+ error: error instanceof Error ? error.message : String(error)
1473
+ });
1474
+ throw new Error(`Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`);
1475
+ }
1476
+ }
1477
+ processHandshake(message) {
1478
+ if (message.type !== "handshake") {
1479
+ throw new Error("Message is not a handshake");
1480
+ }
1481
+ const handshake = message.payload;
1482
+ const nodeId = message.sender;
1483
+ this.handshakes.set(nodeId, handshake);
1484
+ this.schedulePersist();
1485
+ logger2.debug("[SyncProtocol] Handshake processed", {
1486
+ nodeId,
1487
+ protocolVersion: handshake.protocolVersion,
1488
+ capabilities: handshake.capabilities.length
1489
+ });
1490
+ return handshake;
1491
+ }
1492
+ getMessage(messageId) {
1493
+ return this.messageMap.get(messageId);
1494
+ }
1495
+ getAllMessages() {
1496
+ return [...this.messageQueue];
1497
+ }
1498
+ getMessagesByType(type) {
1499
+ return this.messageQueue.filter((m) => m.type === type);
1500
+ }
1501
+ getMessagesFromSender(sender) {
1502
+ return this.messageQueue.filter((m) => m.sender === sender);
1503
+ }
1504
+ getPendingMessages(receiver) {
1505
+ return this.messageQueue.filter((m) => m.receiver === receiver);
1506
+ }
1507
+ getHandshakes() {
1508
+ return new Map(this.handshakes);
1509
+ }
1510
+ getStatistics() {
1511
+ const messagesByType = {};
1512
+ for (const message of this.messageQueue) {
1513
+ messagesByType[message.type] = (messagesByType[message.type] || 0) + 1;
1514
+ }
1515
+ const errorCount = this.protocolErrors.length;
1516
+ const recoverableErrors = this.protocolErrors.filter((e) => e.error.recoverable).length;
1517
+ return {
1518
+ totalMessages: this.messageQueue.length,
1519
+ messagesByType,
1520
+ totalHandshakes: this.handshakes.size,
1521
+ totalErrors: errorCount,
1522
+ recoverableErrors,
1523
+ unrecoverableErrors: errorCount - recoverableErrors
1524
+ };
1525
+ }
1526
+ getErrors() {
1527
+ return [...this.protocolErrors];
1528
+ }
1529
+ async saveToPersistence() {
1530
+ if (!this.persistence) {
1531
+ return;
1532
+ }
1533
+ const data = {
1534
+ protocolVersion: this.version,
1535
+ messageCounter: this.messageCounter,
1536
+ messageQueue: this.getAllMessages(),
1537
+ handshakes: Array.from(this.handshakes.entries()).map(([nodeId, handshake]) => ({
1538
+ nodeId,
1539
+ handshake
1540
+ })),
1541
+ protocolErrors: this.getErrors()
1542
+ };
1543
+ const envelope = {
1544
+ version: 1,
1545
+ updatedAt: Date.now(),
1546
+ data
1547
+ };
1548
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
1549
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
1550
+ }
1551
+ async loadFromPersistence() {
1552
+ if (!this.persistence) {
1553
+ return { messages: 0, handshakes: 0, errors: 0 };
1554
+ }
1555
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
1556
+ if (!raw) {
1557
+ return { messages: 0, handshakes: 0, errors: 0 };
1558
+ }
1559
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
1560
+ const envelope = deserialize(raw);
1561
+ if (envelope.version !== 1 || !envelope.data) {
1562
+ throw new Error("Invalid sync protocol persistence payload");
1563
+ }
1564
+ if (!Array.isArray(envelope.data.messageQueue) || !Array.isArray(envelope.data.handshakes) || !Array.isArray(envelope.data.protocolErrors)) {
1565
+ throw new Error("Invalid sync protocol persistence structure");
1566
+ }
1567
+ const nextMessages = [];
1568
+ for (const message of envelope.data.messageQueue) {
1569
+ const validation = this.validateMessage(message);
1570
+ if (!validation.valid) {
1571
+ throw new Error(`Invalid persisted message ${message?.messageId ?? "unknown"}: ${validation.errors.join(", ")}`);
1572
+ }
1573
+ nextMessages.push(message);
1574
+ }
1575
+ const nextHandshakes = /* @__PURE__ */ new Map;
1576
+ for (const entry of envelope.data.handshakes) {
1577
+ if (typeof entry.nodeId !== "string" || !this.isValidHandshake(entry.handshake)) {
1578
+ throw new Error("Invalid persisted handshake payload");
1579
+ }
1580
+ nextHandshakes.set(entry.nodeId, entry.handshake);
1581
+ }
1582
+ const nextErrors = [];
1583
+ for (const entry of envelope.data.protocolErrors) {
1584
+ if (!this.isValidProtocolErrorEntry(entry)) {
1585
+ throw new Error("Invalid persisted protocol error payload");
1586
+ }
1587
+ nextErrors.push(entry);
1588
+ }
1589
+ this.messageQueue = nextMessages;
1590
+ this.messageMap = new Map(nextMessages.map((m) => [m.messageId, m]));
1591
+ this.handshakes = nextHandshakes;
1592
+ this.protocolErrors = nextErrors;
1593
+ this.messageCounter = Math.max(envelope.data.messageCounter || 0, this.messageQueue.length);
1594
+ logger2.debug("[SyncProtocol] Loaded from persistence", {
1595
+ key: this.persistence.key,
1596
+ messages: this.messageQueue.length,
1597
+ handshakes: this.handshakes.size,
1598
+ errors: this.protocolErrors.length
1599
+ });
1600
+ return {
1601
+ messages: this.messageQueue.length,
1602
+ handshakes: this.handshakes.size,
1603
+ errors: this.protocolErrors.length
1604
+ };
1605
+ }
1606
+ async clearPersistence() {
1607
+ if (!this.persistence) {
1608
+ return;
1609
+ }
1610
+ await this.persistence.adapter.removeItem(this.persistence.key);
1611
+ }
1612
+ schedulePersist() {
1613
+ if (!this.persistence || this.persistence.autoPersist === false) {
1614
+ return;
1615
+ }
1616
+ if (this.persistTimer) {
1617
+ clearTimeout(this.persistTimer);
1618
+ }
1619
+ this.persistTimer = setTimeout(() => {
1620
+ this.persistSafely();
1621
+ }, this.persistence.persistDebounceMs ?? 25);
1622
+ }
1623
+ async persistSafely() {
1624
+ if (!this.persistence) {
1625
+ return;
1626
+ }
1627
+ if (this.persistInFlight) {
1628
+ this.persistPending = true;
1629
+ return;
1630
+ }
1631
+ this.persistInFlight = true;
1632
+ try {
1633
+ await this.saveToPersistence();
1634
+ } catch (error) {
1635
+ logger2.error("[SyncProtocol] Persistence write failed", {
1636
+ key: this.persistence.key,
1637
+ error: error instanceof Error ? error.message : String(error)
1638
+ });
1639
+ } finally {
1640
+ this.persistInFlight = false;
1641
+ const shouldRunAgain = this.persistPending;
1642
+ this.persistPending = false;
1643
+ if (shouldRunAgain) {
1644
+ this.persistSafely();
1645
+ }
1646
+ }
1647
+ }
1648
+ isValidHandshake(value) {
1649
+ if (typeof value !== "object" || value === null) {
1650
+ return false;
1651
+ }
1652
+ const handshake = value;
1653
+ const validState = handshake.state === "initiating" || handshake.state === "responding" || handshake.state === "completed";
1654
+ return typeof handshake.protocolVersion === "string" && typeof handshake.nodeId === "string" && Array.isArray(handshake.capabilities) && handshake.capabilities.every((cap) => typeof cap === "string") && validState;
1655
+ }
1656
+ isValidProtocolErrorEntry(entry) {
1657
+ if (typeof entry !== "object" || entry === null) {
1658
+ return false;
1659
+ }
1660
+ const candidate = entry;
1661
+ return typeof candidate.timestamp === "string" && typeof candidate.error?.code === "string" && typeof candidate.error.message === "string" && typeof candidate.error.recoverable === "boolean";
1662
+ }
1663
+ generateMessageId() {
1664
+ this.messageCounter++;
1665
+ return `msg-${Date.now()}-${this.messageCounter}`;
1666
+ }
1667
+ clear() {
1668
+ this.messageQueue = [];
1669
+ this.messageMap.clear();
1670
+ this.handshakes.clear();
1671
+ this.protocolErrors = [];
1672
+ this.messageCounter = 0;
1673
+ this.cryptoProvider = null;
1674
+ this.cryptoConfig = null;
1675
+ this.schedulePersist();
1676
+ }
1677
+ getCryptoProvider() {
1678
+ return this.cryptoProvider;
1679
+ }
1680
+ };
1681
+
1682
+ // ../aeon/dist/persistence/index.js
1683
+ var DEFAULT_RULE = {
1684
+ urgency: "deferred",
1685
+ debounce: 50,
1686
+ maxBufferSize: 5000,
1687
+ readThrough: true
1688
+ };
1689
+ var DashStorageAdapter = class {
1690
+ backend;
1691
+ syncClient;
1692
+ rules;
1693
+ hooks;
1694
+ pendingChanges = /* @__PURE__ */ new Map;
1695
+ syncTimer = null;
1696
+ syncInFlight = false;
1697
+ syncPending = false;
1698
+ constructor(backend, options = {}) {
1699
+ this.backend = backend;
1700
+ this.syncClient = options.syncClient ?? null;
1701
+ this.hooks = options.hooks ?? {};
1702
+ const defaultRule = {
1703
+ ...DEFAULT_RULE,
1704
+ ...options.rules?.default ?? {}
1705
+ };
1706
+ if (options.syncDebounceMs !== undefined)
1707
+ defaultRule.debounce = options.syncDebounceMs;
1708
+ if (options.maxPendingChanges !== undefined)
1709
+ defaultRule.maxBufferSize = options.maxPendingChanges;
1710
+ if (options.onSyncError && !this.hooks.onSyncError)
1711
+ this.hooks.onSyncError = options.onSyncError;
1712
+ this.rules = {
1713
+ default: defaultRule,
1714
+ prefixes: options.rules?.prefixes ?? {}
1715
+ };
1716
+ }
1717
+ async getItem(key) {
1718
+ const rule = this.getRuleForKey(key);
1719
+ if (rule.readThrough !== false) {
1720
+ const pending = this.pendingChanges.get(key);
1721
+ if (pending) {
1722
+ return pending.operation === "delete" ? null : pending.value ?? null;
1723
+ }
1724
+ }
1725
+ return await this.backend.get(key);
1726
+ }
1727
+ async setItem(key, value) {
1728
+ await this.backend.set(key, value);
1729
+ this.trackChange({
1730
+ key,
1731
+ operation: "set",
1732
+ value,
1733
+ timestamp: Date.now()
1734
+ });
1735
+ }
1736
+ async removeItem(key) {
1737
+ await this.backend.delete(key);
1738
+ this.trackChange({
1739
+ key,
1740
+ operation: "delete",
1741
+ timestamp: Date.now()
1742
+ });
1743
+ }
1744
+ getPendingSyncCount() {
1745
+ return this.pendingChanges.size;
1746
+ }
1747
+ async flushSync() {
1748
+ if (!this.syncClient || this.pendingChanges.size === 0) {
1749
+ return;
1750
+ }
1751
+ if (this.syncTimer) {
1752
+ clearTimeout(this.syncTimer);
1753
+ this.syncTimer = null;
1754
+ }
1755
+ await this.performSync();
1756
+ }
1757
+ trackChange(change) {
1758
+ this.pendingChanges.set(change.key, change);
1759
+ const rule = this.getRuleForKey(change.key);
1760
+ if (rule.urgency === "realtime") {
1761
+ this.performSync();
1762
+ return;
1763
+ }
1764
+ const maxSize = rule.maxBufferSize ?? 5000;
1765
+ if (this.pendingChanges.size >= maxSize) {
1766
+ this.hooks.onBufferOverflow?.(this.getPrefixMatch(change.key) || "default", this.pendingChanges.size, maxSize);
1767
+ this.performSync();
1768
+ return;
1769
+ }
1770
+ this.scheduleSync(rule);
1771
+ }
1772
+ getRuleForKey(key) {
1773
+ const prefix = this.getPrefixMatch(key);
1774
+ return (prefix ? this.rules.prefixes?.[prefix] : this.rules.default) ?? this.rules.default;
1775
+ }
1776
+ getPrefixMatch(key) {
1777
+ if (!this.rules.prefixes)
1778
+ return null;
1779
+ const prefixes = Object.keys(this.rules.prefixes).sort((a, b) => b.length - a.length);
1780
+ return prefixes.find((p) => key.startsWith(p)) ?? null;
1781
+ }
1782
+ scheduleSync(rule) {
1783
+ if (!this.syncClient || this.syncTimer) {
1784
+ return;
1785
+ }
1786
+ const debounceMs = typeof rule.debounce === "string" ? this.parseInterval(rule.debounce) : rule.debounce ?? 50;
1787
+ this.syncTimer = setTimeout(() => {
1788
+ this.syncTimer = null;
1789
+ this.performSync();
1790
+ }, debounceMs);
1791
+ }
1792
+ async performSync() {
1793
+ if (!this.syncClient) {
1794
+ return;
1795
+ }
1796
+ if (this.syncInFlight) {
1797
+ this.syncPending = true;
1798
+ return;
1799
+ }
1800
+ const changes = Array.from(this.pendingChanges.values()).sort((a, b) => a.timestamp - b.timestamp);
1801
+ if (changes.length === 0) {
1802
+ return;
1803
+ }
1804
+ this.pendingChanges.clear();
1805
+ this.syncInFlight = true;
1806
+ try {
1807
+ await this.syncClient.syncChanges(changes);
1808
+ this.hooks.onSync?.(changes);
1809
+ this.hooks.onFlush?.(changes.length);
1810
+ } catch (error) {
1811
+ for (const change of changes) {
1812
+ const current = this.pendingChanges.get(change.key);
1813
+ if (!current || change.timestamp > current.timestamp) {
1814
+ this.pendingChanges.set(change.key, change);
1815
+ }
1816
+ }
1817
+ if (this.hooks.onSyncError) {
1818
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1819
+ this.hooks.onSyncError(normalizedError, changes);
1820
+ }
1821
+ } finally {
1822
+ this.syncInFlight = false;
1823
+ const rerun = this.syncPending || this.pendingChanges.size > 0;
1824
+ this.syncPending = false;
1825
+ if (rerun) {
1826
+ this.scheduleSync(this.rules.default);
1827
+ }
1828
+ }
1829
+ }
1830
+ parseInterval(input) {
1831
+ const match = input.match(/^(\d+)(ms|s|m|h|d)$/);
1832
+ if (!match)
1833
+ return 50;
1834
+ const value = parseInt(match[1], 10);
1835
+ const unit = match[2];
1836
+ switch (unit) {
1837
+ case "ms":
1838
+ return value;
1839
+ case "s":
1840
+ return value * 1000;
1841
+ case "m":
1842
+ return value * 60 * 1000;
1843
+ case "h":
1844
+ return value * 60 * 60 * 1000;
1845
+ case "d":
1846
+ return value * 24 * 60 * 60 * 1000;
1847
+ default:
1848
+ return 50;
1849
+ }
1850
+ }
1851
+ };
1852
+ var InMemoryStorageAdapter = class {
1853
+ store = /* @__PURE__ */ new Map;
1854
+ getItem(key) {
1855
+ return this.store.get(key) ?? null;
1856
+ }
1857
+ setItem(key, value) {
1858
+ this.store.set(key, value);
1859
+ }
1860
+ removeItem(key) {
1861
+ this.store.delete(key);
1862
+ }
1863
+ async flushSync() {}
1864
+ clear() {
1865
+ this.store.clear();
1866
+ }
1867
+ };
1868
+
1869
+ // src/sync/AeonDurableSync.ts
1870
+ function createBrowserStorageBackend() {
1871
+ return {
1872
+ get(key) {
1873
+ if (typeof localStorage === "undefined") {
1874
+ return null;
1875
+ }
1876
+ try {
1877
+ return localStorage.getItem(key);
1878
+ } catch {
1879
+ return null;
1880
+ }
1881
+ },
1882
+ set(key, value) {
1883
+ if (typeof localStorage === "undefined") {
1884
+ return;
1885
+ }
1886
+ try {
1887
+ localStorage.setItem(key, value);
1888
+ } catch {}
1889
+ },
1890
+ delete(key) {
1891
+ if (typeof localStorage === "undefined") {
1892
+ return;
1893
+ }
1894
+ try {
1895
+ localStorage.removeItem(key);
1896
+ } catch {}
1897
+ }
1898
+ };
1899
+ }
1900
+
1901
+ class AeonDurableSyncRuntime {
1902
+ adapter;
1903
+ queue;
1904
+ protocol;
1905
+ replication;
1906
+ constructor(config) {
1907
+ const prefix = config.persistenceKeyPrefix ?? `dash:aeon:${config.roomName}`;
1908
+ this.adapter = config.enabled === false ? new InMemoryStorageAdapter : new DashStorageAdapter(config.storageBackend ?? createBrowserStorageBackend(), config.storageOptions ?? {});
1909
+ this.queue = new OfflineOperationQueue({
1910
+ persistence: {
1911
+ adapter: this.adapter,
1912
+ key: `${prefix}:offline`,
1913
+ autoPersist: true,
1914
+ autoLoad: true
1915
+ }
1916
+ });
1917
+ this.protocol = new SyncProtocol({
1918
+ persistence: {
1919
+ adapter: this.adapter,
1920
+ key: `${prefix}:protocol`,
1921
+ autoPersist: true,
1922
+ autoLoad: true
1923
+ }
1924
+ });
1925
+ this.replication = new ReplicationManager({
1926
+ persistence: {
1927
+ adapter: this.adapter,
1928
+ key: `${prefix}:replication`,
1929
+ autoPersist: true,
1930
+ autoLoad: true
1931
+ }
1932
+ });
1933
+ }
1934
+ async initialize() {
1935
+ try {
1936
+ await Promise.all([
1937
+ this.queue.loadFromPersistence(),
1938
+ this.protocol.loadFromPersistence(),
1939
+ this.replication.loadFromPersistence()
1940
+ ]);
1941
+ } catch {}
1942
+ }
1943
+ async flushSync() {
1944
+ if ("flushSync" in this.adapter) {
1945
+ await this.adapter.flushSync();
1946
+ }
1947
+ }
1948
+ recordTransportSelection(roomName, transport, relay) {
1949
+ if (!transport) {
1950
+ return;
1951
+ }
1952
+ try {
1953
+ this.queue.enqueue("update", {
1954
+ roomName,
1955
+ event: "transport-selected",
1956
+ transport,
1957
+ relay,
1958
+ ts: Date.now()
1959
+ }, roomName, "normal");
1960
+ this.protocol.createSyncRequestMessage(roomName, relay ?? "unknown-relay", `transport-${Date.now()}`, "0.0.0", "1.1.0", {
1961
+ transport,
1962
+ relay
1963
+ });
1964
+ } catch {}
1965
+ }
1966
+ recordDiscovery(roomName, relays) {
1967
+ const sanitizedRelays = relays.filter((relay) => typeof relay === "string" && relay.length > 0).slice(0, 32);
1968
+ try {
1969
+ this.queue.enqueue("sync", {
1970
+ roomName,
1971
+ event: "relay-discovery",
1972
+ count: sanitizedRelays.length,
1973
+ relays: sanitizedRelays,
1974
+ ts: Date.now()
1975
+ }, roomName, "low");
1976
+ } catch {}
1977
+ }
1978
+ getPendingOperations() {
1979
+ return this.queue.getPendingCount();
1980
+ }
1981
+ }
1982
+ export {
1983
+ AeonDurableSyncRuntime
1984
+ };