@adobe/data-persistence 0.9.90

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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +258 -0
  3. package/dist/backend/memory-backend.d.ts +6 -0
  4. package/dist/backend/memory-backend.js +92 -0
  5. package/dist/backend/memory-backend.js.map +1 -0
  6. package/dist/backend/memory-backend.test.d.ts +1 -0
  7. package/dist/backend/memory-backend.test.js +7 -0
  8. package/dist/backend/memory-backend.test.js.map +1 -0
  9. package/dist/backend/memory-file.d.ts +7 -0
  10. package/dist/backend/memory-file.js +90 -0
  11. package/dist/backend/memory-file.js.map +1 -0
  12. package/dist/backend/memory-file.test.d.ts +1 -0
  13. package/dist/backend/memory-file.test.js +5 -0
  14. package/dist/backend/memory-file.test.js.map +1 -0
  15. package/dist/backend/persistence-backend.conformance.d.ts +10 -0
  16. package/dist/backend/persistence-backend.conformance.js +104 -0
  17. package/dist/backend/persistence-backend.conformance.js.map +1 -0
  18. package/dist/backend/persistence-backend.d.ts +54 -0
  19. package/dist/backend/persistence-backend.js +3 -0
  20. package/dist/backend/persistence-backend.js.map +1 -0
  21. package/dist/backend/random-access-file.conformance.d.ts +20 -0
  22. package/dist/backend/random-access-file.conformance.js +156 -0
  23. package/dist/backend/random-access-file.conformance.js.map +1 -0
  24. package/dist/backend/random-access-file.d.ts +39 -0
  25. package/dist/backend/random-access-file.js +3 -0
  26. package/dist/backend/random-access-file.js.map +1 -0
  27. package/dist/backend/validate-rel-path.d.ts +14 -0
  28. package/dist/backend/validate-rel-path.js +44 -0
  29. package/dist/backend/validate-rel-path.js.map +1 -0
  30. package/dist/backend/validate-rel-path.test.d.ts +1 -0
  31. package/dist/backend/validate-rel-path.test.js +37 -0
  32. package/dist/backend/validate-rel-path.test.js.map +1 -0
  33. package/dist/browser/browser-worker-bootstrap.d.ts +1 -0
  34. package/dist/browser/browser-worker-bootstrap.js +97 -0
  35. package/dist/browser/browser-worker-bootstrap.js.map +1 -0
  36. package/dist/browser/browser-worker-transport.browser.test.d.ts +1 -0
  37. package/dist/browser/browser-worker-transport.browser.test.js +156 -0
  38. package/dist/browser/browser-worker-transport.browser.test.js.map +1 -0
  39. package/dist/browser/browser-worker-transport.d.ts +33 -0
  40. package/dist/browser/browser-worker-transport.js +120 -0
  41. package/dist/browser/browser-worker-transport.js.map +1 -0
  42. package/dist/browser/create-opfs-provider.d.ts +23 -0
  43. package/dist/browser/create-opfs-provider.js +49 -0
  44. package/dist/browser/create-opfs-provider.js.map +1 -0
  45. package/dist/browser/index.d.ts +6 -0
  46. package/dist/browser/index.js +12 -0
  47. package/dist/browser/index.js.map +1 -0
  48. package/dist/browser/mount-opfs.d.ts +12 -0
  49. package/dist/browser/mount-opfs.js +15 -0
  50. package/dist/browser/mount-opfs.js.map +1 -0
  51. package/dist/browser/opfs-backend-lock.browser.test.d.ts +1 -0
  52. package/dist/browser/opfs-backend-lock.browser.test.js +127 -0
  53. package/dist/browser/opfs-backend-lock.browser.test.js.map +1 -0
  54. package/dist/browser/opfs-backend.d.ts +6 -0
  55. package/dist/browser/opfs-backend.js +162 -0
  56. package/dist/browser/opfs-backend.js.map +1 -0
  57. package/dist/browser/opfs-lock-test-worker.d.ts +1 -0
  58. package/dist/browser/opfs-lock-test-worker.js +50 -0
  59. package/dist/browser/opfs-lock-test-worker.js.map +1 -0
  60. package/dist/browser/opfs-sync-file.d.ts +10 -0
  61. package/dist/browser/opfs-sync-file.js +58 -0
  62. package/dist/browser/opfs-sync-file.js.map +1 -0
  63. package/dist/encoder/create-column-encoder.d.ts +15 -0
  64. package/dist/encoder/create-column-encoder.js +110 -0
  65. package/dist/encoder/create-column-encoder.js.map +1 -0
  66. package/dist/encoder/journal-snapshot-codec.d.ts +21 -0
  67. package/dist/encoder/journal-snapshot-codec.js +103 -0
  68. package/dist/encoder/journal-snapshot-codec.js.map +1 -0
  69. package/dist/encoder/journal-snapshot-codec.test.d.ts +1 -0
  70. package/dist/encoder/journal-snapshot-codec.test.js +96 -0
  71. package/dist/encoder/journal-snapshot-codec.test.js.map +1 -0
  72. package/dist/encoder/types.d.ts +72 -0
  73. package/dist/encoder/types.js +3 -0
  74. package/dist/encoder/types.js.map +1 -0
  75. package/dist/index.d.ts +24 -0
  76. package/dist/index.js +15 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/journal/journal-codec.d.ts +28 -0
  79. package/dist/journal/journal-codec.js +82 -0
  80. package/dist/journal/journal-codec.js.map +1 -0
  81. package/dist/journal/journal-codec.test.d.ts +1 -0
  82. package/dist/journal/journal-codec.test.js +106 -0
  83. package/dist/journal/journal-codec.test.js.map +1 -0
  84. package/dist/journal/journal-entry.d.ts +63 -0
  85. package/dist/journal/journal-entry.js +28 -0
  86. package/dist/journal/journal-entry.js.map +1 -0
  87. package/dist/node/create-node-fs-provider.d.ts +34 -0
  88. package/dist/node/create-node-fs-provider.js +48 -0
  89. package/dist/node/create-node-fs-provider.js.map +1 -0
  90. package/dist/node/index.d.ts +6 -0
  91. package/dist/node/index.js +10 -0
  92. package/dist/node/index.js.map +1 -0
  93. package/dist/node/mount-node-fs.d.ts +12 -0
  94. package/dist/node/mount-node-fs.js +15 -0
  95. package/dist/node/mount-node-fs.js.map +1 -0
  96. package/dist/node/node-fs-backend-lock.node.test.d.ts +1 -0
  97. package/dist/node/node-fs-backend-lock.node.test.js +160 -0
  98. package/dist/node/node-fs-backend-lock.node.test.js.map +1 -0
  99. package/dist/node/node-fs-backend.d.ts +21 -0
  100. package/dist/node/node-fs-backend.js +186 -0
  101. package/dist/node/node-fs-backend.js.map +1 -0
  102. package/dist/node/node-fs-backend.node.test.d.ts +1 -0
  103. package/dist/node/node-fs-backend.node.test.js +17 -0
  104. package/dist/node/node-fs-backend.node.test.js.map +1 -0
  105. package/dist/node/node-fs-file.d.ts +6 -0
  106. package/dist/node/node-fs-file.js +57 -0
  107. package/dist/node/node-fs-file.js.map +1 -0
  108. package/dist/node/node-fs-file.node.test.d.ts +1 -0
  109. package/dist/node/node-fs-file.node.test.js +13 -0
  110. package/dist/node/node-fs-file.node.test.js.map +1 -0
  111. package/dist/node/node-fs-provider.node.test.d.ts +1 -0
  112. package/dist/node/node-fs-provider.node.test.js +119 -0
  113. package/dist/node/node-fs-provider.node.test.js.map +1 -0
  114. package/dist/node/node-worker-bootstrap.d.ts +1 -0
  115. package/dist/node/node-worker-bootstrap.js +104 -0
  116. package/dist/node/node-worker-bootstrap.js.map +1 -0
  117. package/dist/node/node-worker-transport.d.ts +27 -0
  118. package/dist/node/node-worker-transport.js +152 -0
  119. package/dist/node/node-worker-transport.js.map +1 -0
  120. package/dist/node/node-worker-transport.node.test.d.ts +1 -0
  121. package/dist/node/node-worker-transport.node.test.js +147 -0
  122. package/dist/node/node-worker-transport.node.test.js.map +1 -0
  123. package/dist/perftest/perf-harness.d.ts +74 -0
  124. package/dist/perftest/perf-harness.js +147 -0
  125. package/dist/perftest/perf-harness.js.map +1 -0
  126. package/dist/perftest/persistence-perf.d.ts +2 -0
  127. package/dist/perftest/persistence-perf.js +290 -0
  128. package/dist/perftest/persistence-perf.js.map +1 -0
  129. package/dist/perftest/run.d.ts +1 -0
  130. package/dist/perftest/run.js +28 -0
  131. package/dist/perftest/run.js.map +1 -0
  132. package/dist/provider/create-memory-provider.d.ts +16 -0
  133. package/dist/provider/create-memory-provider.js +44 -0
  134. package/dist/provider/create-memory-provider.js.map +1 -0
  135. package/dist/provider/mount.d.ts +20 -0
  136. package/dist/provider/mount.js +20 -0
  137. package/dist/provider/mount.js.map +1 -0
  138. package/dist/provider/persistence-provider.d.ts +63 -0
  139. package/dist/provider/persistence-provider.js +3 -0
  140. package/dist/provider/persistence-provider.js.map +1 -0
  141. package/dist/provider/persistence-provider.test.d.ts +9 -0
  142. package/dist/provider/persistence-provider.test.js +139 -0
  143. package/dist/provider/persistence-provider.test.js.map +1 -0
  144. package/dist/service/component-nonpersistent-durability.test.d.ts +1 -0
  145. package/dist/service/component-nonpersistent-durability.test.js +57 -0
  146. package/dist/service/component-nonpersistent-durability.test.js.map +1 -0
  147. package/dist/service/create-incremental-persistence-service.d.ts +7 -0
  148. package/dist/service/create-incremental-persistence-service.js +960 -0
  149. package/dist/service/create-incremental-persistence-service.js.map +1 -0
  150. package/dist/service/create-worker-persistence-service.d.ts +1 -0
  151. package/dist/service/create-worker-persistence-service.js +7 -0
  152. package/dist/service/create-worker-persistence-service.js.map +1 -0
  153. package/dist/service/create-worker-persistence-service.test.d.ts +1 -0
  154. package/dist/service/create-worker-persistence-service.test.js +291 -0
  155. package/dist/service/create-worker-persistence-service.test.js.map +1 -0
  156. package/dist/service/entity-location-cache.d.ts +15 -0
  157. package/dist/service/entity-location-cache.js +75 -0
  158. package/dist/service/entity-location-cache.js.map +1 -0
  159. package/dist/service/entity-location-density.test.d.ts +1 -0
  160. package/dist/service/entity-location-density.test.js +42 -0
  161. package/dist/service/entity-location-density.test.js.map +1 -0
  162. package/dist/service/incremental-persistence-service.d.ts +57 -0
  163. package/dist/service/incremental-persistence-service.js +3 -0
  164. package/dist/service/incremental-persistence-service.js.map +1 -0
  165. package/dist/service/intermediate-relocation-durability.test.d.ts +1 -0
  166. package/dist/service/intermediate-relocation-durability.test.js +89 -0
  167. package/dist/service/intermediate-relocation-durability.test.js.map +1 -0
  168. package/dist/service/internal-access.d.ts +34 -0
  169. package/dist/service/internal-access.js +43 -0
  170. package/dist/service/internal-access.js.map +1 -0
  171. package/dist/service/journal-replay.test.d.ts +1 -0
  172. package/dist/service/journal-replay.test.js +424 -0
  173. package/dist/service/journal-replay.test.js.map +1 -0
  174. package/dist/service/journal-storage-durability.test.d.ts +1 -0
  175. package/dist/service/journal-storage-durability.test.js +171 -0
  176. package/dist/service/journal-storage-durability.test.js.map +1 -0
  177. package/dist/service/load-roundtrip.test.d.ts +1 -0
  178. package/dist/service/load-roundtrip.test.js +224 -0
  179. package/dist/service/load-roundtrip.test.js.map +1 -0
  180. package/dist/service/persistence-complexity.test.d.ts +1 -0
  181. package/dist/service/persistence-complexity.test.js +275 -0
  182. package/dist/service/persistence-complexity.test.js.map +1 -0
  183. package/dist/service/worker-persistence-service.d.ts +24 -0
  184. package/dist/service/worker-persistence-service.js +8 -0
  185. package/dist/service/worker-persistence-service.js.map +1 -0
  186. package/dist/transport/inprocess-transport.d.ts +15 -0
  187. package/dist/transport/inprocess-transport.js +64 -0
  188. package/dist/transport/inprocess-transport.js.map +1 -0
  189. package/dist/transport/inprocess-transport.test.d.ts +1 -0
  190. package/dist/transport/inprocess-transport.test.js +100 -0
  191. package/dist/transport/inprocess-transport.test.js.map +1 -0
  192. package/dist/transport/router.d.ts +12 -0
  193. package/dist/transport/router.js +146 -0
  194. package/dist/transport/router.js.map +1 -0
  195. package/dist/transport/transport.d.ts +105 -0
  196. package/dist/transport/transport.js +3 -0
  197. package/dist/transport/transport.js.map +1 -0
  198. package/package.json +59 -0
@@ -0,0 +1,146 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import { Entity } from "@adobe/data/ecs";
3
+ // One entity-location file per persistent quadrant (document = 0, settings = 2),
4
+ // each indexed by the entity's per-quadrant local index so it packs fully
5
+ // densely with no gaps.
6
+ const entityLocationFile = (quadrant) => `entity-location-${quadrant}.bin`;
7
+ const JOURNAL_FILE = "journal.bin";
8
+ const META_FILE = "meta.json";
9
+ // Packed entity-location entry: u32 (archetypeId + 1, so 0 = empty) + u32
10
+ // rowIndex. 8 bytes per persistent entity. An unwritten slot reads as the zero
11
+ // (empty) entry; deletions write a zero entry.
12
+ const ELT_STRIDE = 8;
13
+ const columnPath = (archetypeId, component) => `archetypes/${archetypeId}/${component}.bin`;
14
+ export const createPersistRouter = (backend) => {
15
+ const fileCache = new Map();
16
+ // Concurrent opens for the same path must share a single in-flight
17
+ // backend.open promise; otherwise each caller gets its own handle
18
+ // and writes interleave (the snapshotting memory backend in
19
+ // particular surfaces this as lost data).
20
+ const inflightOpens = new Map();
21
+ const openCached = (relPath) => {
22
+ const existing = fileCache.get(relPath);
23
+ if (existing !== undefined)
24
+ return Promise.resolve(existing);
25
+ const inflight = inflightOpens.get(relPath);
26
+ if (inflight !== undefined)
27
+ return inflight;
28
+ const pending = backend.open(relPath).then((file) => {
29
+ fileCache.set(relPath, file);
30
+ inflightOpens.delete(relPath);
31
+ return file;
32
+ });
33
+ inflightOpens.set(relPath, pending);
34
+ return pending;
35
+ };
36
+ const handle = async (op) => {
37
+ switch (op.kind) {
38
+ case "writeColumnSlice": {
39
+ const file = await openCached(columnPath(op.archetypeId, op.component));
40
+ await file.writeAt(op.rowOffset, new Uint8Array(op.bytes));
41
+ return undefined;
42
+ }
43
+ case "appendJournal": {
44
+ const file = await openCached(JOURNAL_FILE);
45
+ return file.appendAt(new Uint8Array(op.bytes));
46
+ }
47
+ case "writeEntityLocation": {
48
+ if (Entity.isNonPersistent(op.entity)) {
49
+ throw new Error(`writeEntityLocation: non-persistent entity not persistable (entity=${op.entity})`);
50
+ }
51
+ const file = await openCached(entityLocationFile(Entity.quadrantOf(op.entity)));
52
+ const entry = new ArrayBuffer(ELT_STRIDE);
53
+ const view = new DataView(entry);
54
+ // Store archetypeId biased by +1 so a zero-filled (never-written)
55
+ // slot reads as "empty" rather than a valid archetype 0 (the
56
+ // backend zero-fills any gap left before the first write).
57
+ view.setUint32(0, op.archetypeId + 1, true);
58
+ view.setUint32(4, op.rowIndex, true);
59
+ await file.writeAt(Entity.toLocalIndex(op.entity) * ELT_STRIDE, new Uint8Array(entry));
60
+ return undefined;
61
+ }
62
+ case "deleteEntityLocation": {
63
+ if (Entity.isNonPersistent(op.entity)) {
64
+ throw new Error(`deleteEntityLocation: non-persistent entity not persistable (entity=${op.entity})`);
65
+ }
66
+ const file = await openCached(entityLocationFile(Entity.quadrantOf(op.entity)));
67
+ // Zero entry = empty slot (biased archetype 0 means "absent").
68
+ const tombstone = new Uint8Array(ELT_STRIDE);
69
+ await file.writeAt(Entity.toLocalIndex(op.entity) * ELT_STRIDE, tombstone);
70
+ return undefined;
71
+ }
72
+ case "writeJournalSnapshot": {
73
+ // Always a full rewrite at offset 0; truncate removes any
74
+ // stale tail from a previous checkpoint with more rows.
75
+ const path = columnPath(op.archetypeId, op.component);
76
+ const file = await openCached(path);
77
+ const bytes = new Uint8Array(op.bytes);
78
+ await file.writeAt(0, bytes);
79
+ await file.truncate(bytes.byteLength);
80
+ return undefined;
81
+ }
82
+ case "checkpoint": {
83
+ // Atomic-ish: write the manifest to a temp file, then rename.
84
+ // OPFS rename is implemented as copy+delete in our backend
85
+ // (lossy of atomicity); on POSIX the rename is atomic.
86
+ const text = JSON.stringify(op.manifest);
87
+ const bytes = new TextEncoder().encode(text);
88
+ const tmp = META_FILE + ".tmp";
89
+ const tmpFile = await backend.open(tmp);
90
+ await tmpFile.truncate(0);
91
+ await tmpFile.writeAt(0, bytes);
92
+ await tmpFile.sync();
93
+ await tmpFile.close();
94
+ await backend.remove(META_FILE);
95
+ await backend.rename(tmp, META_FILE);
96
+ // Truncate the journal once the new manifest is durable.
97
+ const journal = await openCached(JOURNAL_FILE);
98
+ await journal.truncate(0);
99
+ await journal.sync();
100
+ return undefined;
101
+ }
102
+ case "readFile": {
103
+ // Open via openCached so subsequent writes share the handle.
104
+ // Backends create-on-open (matching POSIX O_CREAT semantics);
105
+ // non-existence is signaled by a zero-byte size. Callers that
106
+ // need to distinguish missing from empty should use `listDir`.
107
+ const file = await openCached(op.path);
108
+ const size = await file.size();
109
+ if (size === 0)
110
+ return { bytes: new ArrayBuffer(0) };
111
+ const data = await file.readAt(0, size);
112
+ // Copy into a fresh ArrayBuffer so it is transferable
113
+ // across postMessage and never aliases an internal buffer.
114
+ const out = new ArrayBuffer(data.byteLength);
115
+ new Uint8Array(out).set(data);
116
+ return { bytes: out };
117
+ }
118
+ case "listDir": {
119
+ try {
120
+ const entries = await backend.list(op.path);
121
+ return { entries };
122
+ }
123
+ catch {
124
+ return { entries: [] };
125
+ }
126
+ }
127
+ default: {
128
+ const exhaustive = op;
129
+ throw new Error(`Unknown PersistOp kind: ${exhaustive.kind}`);
130
+ }
131
+ }
132
+ };
133
+ const close = async () => {
134
+ for (const file of fileCache.values()) {
135
+ await file.close();
136
+ }
137
+ fileCache.clear();
138
+ // Release process-wide backend resources (lock files, etc.) AFTER
139
+ // file handles are closed: backends that use a sync access handle
140
+ // as their lock would otherwise see a stale handle still open
141
+ // when they try to release it.
142
+ await backend.dispose?.();
143
+ };
144
+ return { handle, close };
145
+ };
146
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/transport/router.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,iFAAiF;AACjF,0EAA0E;AAC1E,wBAAwB;AACxB,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,mBAAmB,QAAQ,MAAM,CAAC;AAC3F,MAAM,YAAY,GAAG,aAAa,CAAC;AACnC,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,0EAA0E;AAC1E,+EAA+E;AAC/E,+CAA+C;AAC/C,MAAM,UAAU,GAAG,CAAC,CAAC;AAErB,MAAM,UAAU,GAAG,CAAC,WAAmB,EAAE,SAAiB,EAAU,EAAE,CAClE,cAAc,WAAW,IAAI,SAAS,MAAM,CAAC;AAYjD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAA2B,EAAiB,EAAE;IAC9E,MAAM,SAAS,GAAG,IAAI,GAAG,EAA4B,CAAC;IACtD,mEAAmE;IACnE,kEAAkE;IAClE,4DAA4D;IAC5D,0CAA0C;IAC1C,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqC,CAAC;IAEnE,MAAM,UAAU,GAAG,CAAC,OAAe,EAA6B,EAAE;QAC9D,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAC;QAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAChD,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC7B,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpC,OAAO,OAAO,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,EAAE,EAAa,EAAoB,EAAE;QACrD,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;YACd,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACtB,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;gBACxE,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC3D,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,KAAK,eAAe,CAAC,CAAC,CAAC;gBACnB,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC;gBAC5C,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;YACnD,CAAC;YACD,KAAK,qBAAqB,CAAC,CAAC,CAAC;gBACzB,IAAI,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpC,MAAM,IAAI,KAAK,CAAC,sEAAsE,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;gBACxG,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChF,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;gBAC1C,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACjC,kEAAkE;gBAClE,6DAA6D;gBAC7D,2DAA2D;gBAC3D,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC5C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBACrC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;gBACvF,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,KAAK,sBAAsB,CAAC,CAAC,CAAC;gBAC1B,IAAI,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpC,MAAM,IAAI,KAAK,CAAC,uEAAuE,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;gBACzG,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,kBAAkB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAChF,+DAA+D;gBAC/D,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;gBAC7C,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC;gBAC3E,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,KAAK,sBAAsB,CAAC,CAAC,CAAC;gBAC1B,0DAA0D;gBAC1D,wDAAwD;gBACxD,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC;gBACtD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;gBACpC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;gBACvC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAC7B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACtC,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAChB,8DAA8D;gBAC9D,2DAA2D;gBAC3D,uDAAuD;gBACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;gBACzC,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAC7C,MAAM,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;gBAC/B,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBAChC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBAChC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBACrC,yDAAyD;gBACzD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC;gBAC/C,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,OAAO,SAAS,CAAC;YACrB,CAAC;YACD,KAAK,UAAU,CAAC,CAAC,CAAC;gBACd,6DAA6D;gBAC7D,8DAA8D;gBAC9D,8DAA8D;gBAC9D,+DAA+D;gBAC/D,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;gBACvC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC/B,IAAI,IAAI,KAAK,CAAC;oBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;gBACxC,sDAAsD;gBACtD,2DAA2D;gBAC3D,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC9B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;YAC1B,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACb,IAAI,CAAC;oBACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;oBAC5C,OAAO,EAAE,OAAO,EAAE,CAAC;gBACvB,CAAC;gBAAC,MAAM,CAAC;oBACL,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;gBAC3B,CAAC;YACL,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACN,MAAM,UAAU,GAAU,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,2BAA4B,UAA+B,CAAC,IAAI,EAAE,CAAC,CAAC;YACxF,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACpC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;QACD,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,kEAAkE;QAClE,kEAAkE;QAClE,8DAA8D;QAC9D,+BAA+B;QAC/B,MAAM,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;IAC9B,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC,CAAC"}
@@ -0,0 +1,105 @@
1
+ import type { Manifest } from "../encoder/types.js";
2
+ /**
3
+ * Operations sent from the ECS thread to the persistence worker. The
4
+ * worker is a dumb byte router; all semantic mapping (component → file,
5
+ * row → byte offset) is done by the encoder on the ECS thread before
6
+ * the op is posted.
7
+ */
8
+ export type PersistOp = {
9
+ readonly id: number;
10
+ readonly kind: "writeColumnSlice";
11
+ readonly archetypeId: number;
12
+ readonly component: string;
13
+ readonly rowOffset: number;
14
+ readonly bytes: ArrayBuffer;
15
+ } | {
16
+ readonly id: number;
17
+ readonly kind: "appendJournal";
18
+ readonly bytes: ArrayBuffer;
19
+ } | {
20
+ readonly id: number;
21
+ readonly kind: "writeEntityLocation";
22
+ readonly entity: number;
23
+ readonly archetypeId: number;
24
+ readonly rowIndex: number;
25
+ } | {
26
+ readonly id: number;
27
+ readonly kind: "deleteEntityLocation";
28
+ readonly entity: number;
29
+ } | {
30
+ readonly id: number;
31
+ readonly kind: "checkpoint";
32
+ readonly manifest: Manifest;
33
+ } | {
34
+ readonly id: number;
35
+ readonly kind: "writeJournalSnapshot";
36
+ readonly archetypeId: number;
37
+ readonly component: string;
38
+ /** Full snapshot buffer from encodeJournalSnapshot — always written at offset 0 and truncated to this length. */
39
+ readonly bytes: ArrayBuffer;
40
+ } | {
41
+ readonly id: number;
42
+ readonly kind: "readFile";
43
+ readonly path: string;
44
+ } | {
45
+ readonly id: number;
46
+ readonly kind: "listDir";
47
+ readonly path: string;
48
+ };
49
+ /**
50
+ * Reply payload for `readFile` requests. Carries the entire file
51
+ * contents as an ArrayBuffer (zero-length for empty or non-existent
52
+ * files; callers detect existence separately via `listDir`).
53
+ * Returned via `transport.request()`.
54
+ */
55
+ export interface ReadFileResult {
56
+ readonly bytes: ArrayBuffer;
57
+ }
58
+ /**
59
+ * Reply payload for `listDir` requests. Returns directory entry names
60
+ * (not full paths). Empty array when the directory does not exist.
61
+ */
62
+ export interface ListDirResult {
63
+ readonly entries: readonly string[];
64
+ }
65
+ /** Acknowledgement message sent from worker back to the ECS thread. */
66
+ export interface AckMessage {
67
+ readonly kind: "ack";
68
+ readonly id: number;
69
+ readonly value?: unknown;
70
+ readonly error?: string;
71
+ }
72
+ export type PersistMessage = AckMessage;
73
+ /**
74
+ * The transport layer between the ECS thread and the persistence
75
+ * worker. Three implementations exist:
76
+ *
77
+ * - InprocessTransport - direct in-thread call (Node servers, tests)
78
+ * - BrowserWorkerTransport - DOM Worker + postMessage
79
+ * - NodeWorkerTransport - node:worker_threads + parentPort
80
+ */
81
+ export interface Transport {
82
+ /**
83
+ * Send an operation that does not require a response.
84
+ */
85
+ send(op: PersistOp, transfer?: readonly Transferable[]): void;
86
+ /**
87
+ * Send an operation and await its acknowledgement.
88
+ */
89
+ request<T = unknown>(op: PersistOp, transfer?: readonly Transferable[]): Promise<T>;
90
+ /**
91
+ * Subscribe to incoming messages from the worker side. Returns an
92
+ * unsubscribe function.
93
+ */
94
+ onMessage(handler: (msg: PersistMessage) => void): () => void;
95
+ /**
96
+ * Wait for all currently-enqueued operations (sent via `send` or
97
+ * `request`) to complete. Used by the service to drain pending
98
+ * writes before checkpoints, snapshots, or shutdown.
99
+ */
100
+ flush(): Promise<void>;
101
+ /**
102
+ * Tear down the transport. Pending requests reject.
103
+ */
104
+ close(): Promise<void>;
105
+ }
@@ -0,0 +1,3 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ export {};
3
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA,uDAAuD"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@adobe/data-persistence",
3
+ "version": "0.9.90",
4
+ "description": "Worker-based incremental persistence layer for @adobe/data ECS over OPFS (browser) and node:fs (server).",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "private": false,
8
+ "publishConfig": {
9
+ "registry": "https://registry.npmjs.org/",
10
+ "access": "public"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "dependencies": {
18
+ "@adobe/data": "0.9.90"
19
+ },
20
+ "devDependencies": {
21
+ "@vitest/browser": "^1.6.0",
22
+ "playwright": "^1.46.1",
23
+ "typescript": "^5.8.3",
24
+ "vite": "^5.1.1",
25
+ "vitest": "^1.6.0"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.js",
30
+ "types": "./dist/index.d.ts"
31
+ },
32
+ "./node": {
33
+ "import": "./dist/node/index.js",
34
+ "types": "./dist/node/index.d.ts"
35
+ },
36
+ "./browser": {
37
+ "import": "./dist/browser/index.js",
38
+ "types": "./dist/browser/index.d.ts"
39
+ },
40
+ "./browser-worker": {
41
+ "import": "./dist/browser/browser-worker-bootstrap.js",
42
+ "types": "./dist/browser/browser-worker-bootstrap.d.ts"
43
+ },
44
+ "./node-worker": {
45
+ "import": "./dist/node/node-worker-bootstrap.js",
46
+ "types": "./dist/node/node-worker-bootstrap.d.ts"
47
+ }
48
+ },
49
+ "scripts": {
50
+ "build": "cp ../../LICENSE . 2>/dev/null || true; tsc -b",
51
+ "clean": "rm -rf dist node_modules",
52
+ "dev": "tsc -b -w --preserveWatchOutput",
53
+ "test": "pnpm exec playwright install chromium && vitest --run",
54
+ "test:node": "vitest --run --project=node",
55
+ "test:browser": "vitest --run --project=browser",
56
+ "perf": "tsc -b && node --expose-gc dist/perftest/run.js",
57
+ "typecheck": "tsc -b"
58
+ }
59
+ }