@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
@@ -77,7 +77,9 @@ var SchemaVersionManager = class {
77
77
  */
78
78
  setCurrentVersion(version) {
79
79
  if (!this.versions.has(this.versionToString(version))) {
80
- throw new Error(`Version ${this.versionToString(version)} not registered`);
80
+ throw new Error(
81
+ `Version ${this.versionToString(version)} not registered`
82
+ );
81
83
  }
82
84
  this.currentVersion = version;
83
85
  logger.debug("[SchemaVersionManager] Current version set", {
@@ -363,7 +365,9 @@ var MigrationEngine = class {
363
365
  id: migrationId,
364
366
  error: result.errors[0]
365
367
  });
366
- throw new Error(`Rollback for ${migrationId} failed: ${result.errors[0]}`);
368
+ throw new Error(
369
+ `Rollback for ${migrationId} failed: ${result.errors[0]}`
370
+ );
367
371
  }
368
372
  }
369
373
  /**
@@ -416,8 +420,14 @@ var MigrationEngine = class {
416
420
  getStatistics() {
417
421
  const successful = this.executedMigrations.filter((m) => m.success).length;
418
422
  const failed = this.executedMigrations.filter((m) => !m.success).length;
419
- const totalDuration = this.executedMigrations.reduce((sum, m) => sum + m.duration, 0);
420
- const totalAffected = this.executedMigrations.reduce((sum, m) => sum + m.itemsAffected, 0);
423
+ const totalDuration = this.executedMigrations.reduce(
424
+ (sum, m) => sum + m.duration,
425
+ 0
426
+ );
427
+ const totalAffected = this.executedMigrations.reduce(
428
+ (sum, m) => sum + m.itemsAffected,
429
+ 0
430
+ );
421
431
  return {
422
432
  totalExecuted: this.executedMigrations.length,
423
433
  successful,
@@ -471,7 +481,9 @@ var DataTransformer = class {
471
481
  return rule.transformer(value);
472
482
  } catch (error) {
473
483
  if (rule.required) {
474
- throw new Error(`Failed to transform required field ${field}: ${error instanceof Error ? error.message : String(error)}`);
484
+ throw new Error(
485
+ `Failed to transform required field ${field}: ${error instanceof Error ? error.message : String(error)}`
486
+ );
475
487
  }
476
488
  return rule.defaultValue !== void 0 ? rule.defaultValue : value;
477
489
  }
@@ -544,7 +556,9 @@ var DataTransformer = class {
544
556
  validateTransformation(original, transformed) {
545
557
  const issues = [];
546
558
  if (original.length !== transformed.length) {
547
- issues.push(`Item count mismatch: ${original.length} -> ${transformed.length}`);
559
+ issues.push(
560
+ `Item count mismatch: ${original.length} -> ${transformed.length}`
561
+ );
548
562
  }
549
563
  for (let i = 0; i < Math.min(original.length, transformed.length); i++) {
550
564
  const orig = original[i];
@@ -635,14 +649,40 @@ var DataTransformer = class {
635
649
  };
636
650
 
637
651
  // src/versioning/MigrationTracker.ts
638
- var MigrationTracker = class {
652
+ var MigrationTracker = class _MigrationTracker {
653
+ static DEFAULT_PERSIST_KEY = "aeon:migration-tracker:v1";
654
+ static INTEGRITY_ROOT = "aeon:migration-integrity-root:v1";
639
655
  migrations = [];
640
656
  snapshots = /* @__PURE__ */ new Map();
657
+ persistence = null;
658
+ persistTimer = null;
659
+ persistInFlight = false;
660
+ persistPending = false;
661
+ constructor(options) {
662
+ if (options?.persistence) {
663
+ this.persistence = {
664
+ ...options.persistence,
665
+ key: options.persistence.key ?? _MigrationTracker.DEFAULT_PERSIST_KEY,
666
+ autoPersist: options.persistence.autoPersist ?? true,
667
+ autoLoad: options.persistence.autoLoad ?? false,
668
+ persistDebounceMs: options.persistence.persistDebounceMs ?? 25
669
+ };
670
+ }
671
+ if (this.persistence?.autoLoad) {
672
+ void this.loadFromPersistence().catch((error) => {
673
+ logger.error("[MigrationTracker] Failed to load persistence", {
674
+ key: this.persistence?.key,
675
+ error: error instanceof Error ? error.message : String(error)
676
+ });
677
+ });
678
+ }
679
+ }
641
680
  /**
642
681
  * Track a new migration
643
682
  */
644
683
  recordMigration(record) {
645
684
  this.migrations.push({ ...record });
685
+ this.schedulePersist();
646
686
  logger.debug("[MigrationTracker] Migration recorded", {
647
687
  id: record.id,
648
688
  migrationId: record.migrationId,
@@ -699,7 +739,9 @@ var MigrationTracker = class {
699
739
  * Check if can rollback
700
740
  */
701
741
  canRollback(fromVersion, toVersion) {
702
- const fromIndex = this.migrations.findIndex((m) => m.version === fromVersion);
742
+ const fromIndex = this.migrations.findIndex(
743
+ (m) => m.version === fromVersion
744
+ );
703
745
  const toIndex = this.migrations.findIndex((m) => m.version === toVersion);
704
746
  if (fromIndex === -1 || toIndex === -1) {
705
747
  return false;
@@ -723,7 +765,9 @@ var MigrationTracker = class {
723
765
  const affectedVersions = [];
724
766
  let estimatedDuration = 0;
725
767
  if (canRollback) {
726
- const fromIndex = this.migrations.findIndex((m) => m.version === fromVersion);
768
+ const fromIndex = this.migrations.findIndex(
769
+ (m) => m.version === fromVersion
770
+ );
727
771
  const toIndex = this.migrations.findIndex((m) => m.version === toVersion);
728
772
  for (let i = fromIndex; i > toIndex; i--) {
729
773
  const migration = this.migrations[i];
@@ -779,12 +823,24 @@ var MigrationTracker = class {
779
823
  * Get migration statistics
780
824
  */
781
825
  getStatistics() {
782
- const applied = this.migrations.filter((m) => m.status === "applied").length;
826
+ const applied = this.migrations.filter(
827
+ (m) => m.status === "applied"
828
+ ).length;
783
829
  const failed = this.migrations.filter((m) => m.status === "failed").length;
784
- const pending = this.migrations.filter((m) => m.status === "pending").length;
785
- const rolledBack = this.migrations.filter((m) => m.status === "rolled-back").length;
786
- const totalDuration = this.migrations.reduce((sum, m) => sum + m.duration, 0);
787
- const totalAffected = this.migrations.reduce((sum, m) => sum + m.itemsAffected, 0);
830
+ const pending = this.migrations.filter(
831
+ (m) => m.status === "pending"
832
+ ).length;
833
+ const rolledBack = this.migrations.filter(
834
+ (m) => m.status === "rolled-back"
835
+ ).length;
836
+ const totalDuration = this.migrations.reduce(
837
+ (sum, m) => sum + m.duration,
838
+ 0
839
+ );
840
+ const totalAffected = this.migrations.reduce(
841
+ (sum, m) => sum + m.itemsAffected,
842
+ 0
843
+ );
788
844
  return {
789
845
  total: this.migrations.length,
790
846
  applied,
@@ -835,7 +891,147 @@ var MigrationTracker = class {
835
891
  status,
836
892
  hasError: !!error
837
893
  });
894
+ this.schedulePersist();
895
+ }
896
+ }
897
+ /**
898
+ * Persist tracker state with integrity chain verification metadata.
899
+ */
900
+ async saveToPersistence() {
901
+ if (!this.persistence) {
902
+ return;
903
+ }
904
+ const normalizedMigrations = this.migrations.map((migration) => ({
905
+ ...migration,
906
+ previousHash: void 0,
907
+ integrityHash: void 0
908
+ }));
909
+ const integrityEntries = [];
910
+ let previousHash = _MigrationTracker.INTEGRITY_ROOT;
911
+ for (const migration of normalizedMigrations) {
912
+ const hash = await this.computeDigestHex(
913
+ `${previousHash}|${this.stableStringify(migration)}`
914
+ );
915
+ integrityEntries.push({
916
+ recordId: migration.id,
917
+ previousHash,
918
+ hash
919
+ });
920
+ previousHash = hash;
921
+ }
922
+ const persistedMigrations = normalizedMigrations.map((migration, index) => ({
923
+ ...migration,
924
+ previousHash: integrityEntries[index]?.previousHash,
925
+ integrityHash: integrityEntries[index]?.hash
926
+ }));
927
+ const data = {
928
+ migrations: persistedMigrations,
929
+ snapshots: Array.from(this.snapshots.entries()).map(
930
+ ([recordId, snapshot]) => ({
931
+ recordId,
932
+ beforeHash: snapshot.beforeHash,
933
+ afterHash: snapshot.afterHash,
934
+ itemCount: snapshot.itemCount
935
+ })
936
+ ),
937
+ integrity: {
938
+ algorithm: "sha256-chain-v1",
939
+ entries: integrityEntries,
940
+ rootHash: previousHash
941
+ }
942
+ };
943
+ const envelope = {
944
+ version: 1,
945
+ updatedAt: Date.now(),
946
+ data
947
+ };
948
+ const serialize = this.persistence.serializer ?? ((value) => JSON.stringify(value));
949
+ await this.persistence.adapter.setItem(this.persistence.key, serialize(envelope));
950
+ }
951
+ /**
952
+ * Load tracker state and verify integrity chain.
953
+ */
954
+ async loadFromPersistence() {
955
+ if (!this.persistence) {
956
+ return { migrations: 0, snapshots: 0 };
957
+ }
958
+ const raw = await this.persistence.adapter.getItem(this.persistence.key);
959
+ if (!raw) {
960
+ return { migrations: 0, snapshots: 0 };
961
+ }
962
+ const deserialize = this.persistence.deserializer ?? ((value) => JSON.parse(value));
963
+ const envelope = deserialize(raw);
964
+ if (envelope.version !== 1 || !envelope.data) {
965
+ throw new Error("Invalid migration tracker persistence payload");
966
+ }
967
+ 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") {
968
+ throw new Error("Invalid migration tracker persistence structure");
969
+ }
970
+ if (envelope.data.integrity.algorithm !== "sha256-chain-v1") {
971
+ throw new Error("Unsupported migration integrity algorithm");
972
+ }
973
+ if (envelope.data.integrity.entries.length !== envelope.data.migrations.length) {
974
+ throw new Error("Migration integrity entry count mismatch");
975
+ }
976
+ const validatedMigrations = [];
977
+ let previousHash = _MigrationTracker.INTEGRITY_ROOT;
978
+ for (let i = 0; i < envelope.data.migrations.length; i++) {
979
+ const migration = envelope.data.migrations[i];
980
+ const integrity = envelope.data.integrity.entries[i];
981
+ if (!this.isValidMigrationRecord(migration)) {
982
+ throw new Error("Invalid persisted migration record");
983
+ }
984
+ if (!integrity || integrity.recordId !== migration.id || integrity.previousHash !== previousHash) {
985
+ throw new Error("Migration integrity chain mismatch");
986
+ }
987
+ const expectedHash = await this.computeDigestHex(
988
+ `${previousHash}|${this.stableStringify({
989
+ ...migration,
990
+ previousHash: void 0,
991
+ integrityHash: void 0
992
+ })}`
993
+ );
994
+ if (expectedHash !== integrity.hash) {
995
+ throw new Error("Migration integrity verification failed");
996
+ }
997
+ validatedMigrations.push({
998
+ ...migration,
999
+ previousHash: integrity.previousHash,
1000
+ integrityHash: integrity.hash
1001
+ });
1002
+ previousHash = expectedHash;
1003
+ }
1004
+ if (previousHash !== envelope.data.integrity.rootHash) {
1005
+ throw new Error("Migration integrity root hash mismatch");
1006
+ }
1007
+ const validatedSnapshots = /* @__PURE__ */ new Map();
1008
+ for (const snapshot of envelope.data.snapshots) {
1009
+ if (typeof snapshot.recordId !== "string" || typeof snapshot.beforeHash !== "string" || typeof snapshot.afterHash !== "string" || typeof snapshot.itemCount !== "number") {
1010
+ throw new Error("Invalid persisted migration snapshot");
1011
+ }
1012
+ validatedSnapshots.set(snapshot.recordId, {
1013
+ beforeHash: snapshot.beforeHash,
1014
+ afterHash: snapshot.afterHash,
1015
+ itemCount: snapshot.itemCount
1016
+ });
838
1017
  }
1018
+ this.migrations = validatedMigrations;
1019
+ this.snapshots = validatedSnapshots;
1020
+ logger.debug("[MigrationTracker] Loaded from persistence", {
1021
+ key: this.persistence.key,
1022
+ migrations: this.migrations.length,
1023
+ snapshots: this.snapshots.size
1024
+ });
1025
+ return { migrations: this.migrations.length, snapshots: this.snapshots.size };
1026
+ }
1027
+ /**
1028
+ * Remove persisted migration tracker state.
1029
+ */
1030
+ async clearPersistence() {
1031
+ if (!this.persistence) {
1032
+ return;
1033
+ }
1034
+ await this.persistence.adapter.removeItem(this.persistence.key);
839
1035
  }
840
1036
  /**
841
1037
  * Clear history (for testing)
@@ -843,6 +1039,7 @@ var MigrationTracker = class {
843
1039
  clear() {
844
1040
  this.migrations = [];
845
1041
  this.snapshots.clear();
1042
+ this.schedulePersist();
846
1043
  }
847
1044
  /**
848
1045
  * Get total migrations tracked
@@ -861,6 +1058,91 @@ var MigrationTracker = class {
861
1058
  return time >= start && time <= end;
862
1059
  });
863
1060
  }
1061
+ schedulePersist() {
1062
+ if (!this.persistence || this.persistence.autoPersist === false) {
1063
+ return;
1064
+ }
1065
+ if (this.persistTimer) {
1066
+ clearTimeout(this.persistTimer);
1067
+ }
1068
+ this.persistTimer = setTimeout(() => {
1069
+ void this.persistSafely();
1070
+ }, this.persistence.persistDebounceMs ?? 25);
1071
+ }
1072
+ async persistSafely() {
1073
+ if (!this.persistence) {
1074
+ return;
1075
+ }
1076
+ if (this.persistInFlight) {
1077
+ this.persistPending = true;
1078
+ return;
1079
+ }
1080
+ this.persistInFlight = true;
1081
+ try {
1082
+ await this.saveToPersistence();
1083
+ } catch (error) {
1084
+ logger.error("[MigrationTracker] Persistence write failed", {
1085
+ key: this.persistence.key,
1086
+ error: error instanceof Error ? error.message : String(error)
1087
+ });
1088
+ } finally {
1089
+ this.persistInFlight = false;
1090
+ const shouldRunAgain = this.persistPending;
1091
+ this.persistPending = false;
1092
+ if (shouldRunAgain) {
1093
+ void this.persistSafely();
1094
+ }
1095
+ }
1096
+ }
1097
+ isValidMigrationRecord(value) {
1098
+ if (typeof value !== "object" || value === null) {
1099
+ return false;
1100
+ }
1101
+ const record = value;
1102
+ const validDirection = record.direction === "up" || record.direction === "down";
1103
+ const validStatus = record.status === "pending" || record.status === "applied" || record.status === "failed" || record.status === "rolled-back";
1104
+ 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";
1105
+ }
1106
+ stableStringify(value) {
1107
+ if (value === null || typeof value !== "object") {
1108
+ return JSON.stringify(value);
1109
+ }
1110
+ if (Array.isArray(value)) {
1111
+ return `[${value.map((item) => this.stableStringify(item)).join(",")}]`;
1112
+ }
1113
+ const entries = Object.entries(value).sort(
1114
+ ([a], [b]) => a.localeCompare(b)
1115
+ );
1116
+ return `{${entries.map(
1117
+ ([key, entryValue]) => `${JSON.stringify(key)}:${this.stableStringify(entryValue)}`
1118
+ ).join(",")}}`;
1119
+ }
1120
+ async computeDigestHex(value) {
1121
+ if (globalThis.crypto?.subtle) {
1122
+ const bytes = new TextEncoder().encode(value);
1123
+ const normalized = bytes.buffer.slice(
1124
+ bytes.byteOffset,
1125
+ bytes.byteOffset + bytes.byteLength
1126
+ );
1127
+ const digest = await globalThis.crypto.subtle.digest(
1128
+ "SHA-256",
1129
+ normalized
1130
+ );
1131
+ return this.toHex(new Uint8Array(digest));
1132
+ }
1133
+ return this.fallbackDigestHex(value);
1134
+ }
1135
+ toHex(bytes) {
1136
+ return Array.from(bytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
1137
+ }
1138
+ fallbackDigestHex(value) {
1139
+ let hash = 2166136261;
1140
+ for (let i = 0; i < value.length; i++) {
1141
+ hash ^= value.charCodeAt(i);
1142
+ hash = Math.imul(hash, 16777619);
1143
+ }
1144
+ return (hash >>> 0).toString(16).padStart(8, "0");
1145
+ }
864
1146
  };
865
1147
 
866
1148
  exports.DataTransformer = DataTransformer;