@affectively/aeon 1.0.0 → 1.2.0

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 (62) hide show
  1. package/README.md +10 -0
  2. package/dist/compression/index.cjs +580 -0
  3. package/dist/compression/index.cjs.map +1 -0
  4. package/dist/compression/index.d.cts +189 -0
  5. package/dist/compression/index.d.ts +189 -0
  6. package/dist/compression/index.js +573 -0
  7. package/dist/compression/index.js.map +1 -0
  8. package/dist/core/index.d.cts +70 -5
  9. package/dist/core/index.d.ts +70 -5
  10. package/dist/crypto/index.cjs +100 -0
  11. package/dist/crypto/index.cjs.map +1 -0
  12. package/dist/crypto/index.d.cts +407 -0
  13. package/dist/crypto/index.d.ts +407 -0
  14. package/dist/crypto/index.js +96 -0
  15. package/dist/crypto/index.js.map +1 -0
  16. package/dist/distributed/index.cjs +420 -23
  17. package/dist/distributed/index.cjs.map +1 -1
  18. package/dist/distributed/index.d.cts +901 -2
  19. package/dist/distributed/index.d.ts +901 -2
  20. package/dist/distributed/index.js +420 -23
  21. package/dist/distributed/index.js.map +1 -1
  22. package/dist/index.cjs +1222 -55
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +11 -811
  25. package/dist/index.d.ts +11 -811
  26. package/dist/index.js +1221 -56
  27. package/dist/index.js.map +1 -1
  28. package/dist/offline/index.cjs +419 -0
  29. package/dist/offline/index.cjs.map +1 -0
  30. package/dist/offline/index.d.cts +148 -0
  31. package/dist/offline/index.d.ts +148 -0
  32. package/dist/offline/index.js +415 -0
  33. package/dist/offline/index.js.map +1 -0
  34. package/dist/optimization/index.cjs +797 -0
  35. package/dist/optimization/index.cjs.map +1 -0
  36. package/dist/optimization/index.d.cts +347 -0
  37. package/dist/optimization/index.d.ts +347 -0
  38. package/dist/optimization/index.js +787 -0
  39. package/dist/optimization/index.js.map +1 -0
  40. package/dist/persistence/index.cjs +145 -0
  41. package/dist/persistence/index.cjs.map +1 -0
  42. package/dist/persistence/index.d.cts +63 -0
  43. package/dist/persistence/index.d.ts +63 -0
  44. package/dist/persistence/index.js +142 -0
  45. package/dist/persistence/index.js.map +1 -0
  46. package/dist/presence/index.cjs +489 -0
  47. package/dist/presence/index.cjs.map +1 -0
  48. package/dist/presence/index.d.cts +283 -0
  49. package/dist/presence/index.d.ts +283 -0
  50. package/dist/presence/index.js +485 -0
  51. package/dist/presence/index.js.map +1 -0
  52. package/dist/types-CMxO7QF0.d.cts +33 -0
  53. package/dist/types-CMxO7QF0.d.ts +33 -0
  54. package/dist/versioning/index.cjs +296 -14
  55. package/dist/versioning/index.cjs.map +1 -1
  56. package/dist/versioning/index.d.cts +66 -1
  57. package/dist/versioning/index.d.ts +66 -1
  58. package/dist/versioning/index.js +296 -14
  59. package/dist/versioning/index.js.map +1 -1
  60. package/package.json +51 -1
  61. package/dist/index-C_4CMV5c.d.cts +0 -1207
  62. package/dist/index-C_4CMV5c.d.ts +0 -1207
package/dist/index.js CHANGED
@@ -54,6 +54,145 @@ var logger = {
54
54
  error: (...args) => getLogger().error(...args)
55
55
  };
56
56
 
57
+ // src/persistence/DashStorageAdapter.ts
58
+ var DashStorageAdapter = class {
59
+ backend;
60
+ syncClient;
61
+ syncDebounceMs;
62
+ maxPendingChanges;
63
+ onSyncError;
64
+ pendingChanges = /* @__PURE__ */ new Map();
65
+ syncTimer = null;
66
+ syncInFlight = false;
67
+ syncPending = false;
68
+ constructor(backend, options = {}) {
69
+ this.backend = backend;
70
+ this.syncClient = options.syncClient ?? null;
71
+ this.syncDebounceMs = options.syncDebounceMs ?? 50;
72
+ this.maxPendingChanges = options.maxPendingChanges ?? 5e3;
73
+ this.onSyncError = options.onSyncError ?? null;
74
+ }
75
+ async getItem(key) {
76
+ return await this.backend.get(key);
77
+ }
78
+ async setItem(key, value) {
79
+ await this.backend.set(key, value);
80
+ this.trackChange({
81
+ key,
82
+ operation: "set",
83
+ value,
84
+ timestamp: Date.now()
85
+ });
86
+ }
87
+ async removeItem(key) {
88
+ await this.backend.delete(key);
89
+ this.trackChange({
90
+ key,
91
+ operation: "delete",
92
+ timestamp: Date.now()
93
+ });
94
+ }
95
+ getPendingSyncCount() {
96
+ return this.pendingChanges.size;
97
+ }
98
+ async flushSync() {
99
+ if (!this.syncClient || this.pendingChanges.size === 0) {
100
+ return;
101
+ }
102
+ if (this.syncTimer) {
103
+ clearTimeout(this.syncTimer);
104
+ this.syncTimer = null;
105
+ }
106
+ await this.performSync();
107
+ }
108
+ trackChange(change) {
109
+ this.pendingChanges.set(change.key, change);
110
+ this.enforcePendingLimit();
111
+ this.scheduleSync();
112
+ }
113
+ enforcePendingLimit() {
114
+ if (this.pendingChanges.size <= this.maxPendingChanges) {
115
+ return;
116
+ }
117
+ const sorted = Array.from(this.pendingChanges.values()).sort(
118
+ (a, b) => a.timestamp - b.timestamp
119
+ );
120
+ const overflow = this.pendingChanges.size - this.maxPendingChanges;
121
+ for (let i = 0; i < overflow; i++) {
122
+ const toDrop = sorted[i];
123
+ if (toDrop) {
124
+ this.pendingChanges.delete(toDrop.key);
125
+ }
126
+ }
127
+ }
128
+ scheduleSync() {
129
+ if (!this.syncClient) {
130
+ return;
131
+ }
132
+ if (this.syncTimer) {
133
+ clearTimeout(this.syncTimer);
134
+ }
135
+ this.syncTimer = setTimeout(() => {
136
+ void this.performSync();
137
+ }, this.syncDebounceMs);
138
+ }
139
+ async performSync() {
140
+ if (!this.syncClient) {
141
+ return;
142
+ }
143
+ if (this.syncInFlight) {
144
+ this.syncPending = true;
145
+ return;
146
+ }
147
+ const changes = Array.from(this.pendingChanges.values()).sort(
148
+ (a, b) => a.timestamp - b.timestamp
149
+ );
150
+ if (changes.length === 0) {
151
+ return;
152
+ }
153
+ this.pendingChanges.clear();
154
+ this.syncInFlight = true;
155
+ try {
156
+ await this.syncClient.syncChanges(changes);
157
+ } catch (error) {
158
+ for (const change of changes) {
159
+ const current = this.pendingChanges.get(change.key);
160
+ if (!current || change.timestamp > current.timestamp) {
161
+ this.pendingChanges.set(change.key, change);
162
+ }
163
+ }
164
+ if (this.onSyncError) {
165
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
166
+ this.onSyncError(normalizedError, changes);
167
+ }
168
+ } finally {
169
+ this.syncInFlight = false;
170
+ const rerun = this.syncPending || this.pendingChanges.size > 0;
171
+ this.syncPending = false;
172
+ if (rerun) {
173
+ this.scheduleSync();
174
+ }
175
+ }
176
+ }
177
+ };
178
+
179
+ // src/persistence/InMemoryStorageAdapter.ts
180
+ var InMemoryStorageAdapter = class {
181
+ store = /* @__PURE__ */ new Map();
182
+ getItem(key) {
183
+ return this.store.get(key) ?? null;
184
+ }
185
+ setItem(key, value) {
186
+ this.store.set(key, value);
187
+ }
188
+ removeItem(key) {
189
+ this.store.delete(key);
190
+ }
191
+ clear() {
192
+ this.store.clear();
193
+ }
194
+ };
195
+
57
196
  // src/versioning/SchemaVersionManager.ts
58
197
  var SchemaVersionManager = class {
59
198
  versions = /* @__PURE__ */ new Map();
@@ -105,7 +244,9 @@ var SchemaVersionManager = class {
105
244
  */
106
245
  setCurrentVersion(version) {
107
246
  if (!this.versions.has(this.versionToString(version))) {
108
- throw new Error(`Version ${this.versionToString(version)} not registered`);
247
+ throw new Error(
248
+ `Version ${this.versionToString(version)} not registered`
249
+ );
109
250
  }
110
251
  this.currentVersion = version;
111
252
  logger.debug("[SchemaVersionManager] Current version set", {
@@ -391,7 +532,9 @@ var MigrationEngine = class {
391
532
  id: migrationId,
392
533
  error: result.errors[0]
393
534
  });
394
- throw new Error(`Rollback for ${migrationId} failed: ${result.errors[0]}`);
535
+ throw new Error(
536
+ `Rollback for ${migrationId} failed: ${result.errors[0]}`
537
+ );
395
538
  }
396
539
  }
397
540
  /**
@@ -444,8 +587,14 @@ var MigrationEngine = class {
444
587
  getStatistics() {
445
588
  const successful = this.executedMigrations.filter((m) => m.success).length;
446
589
  const failed = this.executedMigrations.filter((m) => !m.success).length;
447
- const totalDuration = this.executedMigrations.reduce((sum, m) => sum + m.duration, 0);
448
- const totalAffected = this.executedMigrations.reduce((sum, m) => sum + m.itemsAffected, 0);
590
+ const totalDuration = this.executedMigrations.reduce(
591
+ (sum, m) => sum + m.duration,
592
+ 0
593
+ );
594
+ const totalAffected = this.executedMigrations.reduce(
595
+ (sum, m) => sum + m.itemsAffected,
596
+ 0
597
+ );
449
598
  return {
450
599
  totalExecuted: this.executedMigrations.length,
451
600
  successful,
@@ -499,7 +648,9 @@ var DataTransformer = class {
499
648
  return rule.transformer(value);
500
649
  } catch (error) {
501
650
  if (rule.required) {
502
- throw new Error(`Failed to transform required field ${field}: ${error instanceof Error ? error.message : String(error)}`);
651
+ throw new Error(
652
+ `Failed to transform required field ${field}: ${error instanceof Error ? error.message : String(error)}`
653
+ );
503
654
  }
504
655
  return rule.defaultValue !== void 0 ? rule.defaultValue : value;
505
656
  }
@@ -572,7 +723,9 @@ var DataTransformer = class {
572
723
  validateTransformation(original, transformed) {
573
724
  const issues = [];
574
725
  if (original.length !== transformed.length) {
575
- issues.push(`Item count mismatch: ${original.length} -> ${transformed.length}`);
726
+ issues.push(
727
+ `Item count mismatch: ${original.length} -> ${transformed.length}`
728
+ );
576
729
  }
577
730
  for (let i = 0; i < Math.min(original.length, transformed.length); i++) {
578
731
  const orig = original[i];
@@ -663,14 +816,40 @@ var DataTransformer = class {
663
816
  };
664
817
 
665
818
  // src/versioning/MigrationTracker.ts
666
- var MigrationTracker = class {
819
+ var MigrationTracker = class _MigrationTracker {
820
+ static DEFAULT_PERSIST_KEY = "aeon:migration-tracker:v1";
821
+ static INTEGRITY_ROOT = "aeon:migration-integrity-root:v1";
667
822
  migrations = [];
668
823
  snapshots = /* @__PURE__ */ new Map();
824
+ persistence = null;
825
+ persistTimer = null;
826
+ persistInFlight = false;
827
+ persistPending = false;
828
+ constructor(options) {
829
+ if (options?.persistence) {
830
+ this.persistence = {
831
+ ...options.persistence,
832
+ key: options.persistence.key ?? _MigrationTracker.DEFAULT_PERSIST_KEY,
833
+ autoPersist: options.persistence.autoPersist ?? true,
834
+ autoLoad: options.persistence.autoLoad ?? false,
835
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
836
+ };
837
+ }
838
+ if (this.persistence?.autoLoad) {
839
+ void this.loadFromPersistence().catch((error) => {
840
+ logger.error("[MigrationTracker] Failed to load persistence", {
841
+ key: this.persistence?.key,
842
+ error: error instanceof Error ? error.message : String(error)
843
+ });
844
+ });
845
+ }
846
+ }
669
847
  /**
670
848
  * Track a new migration
671
849
  */
672
850
  recordMigration(record) {
673
851
  this.migrations.push({ ...record });
852
+ this.schedulePersist();
674
853
  logger.debug("[MigrationTracker] Migration recorded", {
675
854
  id: record.id,
676
855
  migrationId: record.migrationId,
@@ -727,7 +906,9 @@ var MigrationTracker = class {
727
906
  * Check if can rollback
728
907
  */
729
908
  canRollback(fromVersion, toVersion) {
730
- const fromIndex = this.migrations.findIndex((m) => m.version === fromVersion);
909
+ const fromIndex = this.migrations.findIndex(
910
+ (m) => m.version === fromVersion
911
+ );
731
912
  const toIndex = this.migrations.findIndex((m) => m.version === toVersion);
732
913
  if (fromIndex === -1 || toIndex === -1) {
733
914
  return false;
@@ -751,7 +932,9 @@ var MigrationTracker = class {
751
932
  const affectedVersions = [];
752
933
  let estimatedDuration = 0;
753
934
  if (canRollback) {
754
- const fromIndex = this.migrations.findIndex((m) => m.version === fromVersion);
935
+ const fromIndex = this.migrations.findIndex(
936
+ (m) => m.version === fromVersion
937
+ );
755
938
  const toIndex = this.migrations.findIndex((m) => m.version === toVersion);
756
939
  for (let i = fromIndex; i > toIndex; i--) {
757
940
  const migration = this.migrations[i];
@@ -807,12 +990,24 @@ var MigrationTracker = class {
807
990
  * Get migration statistics
808
991
  */
809
992
  getStatistics() {
810
- const applied = this.migrations.filter((m) => m.status === "applied").length;
993
+ const applied = this.migrations.filter(
994
+ (m) => m.status === "applied"
995
+ ).length;
811
996
  const failed = this.migrations.filter((m) => m.status === "failed").length;
812
- const pending = this.migrations.filter((m) => m.status === "pending").length;
813
- const rolledBack = this.migrations.filter((m) => m.status === "rolled-back").length;
814
- const totalDuration = this.migrations.reduce((sum, m) => sum + m.duration, 0);
815
- const totalAffected = this.migrations.reduce((sum, m) => sum + m.itemsAffected, 0);
997
+ const pending = this.migrations.filter(
998
+ (m) => m.status === "pending"
999
+ ).length;
1000
+ const rolledBack = this.migrations.filter(
1001
+ (m) => m.status === "rolled-back"
1002
+ ).length;
1003
+ const totalDuration = this.migrations.reduce(
1004
+ (sum, m) => sum + m.duration,
1005
+ 0
1006
+ );
1007
+ const totalAffected = this.migrations.reduce(
1008
+ (sum, m) => sum + m.itemsAffected,
1009
+ 0
1010
+ );
816
1011
  return {
817
1012
  total: this.migrations.length,
818
1013
  applied,
@@ -863,7 +1058,147 @@ var MigrationTracker = class {
863
1058
  status,
864
1059
  hasError: !!error
865
1060
  });
1061
+ this.schedulePersist();
1062
+ }
1063
+ }
1064
+ /**
1065
+ * Persist tracker state with integrity chain verification metadata.
1066
+ */
1067
+ async saveToPersistence() {
1068
+ if (!this.persistence) {
1069
+ return;
1070
+ }
1071
+ const normalizedMigrations = this.migrations.map((migration) => ({
1072
+ ...migration,
1073
+ previousHash: void 0,
1074
+ integrityHash: void 0
1075
+ }));
1076
+ const integrityEntries = [];
1077
+ let previousHash = _MigrationTracker.INTEGRITY_ROOT;
1078
+ for (const migration of normalizedMigrations) {
1079
+ const hash = await this.computeDigestHex(
1080
+ `${previousHash}|${this.stableStringify(migration)}`
1081
+ );
1082
+ integrityEntries.push({
1083
+ recordId: migration.id,
1084
+ previousHash,
1085
+ hash
1086
+ });
1087
+ previousHash = hash;
1088
+ }
1089
+ const persistedMigrations = normalizedMigrations.map((migration, index) => ({
1090
+ ...migration,
1091
+ previousHash: integrityEntries[index]?.previousHash,
1092
+ integrityHash: integrityEntries[index]?.hash
1093
+ }));
1094
+ const data = {
1095
+ migrations: persistedMigrations,
1096
+ snapshots: Array.from(this.snapshots.entries()).map(
1097
+ ([recordId, snapshot]) => ({
1098
+ recordId,
1099
+ beforeHash: snapshot.beforeHash,
1100
+ afterHash: snapshot.afterHash,
1101
+ itemCount: snapshot.itemCount
1102
+ })
1103
+ ),
1104
+ integrity: {
1105
+ algorithm: "sha256-chain-v1",
1106
+ entries: integrityEntries,
1107
+ rootHash: previousHash
1108
+ }
1109
+ };
1110
+ const envelope = {
1111
+ version: 1,
1112
+ updatedAt: Date.now(),
1113
+ data
1114
+ };
1115
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
1116
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
1117
+ }
1118
+ /**
1119
+ * Load tracker state and verify integrity chain.
1120
+ */
1121
+ async loadFromPersistence() {
1122
+ if (!this.persistence) {
1123
+ return { migrations: 0, snapshots: 0 };
1124
+ }
1125
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
1126
+ if (!raw) {
1127
+ return { migrations: 0, snapshots: 0 };
1128
+ }
1129
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
1130
+ const envelope = deserialize(raw);
1131
+ if (envelope.version !== 1 || !envelope.data) {
1132
+ throw new Error("Invalid migration tracker persistence payload");
1133
+ }
1134
+ if (!Array.isArray(envelope.data.migrations) || !Array.isArray(envelope.data.snapshots) || !envelope.data.integrity || !Array.isArray(envelope.data.integrity.entries) || typeof envelope.data.integrity.rootHash !== "string") {
1135
+ throw new Error("Invalid migration tracker persistence structure");
1136
+ }
1137
+ if (envelope.data.integrity.algorithm !== "sha256-chain-v1") {
1138
+ throw new Error("Unsupported migration integrity algorithm");
1139
+ }
1140
+ if (envelope.data.integrity.entries.length !== envelope.data.migrations.length) {
1141
+ throw new Error("Migration integrity entry count mismatch");
1142
+ }
1143
+ const validatedMigrations = [];
1144
+ let previousHash = _MigrationTracker.INTEGRITY_ROOT;
1145
+ for (let i = 0; i < envelope.data.migrations.length; i++) {
1146
+ const migration = envelope.data.migrations[i];
1147
+ const integrity = envelope.data.integrity.entries[i];
1148
+ if (!this.isValidMigrationRecord(migration)) {
1149
+ throw new Error("Invalid persisted migration record");
1150
+ }
1151
+ if (!integrity || integrity.recordId !== migration.id || integrity.previousHash !== previousHash) {
1152
+ throw new Error("Migration integrity chain mismatch");
1153
+ }
1154
+ const expectedHash = await this.computeDigestHex(
1155
+ `${previousHash}|${this.stableStringify({
1156
+ ...migration,
1157
+ previousHash: void 0,
1158
+ integrityHash: void 0
1159
+ })}`
1160
+ );
1161
+ if (expectedHash !== integrity.hash) {
1162
+ throw new Error("Migration integrity verification failed");
1163
+ }
1164
+ validatedMigrations.push({
1165
+ ...migration,
1166
+ previousHash: integrity.previousHash,
1167
+ integrityHash: integrity.hash
1168
+ });
1169
+ previousHash = expectedHash;
1170
+ }
1171
+ if (previousHash !== envelope.data.integrity.rootHash) {
1172
+ throw new Error("Migration integrity root hash mismatch");
1173
+ }
1174
+ const validatedSnapshots = /* @__PURE__ */ new Map();
1175
+ for (const snapshot of envelope.data.snapshots) {
1176
+ if (typeof snapshot.recordId !== "string" || typeof snapshot.beforeHash !== "string" || typeof snapshot.afterHash !== "string" || typeof snapshot.itemCount !== "number") {
1177
+ throw new Error("Invalid persisted migration snapshot");
1178
+ }
1179
+ validatedSnapshots.set(snapshot.recordId, {
1180
+ beforeHash: snapshot.beforeHash,
1181
+ afterHash: snapshot.afterHash,
1182
+ itemCount: snapshot.itemCount
1183
+ });
1184
+ }
1185
+ this.migrations = validatedMigrations;
1186
+ this.snapshots = validatedSnapshots;
1187
+ logger.debug("[MigrationTracker] Loaded from persistence", {
1188
+ key: this.persistence.key,
1189
+ migrations: this.migrations.length,
1190
+ snapshots: this.snapshots.size
1191
+ });
1192
+ return { migrations: this.migrations.length, snapshots: this.snapshots.size };
1193
+ }
1194
+ /**
1195
+ * Remove persisted migration tracker state.
1196
+ */
1197
+ async clearPersistence() {
1198
+ if (!this.persistence) {
1199
+ return;
866
1200
  }
1201
+ await this.persistence.adapter.removeItem(this.persistence.key);
867
1202
  }
868
1203
  /**
869
1204
  * Clear history (for testing)
@@ -871,6 +1206,7 @@ var MigrationTracker = class {
871
1206
  clear() {
872
1207
  this.migrations = [];
873
1208
  this.snapshots.clear();
1209
+ this.schedulePersist();
874
1210
  }
875
1211
  /**
876
1212
  * Get total migrations tracked
@@ -889,6 +1225,91 @@ var MigrationTracker = class {
889
1225
  return time >= start && time <= end;
890
1226
  });
891
1227
  }
1228
+ schedulePersist() {
1229
+ if (!this.persistence || this.persistence.autoPersist === false) {
1230
+ return;
1231
+ }
1232
+ if (this.persistTimer) {
1233
+ clearTimeout(this.persistTimer);
1234
+ }
1235
+ this.persistTimer = setTimeout(() => {
1236
+ void this.persistSafely();
1237
+ }, this.persistence.persistDebounceMs ?? 25);
1238
+ }
1239
+ async persistSafely() {
1240
+ if (!this.persistence) {
1241
+ return;
1242
+ }
1243
+ if (this.persistInFlight) {
1244
+ this.persistPending = true;
1245
+ return;
1246
+ }
1247
+ this.persistInFlight = true;
1248
+ try {
1249
+ await this.saveToPersistence();
1250
+ } catch (error) {
1251
+ logger.error("[MigrationTracker] Persistence write failed", {
1252
+ key: this.persistence.key,
1253
+ error: error instanceof Error ? error.message : String(error)
1254
+ });
1255
+ } finally {
1256
+ this.persistInFlight = false;
1257
+ const shouldRunAgain = this.persistPending;
1258
+ this.persistPending = false;
1259
+ if (shouldRunAgain) {
1260
+ void this.persistSafely();
1261
+ }
1262
+ }
1263
+ }
1264
+ isValidMigrationRecord(value) {
1265
+ if (typeof value !== "object" || value === null) {
1266
+ return false;
1267
+ }
1268
+ const record = value;
1269
+ const validDirection = record.direction === "up" || record.direction === "down";
1270
+ const validStatus = record.status === "pending" || record.status === "applied" || record.status === "failed" || record.status === "rolled-back";
1271
+ return typeof record.id === "string" && typeof record.migrationId === "string" && typeof record.timestamp === "string" && typeof record.version === "string" && validDirection && validStatus && typeof record.duration === "number" && typeof record.itemsAffected === "number" && typeof record.appliedBy === "string";
1272
+ }
1273
+ stableStringify(value) {
1274
+ if (value === null || typeof value !== "object") {
1275
+ return JSON.stringify(value);
1276
+ }
1277
+ if (Array.isArray(value)) {
1278
+ return `[${value.map((item) => this.stableStringify(item)).join(",")}]`;
1279
+ }
1280
+ const entries = Object.entries(value).sort(
1281
+ ([a], [b]) => a.localeCompare(b)
1282
+ );
1283
+ return `{${entries.map(
1284
+ ([key, entryValue]) => `${JSON.stringify(key)}:${this.stableStringify(entryValue)}`
1285
+ ).join(",")}}`;
1286
+ }
1287
+ async computeDigestHex(value) {
1288
+ if (globalThis.crypto?.subtle) {
1289
+ const bytes = new TextEncoder().encode(value);
1290
+ const normalized = bytes.buffer.slice(
1291
+ bytes.byteOffset,
1292
+ bytes.byteOffset + bytes.byteLength
1293
+ );
1294
+ const digest = await globalThis.crypto.subtle.digest(
1295
+ "SHA-256",
1296
+ normalized
1297
+ );
1298
+ return this.toHex(new Uint8Array(digest));
1299
+ }
1300
+ return this.fallbackDigestHex(value);
1301
+ }
1302
+ toHex(bytes) {
1303
+ return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
1304
+ }
1305
+ fallbackDigestHex(value) {
1306
+ let hash = 2166136261;
1307
+ for (let i = 0; i < value.length; i++) {
1308
+ hash ^= value.charCodeAt(i);
1309
+ hash = Math.imul(hash, 16777619);
1310
+ }
1311
+ return (hash >>> 0).toString(16).padStart(8, "0");
1312
+ }
892
1313
  };
893
1314
  var SyncCoordinator = class extends EventEmitter {
894
1315
  nodes = /* @__PURE__ */ new Map();
@@ -1246,7 +1667,9 @@ var SyncCoordinator = class extends EventEmitter {
1246
1667
  * Get active sync sessions
1247
1668
  */
1248
1669
  getActiveSyncSessions() {
1249
- return Array.from(this.sessions.values()).filter((s) => s.status === "active");
1670
+ return Array.from(this.sessions.values()).filter(
1671
+ (s) => s.status === "active"
1672
+ );
1250
1673
  }
1251
1674
  /**
1252
1675
  * Get sessions for a node
@@ -1264,8 +1687,14 @@ var SyncCoordinator = class extends EventEmitter {
1264
1687
  const completed = sessions.filter((s) => s.status === "completed").length;
1265
1688
  const failed = sessions.filter((s) => s.status === "failed").length;
1266
1689
  const active = sessions.filter((s) => s.status === "active").length;
1267
- const totalItemsSynced = sessions.reduce((sum, s) => sum + s.itemsSynced, 0);
1268
- const totalConflicts = sessions.reduce((sum, s) => sum + s.conflictsDetected, 0);
1690
+ const totalItemsSynced = sessions.reduce(
1691
+ (sum, s) => sum + s.itemsSynced,
1692
+ 0
1693
+ );
1694
+ const totalConflicts = sessions.reduce(
1695
+ (sum, s) => sum + s.conflictsDetected,
1696
+ 0
1697
+ );
1269
1698
  return {
1270
1699
  totalNodes: this.nodes.size,
1271
1700
  onlineNodes: this.getOnlineNodes().length,
@@ -1332,7 +1761,9 @@ var SyncCoordinator = class extends EventEmitter {
1332
1761
  }
1333
1762
  }
1334
1763
  }, interval);
1335
- logger.debug("[SyncCoordinator] Heartbeat monitoring started", { interval });
1764
+ logger.debug("[SyncCoordinator] Heartbeat monitoring started", {
1765
+ interval
1766
+ });
1336
1767
  }
1337
1768
  /**
1338
1769
  * Stop heartbeat monitoring
@@ -1365,7 +1796,8 @@ var SyncCoordinator = class extends EventEmitter {
1365
1796
  };
1366
1797
 
1367
1798
  // src/distributed/ReplicationManager.ts
1368
- var ReplicationManager = class {
1799
+ var ReplicationManager = class _ReplicationManager {
1800
+ static DEFAULT_PERSIST_KEY = "aeon:replication-state:v1";
1369
1801
  replicas = /* @__PURE__ */ new Map();
1370
1802
  policies = /* @__PURE__ */ new Map();
1371
1803
  replicationEvents = [];
@@ -1374,6 +1806,29 @@ var ReplicationManager = class {
1374
1806
  cryptoProvider = null;
1375
1807
  replicasByDID = /* @__PURE__ */ new Map();
1376
1808
  // DID -> replicaId
1809
+ persistence = null;
1810
+ persistTimer = null;
1811
+ persistInFlight = false;
1812
+ persistPending = false;
1813
+ constructor(options) {
1814
+ if (options?.persistence) {
1815
+ this.persistence = {
1816
+ ...options.persistence,
1817
+ key: options.persistence.key ?? _ReplicationManager.DEFAULT_PERSIST_KEY,
1818
+ autoPersist: options.persistence.autoPersist ?? true,
1819
+ autoLoad: options.persistence.autoLoad ?? false,
1820
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
1821
+ };
1822
+ }
1823
+ if (this.persistence?.autoLoad) {
1824
+ void this.loadFromPersistence().catch((error) => {
1825
+ logger.error("[ReplicationManager] Failed to load persistence", {
1826
+ key: this.persistence?.key,
1827
+ error: error instanceof Error ? error.message : String(error)
1828
+ });
1829
+ });
1830
+ }
1831
+ }
1377
1832
  /**
1378
1833
  * Configure cryptographic provider for encrypted replication
1379
1834
  */
@@ -1418,6 +1873,7 @@ var ReplicationManager = class {
1418
1873
  details: { did: replica.did, encrypted, authenticated: true }
1419
1874
  };
1420
1875
  this.replicationEvents.push(event);
1876
+ this.schedulePersist();
1421
1877
  logger.debug("[ReplicationManager] Authenticated replica registered", {
1422
1878
  replicaId: replica.id,
1423
1879
  did: replica.did,
@@ -1447,7 +1903,10 @@ var ReplicationManager = class {
1447
1903
  throw new Error("Crypto provider not initialized");
1448
1904
  }
1449
1905
  const dataBytes = new TextEncoder().encode(JSON.stringify(data));
1450
- const encrypted = await this.cryptoProvider.encrypt(dataBytes, targetReplicaDID);
1906
+ const encrypted = await this.cryptoProvider.encrypt(
1907
+ dataBytes,
1908
+ targetReplicaDID
1909
+ );
1451
1910
  const localDID = this.cryptoProvider.getLocalDID();
1452
1911
  return {
1453
1912
  ct: encrypted.ct,
@@ -1516,10 +1975,13 @@ var ReplicationManager = class {
1516
1975
  }))
1517
1976
  });
1518
1977
  if (!result.authorized) {
1519
- logger.warn("[ReplicationManager] Replica capability verification failed", {
1520
- replicaDID,
1521
- error: result.error
1522
- });
1978
+ logger.warn(
1979
+ "[ReplicationManager] Replica capability verification failed",
1980
+ {
1981
+ replicaDID,
1982
+ error: result.error
1983
+ }
1984
+ );
1523
1985
  }
1524
1986
  return result;
1525
1987
  }
@@ -1538,6 +2000,7 @@ var ReplicationManager = class {
1538
2000
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1539
2001
  };
1540
2002
  this.replicationEvents.push(event);
2003
+ this.schedulePersist();
1541
2004
  logger.debug("[ReplicationManager] Replica registered", {
1542
2005
  replicaId: replica.id,
1543
2006
  nodeId: replica.nodeId,
@@ -1560,6 +2023,7 @@ var ReplicationManager = class {
1560
2023
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1561
2024
  };
1562
2025
  this.replicationEvents.push(event);
2026
+ this.schedulePersist();
1563
2027
  logger.debug("[ReplicationManager] Replica removed", { replicaId });
1564
2028
  }
1565
2029
  /**
@@ -1575,6 +2039,7 @@ var ReplicationManager = class {
1575
2039
  maxReplicationLag
1576
2040
  };
1577
2041
  this.policies.set(policy.id, policy);
2042
+ this.schedulePersist();
1578
2043
  logger.debug("[ReplicationManager] Policy created", {
1579
2044
  policyId: policy.id,
1580
2045
  name,
@@ -1617,12 +2082,15 @@ var ReplicationManager = class {
1617
2082
  lagBytes,
1618
2083
  lagMillis
1619
2084
  });
2085
+ this.schedulePersist();
1620
2086
  }
1621
2087
  /**
1622
2088
  * Get replicas for node
1623
2089
  */
1624
2090
  getReplicasForNode(nodeId) {
1625
- return Array.from(this.replicas.values()).filter((r) => r.nodeId === nodeId);
2091
+ return Array.from(this.replicas.values()).filter(
2092
+ (r) => r.nodeId === nodeId
2093
+ );
1626
2094
  }
1627
2095
  /**
1628
2096
  * Get healthy replicas
@@ -1636,13 +2104,17 @@ var ReplicationManager = class {
1636
2104
  * Get syncing replicas
1637
2105
  */
1638
2106
  getSyncingReplicas() {
1639
- return Array.from(this.replicas.values()).filter((r) => r.status === "syncing");
2107
+ return Array.from(this.replicas.values()).filter(
2108
+ (r) => r.status === "syncing"
2109
+ );
1640
2110
  }
1641
2111
  /**
1642
2112
  * Get failed replicas
1643
2113
  */
1644
2114
  getFailedReplicas() {
1645
- return Array.from(this.replicas.values()).filter((r) => r.status === "failed");
2115
+ return Array.from(this.replicas.values()).filter(
2116
+ (r) => r.status === "failed"
2117
+ );
1646
2118
  }
1647
2119
  /**
1648
2120
  * Check replication health for policy
@@ -1703,7 +2175,9 @@ var ReplicationManager = class {
1703
2175
  const syncing = this.getSyncingReplicas().length;
1704
2176
  const failed = this.getFailedReplicas().length;
1705
2177
  const total = this.replicas.size;
1706
- const replicationLags = Array.from(this.replicas.values()).map((r) => r.lagMillis);
2178
+ const replicationLags = Array.from(this.replicas.values()).map(
2179
+ (r) => r.lagMillis
2180
+ );
1707
2181
  const avgLag = replicationLags.length > 0 ? replicationLags.reduce((a, b) => a + b) / replicationLags.length : 0;
1708
2182
  const maxLag = replicationLags.length > 0 ? Math.max(...replicationLags) : 0;
1709
2183
  return {
@@ -1777,6 +2251,155 @@ var ReplicationManager = class {
1777
2251
  return false;
1778
2252
  }
1779
2253
  }
2254
+ /**
2255
+ * Persist current replication state snapshot.
2256
+ */
2257
+ async saveToPersistence() {
2258
+ if (!this.persistence) {
2259
+ return;
2260
+ }
2261
+ const data = {
2262
+ replicas: this.getAllReplicas(),
2263
+ policies: this.getAllPolicies(),
2264
+ syncStatus: Array.from(this.syncStatus.entries()).map(
2265
+ ([nodeId, state]) => ({
2266
+ nodeId,
2267
+ synced: state.synced,
2268
+ failed: state.failed
2269
+ })
2270
+ )
2271
+ };
2272
+ const envelope = {
2273
+ version: 1,
2274
+ updatedAt: Date.now(),
2275
+ data
2276
+ };
2277
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
2278
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
2279
+ }
2280
+ /**
2281
+ * Load replication snapshot from persistence.
2282
+ */
2283
+ async loadFromPersistence() {
2284
+ if (!this.persistence) {
2285
+ return { replicas: 0, policies: 0, syncStatus: 0 };
2286
+ }
2287
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
2288
+ if (!raw) {
2289
+ return { replicas: 0, policies: 0, syncStatus: 0 };
2290
+ }
2291
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
2292
+ const envelope = deserialize(raw);
2293
+ if (envelope.version !== 1 || !envelope.data) {
2294
+ throw new Error("Invalid replication persistence payload");
2295
+ }
2296
+ if (!Array.isArray(envelope.data.replicas) || !Array.isArray(envelope.data.policies) || !Array.isArray(envelope.data.syncStatus)) {
2297
+ throw new Error("Invalid replication persistence structure");
2298
+ }
2299
+ this.replicas.clear();
2300
+ this.policies.clear();
2301
+ this.syncStatus.clear();
2302
+ this.replicasByDID.clear();
2303
+ let importedReplicas = 0;
2304
+ for (const replica of envelope.data.replicas) {
2305
+ if (this.isValidReplica(replica)) {
2306
+ this.replicas.set(replica.id, replica);
2307
+ if (replica.did) {
2308
+ this.replicasByDID.set(replica.did, replica.id);
2309
+ }
2310
+ importedReplicas++;
2311
+ }
2312
+ }
2313
+ let importedPolicies = 0;
2314
+ for (const policy of envelope.data.policies) {
2315
+ if (this.isValidPolicy(policy)) {
2316
+ this.policies.set(policy.id, policy);
2317
+ importedPolicies++;
2318
+ }
2319
+ }
2320
+ let importedSyncStatus = 0;
2321
+ for (const status of envelope.data.syncStatus) {
2322
+ if (typeof status.nodeId === "string" && typeof status.synced === "number" && typeof status.failed === "number") {
2323
+ this.syncStatus.set(status.nodeId, {
2324
+ synced: status.synced,
2325
+ failed: status.failed
2326
+ });
2327
+ importedSyncStatus++;
2328
+ }
2329
+ }
2330
+ logger.debug("[ReplicationManager] Loaded from persistence", {
2331
+ key: this.persistence.key,
2332
+ replicas: importedReplicas,
2333
+ policies: importedPolicies,
2334
+ syncStatus: importedSyncStatus
2335
+ });
2336
+ return {
2337
+ replicas: importedReplicas,
2338
+ policies: importedPolicies,
2339
+ syncStatus: importedSyncStatus
2340
+ };
2341
+ }
2342
+ /**
2343
+ * Remove persisted replication snapshot.
2344
+ */
2345
+ async clearPersistence() {
2346
+ if (!this.persistence) {
2347
+ return;
2348
+ }
2349
+ await this.persistence.adapter.removeItem(this.persistence.key);
2350
+ }
2351
+ schedulePersist() {
2352
+ if (!this.persistence || this.persistence.autoPersist === false) {
2353
+ return;
2354
+ }
2355
+ if (this.persistTimer) {
2356
+ clearTimeout(this.persistTimer);
2357
+ }
2358
+ this.persistTimer = setTimeout(() => {
2359
+ void this.persistSafely();
2360
+ }, this.persistence.persistDebounceMs ?? 25);
2361
+ }
2362
+ async persistSafely() {
2363
+ if (!this.persistence) {
2364
+ return;
2365
+ }
2366
+ if (this.persistInFlight) {
2367
+ this.persistPending = true;
2368
+ return;
2369
+ }
2370
+ this.persistInFlight = true;
2371
+ try {
2372
+ await this.saveToPersistence();
2373
+ } catch (error) {
2374
+ logger.error("[ReplicationManager] Persistence write failed", {
2375
+ key: this.persistence.key,
2376
+ error: error instanceof Error ? error.message : String(error)
2377
+ });
2378
+ } finally {
2379
+ this.persistInFlight = false;
2380
+ const shouldRunAgain = this.persistPending;
2381
+ this.persistPending = false;
2382
+ if (shouldRunAgain) {
2383
+ void this.persistSafely();
2384
+ }
2385
+ }
2386
+ }
2387
+ isValidReplica(value) {
2388
+ if (typeof value !== "object" || value === null) {
2389
+ return false;
2390
+ }
2391
+ const candidate = value;
2392
+ const validStatus = candidate.status === "primary" || candidate.status === "secondary" || candidate.status === "syncing" || candidate.status === "failed";
2393
+ return typeof candidate.id === "string" && typeof candidate.nodeId === "string" && validStatus && typeof candidate.lastSyncTime === "string" && typeof candidate.lagBytes === "number" && typeof candidate.lagMillis === "number";
2394
+ }
2395
+ isValidPolicy(value) {
2396
+ if (typeof value !== "object" || value === null) {
2397
+ return false;
2398
+ }
2399
+ const candidate = value;
2400
+ const validConsistency = candidate.consistencyLevel === "eventual" || candidate.consistencyLevel === "read-after-write" || candidate.consistencyLevel === "strong";
2401
+ return typeof candidate.id === "string" && typeof candidate.name === "string" && typeof candidate.replicationFactor === "number" && validConsistency && typeof candidate.syncInterval === "number" && typeof candidate.maxReplicationLag === "number";
2402
+ }
1780
2403
  /**
1781
2404
  * Clear all state (for testing)
1782
2405
  */
@@ -1787,6 +2410,7 @@ var ReplicationManager = class {
1787
2410
  this.syncStatus.clear();
1788
2411
  this.replicasByDID.clear();
1789
2412
  this.cryptoProvider = null;
2413
+ this.schedulePersist();
1790
2414
  }
1791
2415
  /**
1792
2416
  * Get the crypto provider (for advanced usage)
@@ -1797,7 +2421,8 @@ var ReplicationManager = class {
1797
2421
  };
1798
2422
 
1799
2423
  // src/distributed/SyncProtocol.ts
1800
- var SyncProtocol = class {
2424
+ var SyncProtocol = class _SyncProtocol {
2425
+ static DEFAULT_PERSIST_KEY = "aeon:sync-protocol:v1";
1801
2426
  version = "1.0.0";
1802
2427
  messageQueue = [];
1803
2428
  messageMap = /* @__PURE__ */ new Map();
@@ -1807,6 +2432,29 @@ var SyncProtocol = class {
1807
2432
  // Crypto support
1808
2433
  cryptoProvider = null;
1809
2434
  cryptoConfig = null;
2435
+ persistence = null;
2436
+ persistTimer = null;
2437
+ persistInFlight = false;
2438
+ persistPending = false;
2439
+ constructor(options) {
2440
+ if (options?.persistence) {
2441
+ this.persistence = {
2442
+ ...options.persistence,
2443
+ key: options.persistence.key ?? _SyncProtocol.DEFAULT_PERSIST_KEY,
2444
+ autoPersist: options.persistence.autoPersist ?? true,
2445
+ autoLoad: options.persistence.autoLoad ?? false,
2446
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
2447
+ };
2448
+ }
2449
+ if (this.persistence?.autoLoad) {
2450
+ void this.loadFromPersistence().catch((error) => {
2451
+ logger.error("[SyncProtocol] Failed to load persistence", {
2452
+ key: this.persistence?.key,
2453
+ error: error instanceof Error ? error.message : String(error)
2454
+ });
2455
+ });
2456
+ }
2457
+ }
1810
2458
  /**
1811
2459
  * Configure cryptographic provider for authenticated/encrypted messages
1812
2460
  */
@@ -1894,6 +2542,7 @@ var SyncProtocol = class {
1894
2542
  }
1895
2543
  this.messageMap.set(message.messageId, message);
1896
2544
  this.messageQueue.push(message);
2545
+ this.schedulePersist();
1897
2546
  logger.debug("[SyncProtocol] Authenticated handshake created", {
1898
2547
  messageId: message.messageId,
1899
2548
  did: localDID,
@@ -1912,6 +2561,7 @@ var SyncProtocol = class {
1912
2561
  const handshake = message.payload;
1913
2562
  if (!this.cryptoProvider || !this.cryptoConfig) {
1914
2563
  this.handshakes.set(message.sender, handshake);
2564
+ this.schedulePersist();
1915
2565
  return { valid: true, handshake };
1916
2566
  }
1917
2567
  if (handshake.did && handshake.publicSigningKey) {
@@ -1954,6 +2604,7 @@ var SyncProtocol = class {
1954
2604
  }
1955
2605
  }
1956
2606
  this.handshakes.set(message.sender, handshake);
2607
+ this.schedulePersist();
1957
2608
  logger.debug("[SyncProtocol] Authenticated handshake verified", {
1958
2609
  messageId: message.messageId,
1959
2610
  did: handshake.did
@@ -1977,7 +2628,10 @@ var SyncProtocol = class {
1977
2628
  };
1978
2629
  if (encrypt && message.receiver && this.cryptoConfig?.encryptionMode !== "none") {
1979
2630
  const payloadBytes = new TextEncoder().encode(JSON.stringify(payload));
1980
- const encrypted = await this.cryptoProvider.encrypt(payloadBytes, message.receiver);
2631
+ const encrypted = await this.cryptoProvider.encrypt(
2632
+ payloadBytes,
2633
+ message.receiver
2634
+ );
1981
2635
  message.payload = encrypted;
1982
2636
  message.auth.encrypted = true;
1983
2637
  logger.debug("[SyncProtocol] Message encrypted", {
@@ -2050,6 +2704,7 @@ var SyncProtocol = class {
2050
2704
  };
2051
2705
  this.messageMap.set(message.messageId, message);
2052
2706
  this.messageQueue.push(message);
2707
+ this.schedulePersist();
2053
2708
  logger.debug("[SyncProtocol] Handshake message created", {
2054
2709
  messageId: message.messageId,
2055
2710
  nodeId,
@@ -2077,6 +2732,7 @@ var SyncProtocol = class {
2077
2732
  };
2078
2733
  this.messageMap.set(message.messageId, message);
2079
2734
  this.messageQueue.push(message);
2735
+ this.schedulePersist();
2080
2736
  logger.debug("[SyncProtocol] Sync request created", {
2081
2737
  messageId: message.messageId,
2082
2738
  sessionId,
@@ -2107,6 +2763,7 @@ var SyncProtocol = class {
2107
2763
  };
2108
2764
  this.messageMap.set(message.messageId, message);
2109
2765
  this.messageQueue.push(message);
2766
+ this.schedulePersist();
2110
2767
  logger.debug("[SyncProtocol] Sync response created", {
2111
2768
  messageId: message.messageId,
2112
2769
  sessionId,
@@ -2130,6 +2787,7 @@ var SyncProtocol = class {
2130
2787
  };
2131
2788
  this.messageMap.set(message.messageId, message);
2132
2789
  this.messageQueue.push(message);
2790
+ this.schedulePersist();
2133
2791
  return message;
2134
2792
  }
2135
2793
  /**
@@ -2154,6 +2812,7 @@ var SyncProtocol = class {
2154
2812
  error,
2155
2813
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2156
2814
  });
2815
+ this.schedulePersist();
2157
2816
  logger.error("[SyncProtocol] Error message created", {
2158
2817
  messageId: message.messageId,
2159
2818
  errorCode: error.code,
@@ -2178,9 +2837,8 @@ var SyncProtocol = class {
2178
2837
  if (!message.timestamp) {
2179
2838
  errors.push("Timestamp is required");
2180
2839
  }
2181
- try {
2182
- new Date(message.timestamp);
2183
- } catch {
2840
+ const timestampValue = new Date(message.timestamp);
2841
+ if (Number.isNaN(timestampValue.getTime())) {
2184
2842
  errors.push("Invalid timestamp format");
2185
2843
  }
2186
2844
  return {
@@ -2199,7 +2857,9 @@ var SyncProtocol = class {
2199
2857
  messageId: message.messageId,
2200
2858
  error: error instanceof Error ? error.message : String(error)
2201
2859
  });
2202
- throw new Error(`Failed to serialize message: ${error instanceof Error ? error.message : String(error)}`);
2860
+ throw new Error(
2861
+ `Failed to serialize message: ${error instanceof Error ? error.message : String(error)}`
2862
+ );
2203
2863
  }
2204
2864
  }
2205
2865
  /**
@@ -2217,7 +2877,9 @@ var SyncProtocol = class {
2217
2877
  logger.error("[SyncProtocol] Message deserialization failed", {
2218
2878
  error: error instanceof Error ? error.message : String(error)
2219
2879
  });
2220
- throw new Error(`Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`);
2880
+ throw new Error(
2881
+ `Failed to deserialize message: ${error instanceof Error ? error.message : String(error)}`
2882
+ );
2221
2883
  }
2222
2884
  }
2223
2885
  /**
@@ -2230,6 +2892,7 @@ var SyncProtocol = class {
2230
2892
  const handshake = message.payload;
2231
2893
  const nodeId = message.sender;
2232
2894
  this.handshakes.set(nodeId, handshake);
2895
+ this.schedulePersist();
2233
2896
  logger.debug("[SyncProtocol] Handshake processed", {
2234
2897
  nodeId,
2235
2898
  protocolVersion: handshake.protocolVersion,
@@ -2282,7 +2945,9 @@ var SyncProtocol = class {
2282
2945
  messagesByType[message.type] = (messagesByType[message.type] || 0) + 1;
2283
2946
  }
2284
2947
  const errorCount = this.protocolErrors.length;
2285
- const recoverableErrors = this.protocolErrors.filter((e) => e.error.recoverable).length;
2948
+ const recoverableErrors = this.protocolErrors.filter(
2949
+ (e) => e.error.recoverable
2950
+ ).length;
2286
2951
  return {
2287
2952
  totalMessages: this.messageQueue.length,
2288
2953
  messagesByType,
@@ -2298,6 +2963,156 @@ var SyncProtocol = class {
2298
2963
  getErrors() {
2299
2964
  return [...this.protocolErrors];
2300
2965
  }
2966
+ /**
2967
+ * Persist protocol state for reconnect/replay.
2968
+ */
2969
+ async saveToPersistence() {
2970
+ if (!this.persistence) {
2971
+ return;
2972
+ }
2973
+ const data = {
2974
+ protocolVersion: this.version,
2975
+ messageCounter: this.messageCounter,
2976
+ messageQueue: this.getAllMessages(),
2977
+ handshakes: Array.from(this.handshakes.entries()).map(
2978
+ ([nodeId, handshake]) => ({
2979
+ nodeId,
2980
+ handshake
2981
+ })
2982
+ ),
2983
+ protocolErrors: this.getErrors()
2984
+ };
2985
+ const envelope = {
2986
+ version: 1,
2987
+ updatedAt: Date.now(),
2988
+ data
2989
+ };
2990
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
2991
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
2992
+ }
2993
+ /**
2994
+ * Load protocol state from persistence.
2995
+ */
2996
+ async loadFromPersistence() {
2997
+ if (!this.persistence) {
2998
+ return { messages: 0, handshakes: 0, errors: 0 };
2999
+ }
3000
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
3001
+ if (!raw) {
3002
+ return { messages: 0, handshakes: 0, errors: 0 };
3003
+ }
3004
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
3005
+ const envelope = deserialize(raw);
3006
+ if (envelope.version !== 1 || !envelope.data) {
3007
+ throw new Error("Invalid sync protocol persistence payload");
3008
+ }
3009
+ if (!Array.isArray(envelope.data.messageQueue) || !Array.isArray(envelope.data.handshakes) || !Array.isArray(envelope.data.protocolErrors)) {
3010
+ throw new Error("Invalid sync protocol persistence structure");
3011
+ }
3012
+ const nextMessages = [];
3013
+ for (const message of envelope.data.messageQueue) {
3014
+ const validation = this.validateMessage(message);
3015
+ if (!validation.valid) {
3016
+ throw new Error(
3017
+ `Invalid persisted message ${message?.messageId ?? "unknown"}: ${validation.errors.join(", ")}`
3018
+ );
3019
+ }
3020
+ nextMessages.push(message);
3021
+ }
3022
+ const nextHandshakes = /* @__PURE__ */ new Map();
3023
+ for (const entry of envelope.data.handshakes) {
3024
+ if (typeof entry.nodeId !== "string" || !this.isValidHandshake(entry.handshake)) {
3025
+ throw new Error("Invalid persisted handshake payload");
3026
+ }
3027
+ nextHandshakes.set(entry.nodeId, entry.handshake);
3028
+ }
3029
+ const nextErrors = [];
3030
+ for (const entry of envelope.data.protocolErrors) {
3031
+ if (!this.isValidProtocolErrorEntry(entry)) {
3032
+ throw new Error("Invalid persisted protocol error payload");
3033
+ }
3034
+ nextErrors.push(entry);
3035
+ }
3036
+ this.messageQueue = nextMessages;
3037
+ this.messageMap = new Map(nextMessages.map((m) => [m.messageId, m]));
3038
+ this.handshakes = nextHandshakes;
3039
+ this.protocolErrors = nextErrors;
3040
+ this.messageCounter = Math.max(
3041
+ envelope.data.messageCounter || 0,
3042
+ this.messageQueue.length
3043
+ );
3044
+ logger.debug("[SyncProtocol] Loaded from persistence", {
3045
+ key: this.persistence.key,
3046
+ messages: this.messageQueue.length,
3047
+ handshakes: this.handshakes.size,
3048
+ errors: this.protocolErrors.length
3049
+ });
3050
+ return {
3051
+ messages: this.messageQueue.length,
3052
+ handshakes: this.handshakes.size,
3053
+ errors: this.protocolErrors.length
3054
+ };
3055
+ }
3056
+ /**
3057
+ * Clear persisted protocol checkpoint.
3058
+ */
3059
+ async clearPersistence() {
3060
+ if (!this.persistence) {
3061
+ return;
3062
+ }
3063
+ await this.persistence.adapter.removeItem(this.persistence.key);
3064
+ }
3065
+ schedulePersist() {
3066
+ if (!this.persistence || this.persistence.autoPersist === false) {
3067
+ return;
3068
+ }
3069
+ if (this.persistTimer) {
3070
+ clearTimeout(this.persistTimer);
3071
+ }
3072
+ this.persistTimer = setTimeout(() => {
3073
+ void this.persistSafely();
3074
+ }, this.persistence.persistDebounceMs ?? 25);
3075
+ }
3076
+ async persistSafely() {
3077
+ if (!this.persistence) {
3078
+ return;
3079
+ }
3080
+ if (this.persistInFlight) {
3081
+ this.persistPending = true;
3082
+ return;
3083
+ }
3084
+ this.persistInFlight = true;
3085
+ try {
3086
+ await this.saveToPersistence();
3087
+ } catch (error) {
3088
+ logger.error("[SyncProtocol] Persistence write failed", {
3089
+ key: this.persistence.key,
3090
+ error: error instanceof Error ? error.message : String(error)
3091
+ });
3092
+ } finally {
3093
+ this.persistInFlight = false;
3094
+ const shouldRunAgain = this.persistPending;
3095
+ this.persistPending = false;
3096
+ if (shouldRunAgain) {
3097
+ void this.persistSafely();
3098
+ }
3099
+ }
3100
+ }
3101
+ isValidHandshake(value) {
3102
+ if (typeof value !== "object" || value === null) {
3103
+ return false;
3104
+ }
3105
+ const handshake = value;
3106
+ const validState = handshake.state === "initiating" || handshake.state === "responding" || handshake.state === "completed";
3107
+ return typeof handshake.protocolVersion === "string" && typeof handshake.nodeId === "string" && Array.isArray(handshake.capabilities) && handshake.capabilities.every((cap) => typeof cap === "string") && validState;
3108
+ }
3109
+ isValidProtocolErrorEntry(entry) {
3110
+ if (typeof entry !== "object" || entry === null) {
3111
+ return false;
3112
+ }
3113
+ const candidate = entry;
3114
+ return typeof candidate.timestamp === "string" && typeof candidate.error?.code === "string" && typeof candidate.error.message === "string" && typeof candidate.error.recoverable === "boolean";
3115
+ }
2301
3116
  /**
2302
3117
  * Generate message ID
2303
3118
  */
@@ -2316,6 +3131,7 @@ var SyncProtocol = class {
2316
3131
  this.messageCounter = 0;
2317
3132
  this.cryptoProvider = null;
2318
3133
  this.cryptoConfig = null;
3134
+ this.schedulePersist();
2319
3135
  }
2320
3136
  /**
2321
3137
  * Get the crypto provider (for advanced usage)
@@ -2701,7 +3517,9 @@ var StateReconciler = class {
2701
3517
  }
2702
3518
  return {
2703
3519
  totalReconciliations: this.reconciliationHistory.length,
2704
- successfulReconciliations: this.reconciliationHistory.filter((r) => r.success).length,
3520
+ successfulReconciliations: this.reconciliationHistory.filter(
3521
+ (r) => r.success
3522
+ ).length,
2705
3523
  totalConflictsResolved: resolvedConflicts,
2706
3524
  averageConflictsPerReconciliation: this.reconciliationHistory.length > 0 ? resolvedConflicts / this.reconciliationHistory.length : 0,
2707
3525
  strategyUsage,
@@ -2725,18 +3543,46 @@ var StateReconciler = class {
2725
3543
  }
2726
3544
  };
2727
3545
  var logger2 = getLogger();
2728
- var OfflineOperationQueue = class extends EventEmitter {
3546
+ var OfflineOperationQueue = class _OfflineOperationQueue extends EventEmitter {
3547
+ static DEFAULT_PERSIST_KEY = "aeon:offline-queue:v1";
2729
3548
  queue = /* @__PURE__ */ new Map();
2730
3549
  syncingIds = /* @__PURE__ */ new Set();
2731
3550
  maxQueueSize = 1e3;
2732
3551
  defaultMaxRetries = 3;
2733
- constructor(maxQueueSize = 1e3, defaultMaxRetries = 3) {
3552
+ persistence = null;
3553
+ persistTimer = null;
3554
+ persistInFlight = false;
3555
+ persistPending = false;
3556
+ constructor(maxQueueSizeOrOptions = 1e3, defaultMaxRetries = 3) {
2734
3557
  super();
2735
- this.maxQueueSize = maxQueueSize;
2736
- this.defaultMaxRetries = defaultMaxRetries;
3558
+ if (typeof maxQueueSizeOrOptions === "number") {
3559
+ this.maxQueueSize = maxQueueSizeOrOptions;
3560
+ this.defaultMaxRetries = defaultMaxRetries;
3561
+ } else {
3562
+ this.maxQueueSize = maxQueueSizeOrOptions.maxQueueSize ?? 1e3;
3563
+ this.defaultMaxRetries = maxQueueSizeOrOptions.defaultMaxRetries ?? 3;
3564
+ if (maxQueueSizeOrOptions.persistence) {
3565
+ this.persistence = {
3566
+ ...maxQueueSizeOrOptions.persistence,
3567
+ key: maxQueueSizeOrOptions.persistence.key ?? _OfflineOperationQueue.DEFAULT_PERSIST_KEY,
3568
+ autoPersist: maxQueueSizeOrOptions.persistence.autoPersist ?? true,
3569
+ autoLoad: maxQueueSizeOrOptions.persistence.autoLoad ?? false,
3570
+ persistDebounceMs: maxQueueSizeOrOptions.persistence.persistDebounceMs ?? 25
3571
+ };
3572
+ if (this.persistence.autoLoad) {
3573
+ void this.loadFromPersistence().catch((error) => {
3574
+ logger2.error("[OfflineOperationQueue] Failed to load persistence", {
3575
+ key: this.persistence?.key,
3576
+ error: error instanceof Error ? error.message : String(error)
3577
+ });
3578
+ });
3579
+ }
3580
+ }
3581
+ }
2737
3582
  logger2.debug("[OfflineOperationQueue] Initialized", {
2738
- maxQueueSize,
2739
- defaultMaxRetries
3583
+ maxQueueSize: this.maxQueueSize,
3584
+ defaultMaxRetries: this.defaultMaxRetries,
3585
+ persistenceEnabled: this.persistence !== null
2740
3586
  });
2741
3587
  }
2742
3588
  /**
@@ -2765,6 +3611,7 @@ var OfflineOperationQueue = class extends EventEmitter {
2765
3611
  };
2766
3612
  this.queue.set(operation.id, operation);
2767
3613
  this.emit("operation-added", operation);
3614
+ this.schedulePersist();
2768
3615
  logger2.debug("[OfflineOperationQueue] Operation enqueued", {
2769
3616
  id: operation.id,
2770
3617
  type,
@@ -2789,13 +3636,18 @@ var OfflineOperationQueue = class extends EventEmitter {
2789
3636
  * Mark operations as syncing
2790
3637
  */
2791
3638
  markSyncing(operationIds) {
3639
+ let changed = false;
2792
3640
  for (const id of operationIds) {
2793
3641
  const op = this.queue.get(id);
2794
3642
  if (op) {
2795
3643
  op.status = "syncing";
2796
3644
  this.syncingIds.add(id);
3645
+ changed = true;
2797
3646
  }
2798
3647
  }
3648
+ if (changed) {
3649
+ this.schedulePersist();
3650
+ }
2799
3651
  }
2800
3652
  /**
2801
3653
  * Mark operation as synced
@@ -2806,8 +3658,10 @@ var OfflineOperationQueue = class extends EventEmitter {
2806
3658
  op.status = "synced";
2807
3659
  this.syncingIds.delete(operationId);
2808
3660
  this.emit("operation-synced", op);
3661
+ this.schedulePersist();
2809
3662
  setTimeout(() => {
2810
3663
  this.queue.delete(operationId);
3664
+ this.schedulePersist();
2811
3665
  if (this.getPendingCount() === 0) {
2812
3666
  this.emit("queue-empty");
2813
3667
  }
@@ -2839,6 +3693,7 @@ var OfflineOperationQueue = class extends EventEmitter {
2839
3693
  maxRetries: op.maxRetries
2840
3694
  });
2841
3695
  }
3696
+ this.schedulePersist();
2842
3697
  }
2843
3698
  }
2844
3699
  /**
@@ -2891,28 +3746,39 @@ var OfflineOperationQueue = class extends EventEmitter {
2891
3746
  clear() {
2892
3747
  this.queue.clear();
2893
3748
  this.syncingIds.clear();
3749
+ this.schedulePersist();
2894
3750
  logger2.debug("[OfflineOperationQueue] Queue cleared");
2895
3751
  }
2896
3752
  /**
2897
3753
  * Clear failed operations
2898
3754
  */
2899
3755
  clearFailed() {
3756
+ let changed = false;
2900
3757
  for (const [id, op] of this.queue.entries()) {
2901
3758
  if (op.status === "failed") {
2902
3759
  this.queue.delete(id);
3760
+ changed = true;
2903
3761
  }
2904
3762
  }
3763
+ if (changed) {
3764
+ this.schedulePersist();
3765
+ }
2905
3766
  }
2906
3767
  /**
2907
3768
  * Retry failed operations
2908
3769
  */
2909
3770
  retryFailed() {
3771
+ let changed = false;
2910
3772
  for (const op of this.queue.values()) {
2911
3773
  if (op.status === "failed") {
2912
3774
  op.status = "pending";
2913
3775
  op.retryCount = 0;
3776
+ changed = true;
2914
3777
  }
2915
3778
  }
3779
+ if (changed) {
3780
+ this.schedulePersist();
3781
+ }
2916
3782
  }
2917
3783
  /**
2918
3784
  * Find oldest low-priority operation
@@ -2931,12 +3797,125 @@ var OfflineOperationQueue = class extends EventEmitter {
2931
3797
  * Import queue from persistence
2932
3798
  */
2933
3799
  import(operations) {
3800
+ this.queue.clear();
3801
+ this.syncingIds.clear();
2934
3802
  for (const op of operations) {
2935
- this.queue.set(op.id, op);
3803
+ if (this.isValidOfflineOperation(op)) {
3804
+ this.queue.set(op.id, {
3805
+ ...op,
3806
+ status: op.status === "syncing" ? "pending" : op.status
3807
+ });
3808
+ }
2936
3809
  }
3810
+ this.schedulePersist();
2937
3811
  logger2.debug("[OfflineOperationQueue] Imported operations", {
2938
- count: operations.length
3812
+ count: this.queue.size
3813
+ });
3814
+ }
3815
+ /**
3816
+ * Persist current queue snapshot.
3817
+ */
3818
+ async saveToPersistence() {
3819
+ if (!this.persistence) {
3820
+ return;
3821
+ }
3822
+ const envelope = {
3823
+ version: 1,
3824
+ updatedAt: Date.now(),
3825
+ data: this.export()
3826
+ };
3827
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
3828
+ const raw = serialize(envelope);
3829
+ await this.persistence.adapter.setItem(this.persistence.key, raw);
3830
+ }
3831
+ /**
3832
+ * Load queue snapshot from persistence.
3833
+ */
3834
+ async loadFromPersistence() {
3835
+ if (!this.persistence) {
3836
+ return 0;
3837
+ }
3838
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
3839
+ if (!raw) {
3840
+ return 0;
3841
+ }
3842
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
3843
+ const envelope = deserialize(raw);
3844
+ if (envelope.version !== 1 || !Array.isArray(envelope.data)) {
3845
+ throw new Error("Invalid offline queue persistence payload");
3846
+ }
3847
+ this.queue.clear();
3848
+ this.syncingIds.clear();
3849
+ let imported = 0;
3850
+ for (const operation of envelope.data) {
3851
+ if (this.isValidOfflineOperation(operation)) {
3852
+ this.queue.set(operation.id, {
3853
+ ...operation,
3854
+ status: operation.status === "syncing" ? "pending" : operation.status
3855
+ });
3856
+ imported++;
3857
+ }
3858
+ }
3859
+ logger2.debug("[OfflineOperationQueue] Loaded from persistence", {
3860
+ key: this.persistence.key,
3861
+ imported
2939
3862
  });
3863
+ return imported;
3864
+ }
3865
+ /**
3866
+ * Remove persisted queue snapshot.
3867
+ */
3868
+ async clearPersistence() {
3869
+ if (!this.persistence) {
3870
+ return;
3871
+ }
3872
+ await this.persistence.adapter.removeItem(this.persistence.key);
3873
+ }
3874
+ schedulePersist() {
3875
+ if (!this.persistence || this.persistence.autoPersist === false) {
3876
+ return;
3877
+ }
3878
+ if (this.persistTimer) {
3879
+ clearTimeout(this.persistTimer);
3880
+ }
3881
+ this.persistTimer = setTimeout(() => {
3882
+ void this.persistSafely();
3883
+ }, this.persistence.persistDebounceMs ?? 25);
3884
+ }
3885
+ async persistSafely() {
3886
+ if (!this.persistence) {
3887
+ return;
3888
+ }
3889
+ if (this.persistInFlight) {
3890
+ this.persistPending = true;
3891
+ return;
3892
+ }
3893
+ this.persistInFlight = true;
3894
+ try {
3895
+ await this.saveToPersistence();
3896
+ } catch (error) {
3897
+ logger2.error("[OfflineOperationQueue] Persistence write failed", {
3898
+ key: this.persistence.key,
3899
+ error: error instanceof Error ? error.message : String(error)
3900
+ });
3901
+ } finally {
3902
+ this.persistInFlight = false;
3903
+ const shouldRunAgain = this.persistPending;
3904
+ this.persistPending = false;
3905
+ if (shouldRunAgain) {
3906
+ void this.persistSafely();
3907
+ }
3908
+ }
3909
+ }
3910
+ isValidOfflineOperation(value) {
3911
+ if (typeof value !== "object" || value === null) {
3912
+ return false;
3913
+ }
3914
+ const candidate = value;
3915
+ const validType = candidate.type === "create" || candidate.type === "update" || candidate.type === "delete" || candidate.type === "sync" || candidate.type === "batch";
3916
+ const validPriority = candidate.priority === "high" || candidate.priority === "normal" || candidate.priority === "low";
3917
+ const validStatus = candidate.status === "pending" || candidate.status === "syncing" || candidate.status === "failed" || candidate.status === "synced";
3918
+ 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;
2940
3919
  }
2941
3920
  };
2942
3921
  var offlineQueueInstance = null;
@@ -2987,9 +3966,15 @@ var CompressionEngine = class {
2987
3966
  let algorithm = this.preferredAlgorithm;
2988
3967
  if (this.supportsNativeCompression()) {
2989
3968
  try {
2990
- compressed = await this.compressNative(inputData, this.preferredAlgorithm);
3969
+ compressed = await this.compressNative(
3970
+ inputData,
3971
+ this.preferredAlgorithm
3972
+ );
2991
3973
  } catch (error) {
2992
- logger3.warn("[CompressionEngine] Native compression failed, using fallback", error);
3974
+ logger3.warn(
3975
+ "[CompressionEngine] Native compression failed, using fallback",
3976
+ error
3977
+ );
2993
3978
  compressed = inputData;
2994
3979
  algorithm = "none";
2995
3980
  }
@@ -3061,7 +4046,13 @@ var CompressionEngine = class {
3061
4046
  const stream = new CompressionStream(algorithm);
3062
4047
  const writer = stream.writable.getWriter();
3063
4048
  const reader = stream.readable.getReader();
3064
- writer.write(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
4049
+ writer.write(
4050
+ new Uint8Array(
4051
+ data.buffer,
4052
+ data.byteOffset,
4053
+ data.byteLength
4054
+ )
4055
+ );
3065
4056
  writer.close();
3066
4057
  const chunks = [];
3067
4058
  let done = false;
@@ -3088,7 +4079,13 @@ var CompressionEngine = class {
3088
4079
  const stream = new DecompressionStream(algorithm);
3089
4080
  const writer = stream.writable.getWriter();
3090
4081
  const reader = stream.readable.getReader();
3091
- writer.write(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
4082
+ writer.write(
4083
+ new Uint8Array(
4084
+ data.buffer,
4085
+ data.byteOffset,
4086
+ data.byteLength
4087
+ )
4088
+ );
3092
4089
  writer.close();
3093
4090
  const chunks = [];
3094
4091
  let done = false;
@@ -3137,9 +4134,14 @@ var CompressionEngine = class {
3137
4134
  const sorted = [...chunks].sort((a, b) => a.index - b.index);
3138
4135
  const total = sorted[0]?.total ?? 0;
3139
4136
  if (sorted.length !== total) {
3140
- throw new Error(`Missing chunks: got ${sorted.length}, expected ${total}`);
4137
+ throw new Error(
4138
+ `Missing chunks: got ${sorted.length}, expected ${total}`
4139
+ );
3141
4140
  }
3142
- const totalLength = sorted.reduce((sum, chunk) => sum + chunk.data.length, 0);
4141
+ const totalLength = sorted.reduce(
4142
+ (sum, chunk) => sum + chunk.data.length,
4143
+ 0
4144
+ );
3143
4145
  const combined = new Uint8Array(totalLength);
3144
4146
  let offset = 0;
3145
4147
  for (const chunk of sorted) {
@@ -4070,7 +5072,10 @@ var AdaptiveCompressionOptimizer = class {
4070
5072
  * Update device resource usage
4071
5073
  */
4072
5074
  updateDeviceResources(cpuUtilization, memoryAvailableMB) {
4073
- this.deviceProfile.cpuUtilization = Math.max(0, Math.min(1, cpuUtilization));
5075
+ this.deviceProfile.cpuUtilization = Math.max(
5076
+ 0,
5077
+ Math.min(1, cpuUtilization)
5078
+ );
4074
5079
  this.deviceProfile.memoryAvailableMB = memoryAvailableMB;
4075
5080
  this.deviceProfile.isConstrained = memoryAvailableMB < 512;
4076
5081
  this.deviceProfile.isPremium = memoryAvailableMB > 2048;
@@ -4130,7 +5135,10 @@ var AdaptiveCompressionOptimizer = class {
4130
5135
  networkFactor,
4131
5136
  deviceFactor
4132
5137
  };
4133
- logger7.debug("[AdaptiveCompressionOptimizer] Recommendation", recommendation);
5138
+ logger7.debug(
5139
+ "[AdaptiveCompressionOptimizer] Recommendation",
5140
+ recommendation
5141
+ );
4134
5142
  return recommendation;
4135
5143
  }
4136
5144
  /**
@@ -4282,7 +5290,11 @@ var AgentPresenceManager = class extends EventEmitter {
4282
5290
  };
4283
5291
  this.presences.set(agentId, presence);
4284
5292
  this.emit("agent_joined", { agentId, presence });
4285
- logger8.debug("[AgentPresenceManager] Agent joined", { agentId, name, role });
5293
+ logger8.debug("[AgentPresenceManager] Agent joined", {
5294
+ agentId,
5295
+ name,
5296
+ role
5297
+ });
4286
5298
  }
4287
5299
  /**
4288
5300
  * Agent left
@@ -4327,6 +5339,157 @@ var AgentPresenceManager = class extends EventEmitter {
4327
5339
  });
4328
5340
  }
4329
5341
  }
5342
+ /**
5343
+ * Update focused node path
5344
+ */
5345
+ updateFocusNode(agentId, nodePath) {
5346
+ const presence = this.presences.get(agentId);
5347
+ if (presence) {
5348
+ presence.focusNode = nodePath;
5349
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5350
+ this.presences.set(agentId, presence);
5351
+ this.emit("focus_updated", {
5352
+ agentId,
5353
+ focusNode: nodePath
5354
+ });
5355
+ }
5356
+ }
5357
+ /**
5358
+ * Update text selection range
5359
+ */
5360
+ updateSelection(agentId, selectionRange) {
5361
+ const presence = this.presences.get(agentId);
5362
+ if (presence) {
5363
+ presence.selectionRange = selectionRange;
5364
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5365
+ this.presences.set(agentId, presence);
5366
+ this.emit("selection_updated", {
5367
+ agentId,
5368
+ selectionRange
5369
+ });
5370
+ }
5371
+ }
5372
+ /**
5373
+ * Update typing state
5374
+ */
5375
+ updateTyping(agentId, isTyping, field, isComposing = false) {
5376
+ const presence = this.presences.get(agentId);
5377
+ if (presence) {
5378
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5379
+ const previous = presence.typingState;
5380
+ const typingState = {
5381
+ isTyping,
5382
+ field,
5383
+ isComposing,
5384
+ startedAt: isTyping && !previous?.isTyping ? now : isTyping ? previous?.startedAt : void 0,
5385
+ stoppedAt: isTyping ? void 0 : now
5386
+ };
5387
+ presence.typingState = typingState;
5388
+ presence.lastSeen = now;
5389
+ this.presences.set(agentId, presence);
5390
+ this.emit("typing_updated", {
5391
+ agentId,
5392
+ typingState
5393
+ });
5394
+ }
5395
+ }
5396
+ /**
5397
+ * Update scroll state
5398
+ */
5399
+ updateScroll(agentId, scrollState) {
5400
+ const presence = this.presences.get(agentId);
5401
+ if (presence) {
5402
+ presence.scrollState = {
5403
+ ...scrollState,
5404
+ depth: Math.max(0, Math.min(1, scrollState.depth))
5405
+ };
5406
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5407
+ this.presences.set(agentId, presence);
5408
+ this.emit("scroll_updated", {
5409
+ agentId,
5410
+ scrollState: presence.scrollState
5411
+ });
5412
+ }
5413
+ }
5414
+ /**
5415
+ * Update viewport size
5416
+ */
5417
+ updateViewport(agentId, width, height) {
5418
+ const presence = this.presences.get(agentId);
5419
+ if (presence) {
5420
+ presence.viewport = { width, height };
5421
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5422
+ this.presences.set(agentId, presence);
5423
+ this.emit("viewport_updated", {
5424
+ agentId,
5425
+ viewport: presence.viewport
5426
+ });
5427
+ }
5428
+ }
5429
+ /**
5430
+ * Update input state
5431
+ */
5432
+ updateInputState(agentId, inputState) {
5433
+ const presence = this.presences.get(agentId);
5434
+ if (presence) {
5435
+ presence.inputState = inputState;
5436
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5437
+ this.presences.set(agentId, presence);
5438
+ this.emit("input_state_updated", {
5439
+ agentId,
5440
+ inputState
5441
+ });
5442
+ }
5443
+ }
5444
+ /**
5445
+ * Clear input state
5446
+ */
5447
+ clearInputState(agentId) {
5448
+ const presence = this.presences.get(agentId);
5449
+ if (presence) {
5450
+ presence.inputState = void 0;
5451
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5452
+ this.presences.set(agentId, presence);
5453
+ this.emit("input_state_updated", {
5454
+ agentId,
5455
+ inputState: void 0
5456
+ });
5457
+ }
5458
+ }
5459
+ /**
5460
+ * Update emotional state
5461
+ */
5462
+ updateEmotionState(agentId, emotionState) {
5463
+ const presence = this.presences.get(agentId);
5464
+ if (presence) {
5465
+ const enrichedState = {
5466
+ ...emotionState,
5467
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
5468
+ };
5469
+ presence.emotionState = enrichedState;
5470
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5471
+ this.presences.set(agentId, presence);
5472
+ this.emit("emotion_updated", {
5473
+ agentId,
5474
+ emotionState: enrichedState
5475
+ });
5476
+ }
5477
+ }
5478
+ /**
5479
+ * Clear emotional state
5480
+ */
5481
+ clearEmotionState(agentId) {
5482
+ const presence = this.presences.get(agentId);
5483
+ if (presence) {
5484
+ presence.emotionState = void 0;
5485
+ presence.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
5486
+ this.presences.set(agentId, presence);
5487
+ this.emit("emotion_updated", {
5488
+ agentId,
5489
+ emotionState: void 0
5490
+ });
5491
+ }
5492
+ }
4330
5493
  /**
4331
5494
  * Update status
4332
5495
  */
@@ -4516,7 +5679,9 @@ var AgentPresenceManager = class extends EventEmitter {
4516
5679
  this.stopHeartbeatMonitoring();
4517
5680
  this.presences.clear();
4518
5681
  this.removeAllListeners();
4519
- logger8.debug("[AgentPresenceManager] Destroyed", { sessionId: this.sessionId });
5682
+ logger8.debug("[AgentPresenceManager] Destroyed", {
5683
+ sessionId: this.sessionId
5684
+ });
4520
5685
  }
4521
5686
  };
4522
5687
  var instances = /* @__PURE__ */ new Map();
@@ -4627,6 +5792,6 @@ var NullCryptoProvider = class {
4627
5792
  }
4628
5793
  };
4629
5794
 
4630
- export { AEON_CAPABILITIES, AdaptiveCompressionOptimizer, AgentPresenceManager, BatchTimingOptimizer, CompressionEngine, DEFAULT_CRYPTO_CONFIG, DataTransformer, DeltaSyncOptimizer, MigrationEngine, MigrationTracker, NullCryptoProvider, OfflineOperationQueue, PrefetchingEngine, ReplicationManager, SchemaVersionManager, StateReconciler, SyncCoordinator, SyncProtocol, clearAgentPresenceManager, createNamespacedLogger, disableLogging, getAdaptiveCompressionOptimizer, getAgentPresenceManager, getBatchTimingOptimizer, getCompressionEngine, getDeltaSyncOptimizer, getLogger, getOfflineOperationQueue, getPrefetchingEngine, logger, resetAdaptiveCompressionOptimizer, resetBatchTimingOptimizer, resetCompressionEngine, resetDeltaSyncOptimizer, resetLogger, resetOfflineOperationQueue, resetPrefetchingEngine, setLogger };
5795
+ export { AEON_CAPABILITIES, AdaptiveCompressionOptimizer, AgentPresenceManager, BatchTimingOptimizer, CompressionEngine, DEFAULT_CRYPTO_CONFIG, DashStorageAdapter, DataTransformer, DeltaSyncOptimizer, InMemoryStorageAdapter, MigrationEngine, MigrationTracker, NullCryptoProvider, OfflineOperationQueue, PrefetchingEngine, ReplicationManager, SchemaVersionManager, StateReconciler, SyncCoordinator, SyncProtocol, clearAgentPresenceManager, createNamespacedLogger, disableLogging, getAdaptiveCompressionOptimizer, getAgentPresenceManager, getBatchTimingOptimizer, getCompressionEngine, getDeltaSyncOptimizer, getLogger, getOfflineOperationQueue, getPrefetchingEngine, logger, resetAdaptiveCompressionOptimizer, resetBatchTimingOptimizer, resetCompressionEngine, resetDeltaSyncOptimizer, resetLogger, resetOfflineOperationQueue, resetPrefetchingEngine, setLogger };
4631
5796
  //# sourceMappingURL=index.js.map
4632
5797
  //# sourceMappingURL=index.js.map