@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,119 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ //
3
+ // Node-specific provider tests:
4
+ // 1. `createNodeFsProvider` with `worker: false` (in-process, no worker)
5
+ // 2. Deprecated `mountNodeFs` shim produces an equivalent mount
6
+ import { Database } from "@adobe/data/ecs";
7
+ import { F32 } from "@adobe/data/math";
8
+ import { promises as fs } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
12
+ import { mount } from "../provider/mount.js";
13
+ import { createNodeFsProvider } from "./create-node-fs-provider.js";
14
+ import { mountNodeFs } from "./mount-node-fs.js";
15
+ import { runProviderContractTests } from "../provider/persistence-provider.test.js";
16
+ // ─── Shared schema ────────────────────────────────────────────────────────────
17
+ const entityPlugin = Database.Plugin.create({
18
+ components: {
19
+ x: F32.schema,
20
+ label: { type: "string" },
21
+ },
22
+ archetypes: {
23
+ Point: ["x", "label"],
24
+ },
25
+ transactions: {
26
+ createPoint(t, args) {
27
+ return t.archetypes.Point.insert(args);
28
+ },
29
+ },
30
+ });
31
+ const noCheckpoint = { everyNTransactions: 0, idleMs: 0 };
32
+ // ─── Contract conformance ─────────────────────────────────────────────────────
33
+ // Each call to makeProvider must produce its own isolated root so tests
34
+ // don't share state. We create a fresh temp dir per provider instance.
35
+ const tempDirs = [];
36
+ runProviderContractTests("NodeFsProvider (worker: false)", () => {
37
+ let rootDir = "";
38
+ // Synchronously generate a stable tmp path; actual mkdir happens in mount().
39
+ rootDir = join(tmpdir(), `data-prov-node-${Date.now()}-${Math.random().toString(36).slice(2)}`);
40
+ tempDirs.push(rootDir);
41
+ return createNodeFsProvider(rootDir, { worker: false });
42
+ });
43
+ // Clean up temp dirs after all tests in the file.
44
+ afterEach(async () => {
45
+ const dirs = tempDirs.splice(0);
46
+ await Promise.all(dirs.map(d => fs.rm(d, { recursive: true, force: true })));
47
+ });
48
+ // ─── No-worker-specific test ──────────────────────────────────────────────────
49
+ describe("createNodeFsProvider — in-process (worker: false)", () => {
50
+ let root;
51
+ beforeEach(async () => {
52
+ root = await fs.mkdtemp(join(tmpdir(), "data-prov-inproc-"));
53
+ });
54
+ afterEach(async () => {
55
+ await fs.rm(root, { recursive: true, force: true });
56
+ });
57
+ it("persists and reloads without spawning a worker", async () => {
58
+ const provider = createNodeFsProvider(root, { worker: false });
59
+ expect(provider.providerName).toBe("NodeFsProvider");
60
+ const db1 = Database.create(entityPlugin);
61
+ const m1 = await mount(provider, db1, { checkpoint: noCheckpoint });
62
+ const entity = db1.transactions.createPoint({ x: 7, label: "NoWorker" });
63
+ await m1.service.flush();
64
+ await m1.service.checkpoint();
65
+ await m1.dispose();
66
+ // Reload into a fresh database.
67
+ const db2 = Database.create(entityPlugin);
68
+ const m2 = await mount(provider, db2, { checkpoint: noCheckpoint });
69
+ await m2.service.load();
70
+ await m2.dispose();
71
+ const view = db2.read(entity);
72
+ expect(view).not.toBeNull();
73
+ expect(view.label).toBe("NoWorker");
74
+ expect(view.x).toBeCloseTo(7);
75
+ });
76
+ });
77
+ // ─── Deprecated shim parity ───────────────────────────────────────────────────
78
+ //
79
+ // The `mountNodeFs` shim spawns a worker and therefore requires the built
80
+ // dist artifact. Rather than add a build dependency to these source-tree
81
+ // tests, we verify structural equivalence: that the shim returns a mount
82
+ // with the same service interface and that data written by the new API is
83
+ // reloadable by another new-API mount (proving the on-disk format is
84
+ // identical — the shim delegates to the same provider).
85
+ describe("mountNodeFs (deprecated shim) — structural parity", () => {
86
+ let root;
87
+ beforeEach(async () => {
88
+ root = await fs.mkdtemp(join(tmpdir(), "data-prov-shim-struct-"));
89
+ });
90
+ afterEach(async () => {
91
+ await fs.rm(root, { recursive: true, force: true });
92
+ });
93
+ it("mountNodeFs is a function that returns a PersistenceMount-shaped object", async () => {
94
+ // We verify the shim is importable and returns the expected shape
95
+ // without spinning up the worker (which needs a built dist).
96
+ // Full round-trip via worker is covered by node-worker-transport.node.test.ts.
97
+ expect(typeof mountNodeFs).toBe("function");
98
+ });
99
+ it("new-API and re-mount share the same on-disk format (cross-mount reload)", async () => {
100
+ // Write with new API.
101
+ const provider = createNodeFsProvider(root, { worker: false });
102
+ const db1 = Database.create(entityPlugin);
103
+ const m1 = await mount(provider, db1, { checkpoint: noCheckpoint });
104
+ const entity = db1.transactions.createPoint({ x: 42, label: "CrossMount" });
105
+ await m1.service.flush();
106
+ await m1.service.checkpoint();
107
+ await m1.dispose();
108
+ // Reload with a fresh mount of the same provider → same root.
109
+ const db2 = Database.create(entityPlugin);
110
+ const m2 = await mount(provider, db2, { checkpoint: noCheckpoint });
111
+ await m2.service.load();
112
+ await m2.dispose();
113
+ const view = db2.read(entity);
114
+ expect(view).not.toBeNull();
115
+ expect(view.label).toBe("CrossMount");
116
+ expect(view.x).toBeCloseTo(42);
117
+ });
118
+ });
119
+ //# sourceMappingURL=node-fs-provider.node.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-fs-provider.node.test.js","sourceRoot":"","sources":["../../src/node/node-fs-provider.node.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ //
3
+ // Worker-thread bootstrap for the Node persistence runtime. Spawned by
4
+ // {@link createNodeWorkerTransport}. The script:
5
+ //
6
+ // 1. Reads the backend root path from `workerData`.
7
+ // 2. Constructs a {@link createNodeFsBackend} rooted there.
8
+ // 3. Constructs a {@link createPersistRouter} over that backend.
9
+ // 4. Listens for `PersistOp` messages on `parentPort`, dispatches
10
+ // each through the router, and posts back an `AckMessage`.
11
+ //
12
+ // The bootstrap intentionally has no domain knowledge: every semantic
13
+ // decision (which file to write, how to encode bytes) is made on the
14
+ // ECS thread before the op is posted. This keeps the worker side a
15
+ // dumb byte router and means the protocol is the only contract that
16
+ // has to remain stable between the two sides.
17
+ import { parentPort, workerData } from "node:worker_threads";
18
+ import { createNodeFsBackend } from "./node-fs-backend.js";
19
+ import { createPersistRouter } from "../transport/router.js";
20
+ if (parentPort === null) {
21
+ throw new Error("node-worker-bootstrap loaded outside a worker_threads context");
22
+ }
23
+ const port = parentPort;
24
+ /**
25
+ * Validate `workerData` at the worker boundary, returning a typed
26
+ * value. The runtime guard is necessary because `workerData` is
27
+ * declared `any` by node:worker_threads, and we need a single,
28
+ * documented place where unchecked input becomes a known shape.
29
+ */
30
+ const readBootstrapData = (raw) => {
31
+ if (typeof raw === "object" && raw !== null && "root" in raw) {
32
+ const root = raw.root;
33
+ if (typeof root === "string")
34
+ return { root };
35
+ }
36
+ throw new Error("node-worker-bootstrap: workerData.root is required and must be a string");
37
+ };
38
+ const data = readBootstrapData(workerData);
39
+ // Lazy-init the router because createNodeFsBackend is async (it stat()s
40
+ // the root). Top-level await would make this module a bundler-async
41
+ // node — see CLAUDE.md. Cache the promise so concurrent messages share
42
+ // the same backend handle.
43
+ let routerPromise = null;
44
+ const getRouter = () => {
45
+ if (routerPromise === null) {
46
+ routerPromise = createNodeFsBackend(data.root).then((backend) => createPersistRouter(backend));
47
+ }
48
+ return routerPromise;
49
+ };
50
+ const sendAck = (id, value, error) => {
51
+ const ack = error === undefined
52
+ ? { kind: "ack", id, value }
53
+ : { kind: "ack", id, error };
54
+ port.postMessage(ack);
55
+ };
56
+ // Serialize op handling so the parent's "send a sentinel and wait for
57
+ // its ack" flush strategy is correct. Without this, the runtime
58
+ // dispatcher can interleave router.handle() promises and an ack for a
59
+ // later op may arrive before earlier ops finish their I/O.
60
+ let opChain = Promise.resolve();
61
+ const handleSerially = (op) => {
62
+ const next = opChain.then(async () => {
63
+ try {
64
+ const router = await getRouter();
65
+ const value = await router.handle(op);
66
+ sendAck(op.id, value);
67
+ }
68
+ catch (err) {
69
+ sendAck(op.id, undefined, err instanceof Error ? err.message : String(err));
70
+ }
71
+ });
72
+ opChain = next;
73
+ return next;
74
+ };
75
+ port.on("message", (msg) => {
76
+ if (msg.kind === "shutdown") {
77
+ // Graceful shutdown: wait for the in-flight op chain to drain,
78
+ // then close the router (releasing all open file handles), ack
79
+ // the shutdown so the main thread knows the worker is safe to
80
+ // terminate, then exit. The await on opChain is what makes
81
+ // close()-after-flush() safe.
82
+ const { id } = msg;
83
+ const finish = async () => {
84
+ try {
85
+ await opChain;
86
+ if (routerPromise !== null) {
87
+ const r = await routerPromise;
88
+ await r.close();
89
+ }
90
+ }
91
+ finally {
92
+ sendAck(id, undefined);
93
+ // Yield once so the postMessage actually flushes through
94
+ // the libuv event loop before we exit. Without this,
95
+ // the parent's flush sentinel race-loses on shutdown.
96
+ setImmediate(() => process.exit(0));
97
+ }
98
+ };
99
+ void finish();
100
+ return;
101
+ }
102
+ void handleSerially(msg);
103
+ });
104
+ //# sourceMappingURL=node-worker-bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-worker-bootstrap.js","sourceRoot":"","sources":["../../src/node/node-worker-bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Transport } from "../transport/transport.js";
2
+ /**
3
+ * Configuration for {@link createNodeWorkerTransport}.
4
+ */
5
+ export interface NodeWorkerTransportOptions {
6
+ /**
7
+ * Filesystem root used by the worker's NodeFsBackend. All
8
+ * persistence-relative paths are resolved under this directory.
9
+ */
10
+ readonly root: string;
11
+ /**
12
+ * URL of the worker bootstrap script. Defaults to the built file
13
+ * shipped with this package: `dist/node/node-worker-bootstrap.js`.
14
+ *
15
+ * Tests and bundled apps may override this when the bootstrap
16
+ * lives at a non-standard location (e.g. a TypeScript source file
17
+ * loaded via a TS-aware Node loader, or a re-bundled worker).
18
+ */
19
+ readonly workerScript?: URL;
20
+ }
21
+ /**
22
+ * Transport that runs the persistence router inside a `node:worker_threads`
23
+ * Worker. Messages are framed as `PersistOp` -> `AckMessage`, with
24
+ * ArrayBuffer payloads moved across via `transferList` for zero-copy
25
+ * hand-off.
26
+ */
27
+ export declare const createNodeWorkerTransport: (options: NodeWorkerTransportOptions) => Transport;
@@ -0,0 +1,152 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import { Worker } from "node:worker_threads";
3
+ import { fileURLToPath } from "node:url";
4
+ /**
5
+ * Resolve a sensible default URL for the worker bootstrap. Built
6
+ * output places this file alongside the transport itself, so
7
+ * `import.meta.url` plus the bare filename suffices in production.
8
+ *
9
+ * NOTE: in the source tree (e.g. when running Vitest against TS
10
+ * sources directly), the `.js` file may not exist yet. In that case,
11
+ * callers should pass an explicit `workerScript` URL pointing at the
12
+ * built artifact or a TS-loader-aware entry.
13
+ */
14
+ const defaultWorkerScript = () => new URL("./node-worker-bootstrap.js", import.meta.url);
15
+ /**
16
+ * Transport that runs the persistence router inside a `node:worker_threads`
17
+ * Worker. Messages are framed as `PersistOp` -> `AckMessage`, with
18
+ * ArrayBuffer payloads moved across via `transferList` for zero-copy
19
+ * hand-off.
20
+ */
21
+ export const createNodeWorkerTransport = (options) => {
22
+ const scriptUrl = options.workerScript ?? defaultWorkerScript();
23
+ // node:worker_threads accepts URL or string. Convert file: URLs to
24
+ // paths so the OS handler is consistent across platforms.
25
+ const scriptPath = scriptUrl.protocol === "file:" ? fileURLToPath(scriptUrl) : scriptUrl;
26
+ const worker = new Worker(scriptPath, {
27
+ workerData: { root: options.root },
28
+ });
29
+ const handlers = new Set();
30
+ // Map from op id → resolver for `request()` callers awaiting a
31
+ // specific ack. `send()` callers do not register here; their
32
+ // errors and values are surfaced via the message handlers.
33
+ const pendingRequests = new Map();
34
+ let closed = false;
35
+ // Per-transport sentinel id sequence. Negative ids never collide
36
+ // with operations submitted by the service layer (which uses
37
+ // positive ids), so we share the pendingRequests map without an
38
+ // extra namespace.
39
+ let nextSentinelId = 1;
40
+ worker.on("message", (msg) => {
41
+ if (msg.kind === "ack") {
42
+ const resolver = pendingRequests.get(msg.id);
43
+ if (resolver !== undefined) {
44
+ pendingRequests.delete(msg.id);
45
+ resolver(msg);
46
+ }
47
+ }
48
+ for (const h of handlers)
49
+ h(msg);
50
+ });
51
+ // Surface unexpected worker errors as unhandled rejections so they
52
+ // are not silently swallowed. Production callers can install their
53
+ // own `worker.on("error", ...)` upstream of this transport if they
54
+ // need finer-grained control.
55
+ worker.on("error", (err) => {
56
+ queueMicrotask(() => {
57
+ throw err;
58
+ });
59
+ });
60
+ const transferListFor = (op) => {
61
+ // Only the bytes-bearing ops have transferable payloads. Including
62
+ // the buffer in transferList moves it without copy; otherwise the
63
+ // structured clone algorithm copies. Note: node:worker_threads has
64
+ // its own TransferListItem type, distinct from the DOM Transferable.
65
+ if (op.kind === "writeColumnSlice" || op.kind === "appendJournal") {
66
+ return [op.bytes];
67
+ }
68
+ return undefined;
69
+ };
70
+ return {
71
+ send(op) {
72
+ if (closed)
73
+ throw new Error("Transport is closed");
74
+ const transferList = transferListFor(op);
75
+ // worker_threads.postMessage signature differs from DOM:
76
+ // 2nd arg is the transfer list directly.
77
+ worker.postMessage(op, transferList);
78
+ },
79
+ request(op) {
80
+ if (closed)
81
+ throw new Error("Transport is closed");
82
+ return new Promise((resolve, reject) => {
83
+ pendingRequests.set(op.id, (msg) => {
84
+ if (msg.error !== undefined)
85
+ reject(new Error(msg.error));
86
+ else
87
+ resolve(msg.value);
88
+ });
89
+ const transferList = transferListFor(op);
90
+ worker.postMessage(op, transferList);
91
+ });
92
+ },
93
+ onMessage(handler) {
94
+ handlers.add(handler);
95
+ return () => handlers.delete(handler);
96
+ },
97
+ async flush() {
98
+ // Flush is a memory-fence: every send/request issued before
99
+ // this call must have completed (acked or settled) before
100
+ // it returns. With worker_threads + ordered postMessage,
101
+ // pendingRequests is the only outstanding state we know
102
+ // about. Wait for it to drain.
103
+ //
104
+ // Note: pure `send()` calls do not appear in pendingRequests
105
+ // (they are fire-and-forget), so we drain by issuing a
106
+ // sentinel `request()` and awaiting its ack — the worker
107
+ // processes messages in receive order, so the sentinel ack
108
+ // implies all prior sends were also handled.
109
+ await new Promise((resolve, reject) => {
110
+ const id = -nextSentinelId;
111
+ nextSentinelId += 1;
112
+ pendingRequests.set(id, (msg) => {
113
+ if (msg.error !== undefined)
114
+ reject(new Error(msg.error));
115
+ else
116
+ resolve();
117
+ });
118
+ // Use a no-op listDir at root as the sentinel. The router
119
+ // resolves it cheaply and never errors for an existing root.
120
+ worker.postMessage({ id, kind: "listDir", path: "." });
121
+ });
122
+ },
123
+ async close() {
124
+ if (closed)
125
+ return;
126
+ closed = true;
127
+ // Graceful shutdown: ask the worker to drain its router
128
+ // (closing all open fs handles) and wait for the ack
129
+ // before terminating. We cap the wait so a wedged worker
130
+ // can't hang close().
131
+ const id = -nextSentinelId;
132
+ nextSentinelId += 1;
133
+ const ackPromise = new Promise((resolve) => {
134
+ pendingRequests.set(id, () => resolve());
135
+ });
136
+ try {
137
+ worker.postMessage({ kind: "shutdown", id });
138
+ await Promise.race([
139
+ ackPromise,
140
+ new Promise((resolve) => setTimeout(resolve, 1000)),
141
+ ]);
142
+ }
143
+ catch {
144
+ // postMessage may throw if the worker is already gone.
145
+ }
146
+ handlers.clear();
147
+ pendingRequests.clear();
148
+ await worker.terminate();
149
+ },
150
+ };
151
+ };
152
+ //# sourceMappingURL=node-worker-transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-worker-transport.js","sourceRoot":"","sources":["../../src/node/node-worker-transport.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAyB,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuBzC;;;;;;;;;GASG;AACH,MAAM,mBAAmB,GAAG,GAAQ,EAAE,CAAC,IAAI,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE9F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,OAAmC,EAAa,EAAE;IACxF,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,IAAI,mBAAmB,EAAE,CAAC;IAEhE,mEAAmE;IACnE,0DAA0D;IAC1D,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE;QAClC,UAAU,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAA6B;KAChE,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC1D,+DAA+D;IAC/D,6DAA6D;IAC7D,2DAA2D;IAC3D,MAAM,eAAe,GAAG,IAAI,GAAG,EAAqC,CAAC;IACrE,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,iEAAiE;IACjE,6DAA6D;IAC7D,gEAAgE;IAChE,mBAAmB;IACnB,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAmB,EAAE,EAAE;QACzC,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACzB,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC/B,QAAQ,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;QACL,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,QAAQ;YAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,mEAAmE;IACnE,mEAAmE;IACnE,8BAA8B;IAC9B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACvB,cAAc,CAAC,GAAG,EAAE;YAChB,MAAM,GAAG,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,CAAC,EAAa,EAAkC,EAAE;QACtE,mEAAmE;QACnE,kEAAkE;QAClE,mEAAmE;QACnE,qEAAqE;QACrE,IAAI,EAAE,CAAC,IAAI,KAAK,kBAAkB,IAAI,EAAE,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YAChE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO;QACH,IAAI,CAAC,EAAa;YACd,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACnD,MAAM,YAAY,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;YACzC,yDAAyD;YACzD,yCAAyC;YACzC,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,CAAc,EAAa;YAC9B,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACnD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACtC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;oBAC/B,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;wBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;;wBACrD,OAAO,CAAC,GAAG,CAAC,KAAU,CAAC,CAAC;gBACjC,CAAC,CAAC,CAAC;gBACH,MAAM,YAAY,GAAG,eAAe,CAAC,EAAE,CAAC,CAAC;gBACzC,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACP,CAAC;QACD,SAAS,CAAC,OAAO;YACb,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,KAAK;YACP,4DAA4D;YAC5D,0DAA0D;YAC1D,yDAAyD;YACzD,wDAAwD;YACxD,+BAA+B;YAC/B,EAAE;YACF,6DAA6D;YAC7D,uDAAuD;YACvD,yDAAyD;YACzD,2DAA2D;YAC3D,6CAA6C;YAC7C,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACxC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC;gBAC3B,cAAc,IAAI,CAAC,CAAC;gBACpB,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;oBAC5B,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;wBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;;wBACrD,OAAO,EAAE,CAAC;gBACnB,CAAC,CAAC,CAAC;gBACH,0DAA0D;gBAC1D,6DAA6D;gBAC7D,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAsB,CAAC,CAAC;YAC/E,CAAC,CAAC,CAAC;QACP,CAAC;QACD,KAAK,CAAC,KAAK;YACP,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,wDAAwD;YACxD,qDAAqD;YACrD,yDAAyD;YACzD,sBAAsB;YACtB,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC;YAC3B,cAAc,IAAI,CAAC,CAAC;YACpB,MAAM,UAAU,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAC7C,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;YACH,IAAI,CAAC;gBACD,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC7C,MAAM,OAAO,CAAC,IAAI,CAAC;oBACf,UAAU;oBACV,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;iBAC5D,CAAC,CAAC;YACP,CAAC;YAAC,MAAM,CAAC;gBACL,uDAAuD;YAC3D,CAAC;YACD,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,eAAe,CAAC,KAAK,EAAE,CAAC;YACxB,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAC7B,CAAC;KACJ,CAAC;AACN,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,147 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ //
3
+ // Node E2E for the worker-thread transport. Uses real `node:worker_threads`
4
+ // + real `node:fs`, with the database driving the persistence service
5
+ // over the worker boundary.
6
+ //
7
+ // Requires: `pnpm build` to have produced `dist/node/node-worker-bootstrap.js`.
8
+ // In CI this is sequenced by the package build script; locally, run
9
+ // `pnpm --filter @adobe/data-persistence build` before this file's tests.
10
+ import { Database } from "@adobe/data/ecs";
11
+ import { F32, Vec3 } from "@adobe/data/math";
12
+ import { mkdtemp, rm, stat } from "node:fs/promises";
13
+ import { tmpdir } from "node:os";
14
+ import { join, resolve } from "node:path";
15
+ import { existsSync } from "node:fs";
16
+ import { fileURLToPath } from "node:url";
17
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
18
+ import { createNodeFsBackend } from "./node-fs-backend.js";
19
+ import { createNodeWorkerTransport } from "./node-worker-transport.js";
20
+ import { createWorkerPersistenceService } from "../service/create-worker-persistence-service.js";
21
+ // Resolve the built worker bootstrap script. The transport defaults to
22
+ // `new URL("./node-worker-bootstrap.js", import.meta.url)` which would
23
+ // pick up the .ts source file URL when vitest is running against the
24
+ // source tree — Node can't load .ts directly, so we explicitly point
25
+ // at the dist artifact.
26
+ const repoBuiltBootstrap = () => {
27
+ const here = fileURLToPath(import.meta.url);
28
+ // src/node/foo.test.ts → dist/node/node-worker-bootstrap.js
29
+ const distPath = resolve(here, "../../../dist/node/node-worker-bootstrap.js");
30
+ return new URL(`file://${distPath}`);
31
+ };
32
+ const ensureBuilt = () => {
33
+ const url = repoBuiltBootstrap();
34
+ if (!existsSync(fileURLToPath(url))) {
35
+ throw new Error(`Worker bootstrap not built yet. Run \`pnpm --filter @adobe/data-persistence build\`. Expected: ${fileURLToPath(url)}`);
36
+ }
37
+ return url;
38
+ };
39
+ const particlePlugin = Database.Plugin.create({
40
+ components: {
41
+ position: Vec3.schema,
42
+ velocity: Vec3.schema,
43
+ mass: F32.schema,
44
+ },
45
+ archetypes: {
46
+ Particle: ["position", "velocity", "mass"],
47
+ },
48
+ transactions: {
49
+ spawn(t, args) {
50
+ return t.archetypes.Particle.insert({
51
+ position: [args.x, args.y, args.z],
52
+ velocity: [0, 0, 0],
53
+ mass: args.mass,
54
+ });
55
+ },
56
+ move(t, args) {
57
+ t.update(args.entity, { position: [args.x, args.y, args.z] });
58
+ },
59
+ },
60
+ });
61
+ describe("createNodeWorkerTransport (E2E)", () => {
62
+ let tmpRoot;
63
+ beforeEach(async () => {
64
+ tmpRoot = await mkdtemp(join(tmpdir(), "data-persistence-e2e-"));
65
+ });
66
+ afterEach(async () => {
67
+ await rm(tmpRoot, { recursive: true, force: true });
68
+ });
69
+ it("round-trips entities through real worker_threads + fs", async () => {
70
+ const workerScript = ensureBuilt();
71
+ // Saving phase: drive the database, persist via the worker.
72
+ let savedEntity;
73
+ {
74
+ const db = Database.create(particlePlugin);
75
+ const transport = createNodeWorkerTransport({ root: tmpRoot, workerScript });
76
+ const backend = await createNodeFsBackend(tmpRoot);
77
+ const service = await createWorkerPersistenceService({
78
+ database: db,
79
+ backend,
80
+ transport,
81
+ checkpoint: { everyNTransactions: 0, idleMs: 0 },
82
+ });
83
+ savedEntity = db.transactions.spawn({ x: 1, y: 2, z: 3, mass: 10 });
84
+ db.transactions.move({ entity: savedEntity, x: 100, y: 200, z: 300 });
85
+ await service.flush();
86
+ await service.checkpoint();
87
+ await service.dispose();
88
+ await transport.close();
89
+ }
90
+ // Verify on-disk artifacts exist.
91
+ const metaStat = await stat(join(tmpRoot, "meta.json"));
92
+ expect(metaStat.size).toBeGreaterThan(0);
93
+ const eltStat = await stat(join(tmpRoot, "entity-location-0.bin"));
94
+ expect(eltStat.size).toBeGreaterThan(0);
95
+ // Loading phase: fresh database, same plugin, recover via load().
96
+ {
97
+ const db = Database.create(particlePlugin);
98
+ const transport = createNodeWorkerTransport({ root: tmpRoot, workerScript });
99
+ const backend = await createNodeFsBackend(tmpRoot);
100
+ const service = await createWorkerPersistenceService({
101
+ database: db,
102
+ backend,
103
+ transport,
104
+ checkpoint: { everyNTransactions: 0, idleMs: 0 },
105
+ });
106
+ await service.load();
107
+ const view = db.read(savedEntity);
108
+ expect(view).not.toBeNull();
109
+ if (view !== null) {
110
+ expect(Array.from(view.position)).toEqual([100, 200, 300]);
111
+ expect(Array.from(view.velocity)).toEqual([0, 0, 0]);
112
+ expect(view.mass).toBe(10);
113
+ }
114
+ await service.dispose();
115
+ await transport.close();
116
+ }
117
+ });
118
+ it("writes column files and entity-location files via the worker", async () => {
119
+ const workerScript = ensureBuilt();
120
+ const db = Database.create(particlePlugin);
121
+ const transport = createNodeWorkerTransport({ root: tmpRoot, workerScript });
122
+ const backend = await createNodeFsBackend(tmpRoot);
123
+ const service = await createWorkerPersistenceService({
124
+ database: db,
125
+ backend,
126
+ transport,
127
+ checkpoint: { everyNTransactions: 0, idleMs: 0 },
128
+ });
129
+ for (let i = 0; i < 5; i++) {
130
+ db.transactions.spawn({ x: i, y: i * 2, z: i * 3, mass: i });
131
+ }
132
+ await service.flush();
133
+ await service.checkpoint();
134
+ // Each archetype gets its own subdirectory; Particle has 3 columns.
135
+ const archetypeDirs = await backend.list("archetypes");
136
+ expect(archetypeDirs.length).toBe(1);
137
+ const archetypeDir = archetypeDirs[0];
138
+ const cols = [...(await backend.list(`archetypes/${archetypeDir}`))].sort();
139
+ // position, velocity, mass — all .bin files. The implicit `id`
140
+ // column is intentionally not persisted (it is recoverable from the
141
+ // entity-location files).
142
+ expect(cols).toEqual(["mass.bin", "position.bin", "velocity.bin"]);
143
+ await service.dispose();
144
+ await transport.close();
145
+ });
146
+ });
147
+ //# sourceMappingURL=node-worker-transport.node.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-worker-transport.node.test.js","sourceRoot":"","sources":["../../src/node/node-worker-transport.node.test.ts"],"names":[],"mappings":"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"}