orez 0.2.26 → 0.2.29

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 (172) hide show
  1. package/dist/cf-do/worker.d.ts.map +1 -1
  2. package/dist/cf-do/worker.js +9 -1
  3. package/dist/cf-do/worker.js.map +1 -1
  4. package/dist/pg-proxy-do-backend.d.ts +2 -0
  5. package/dist/pg-proxy-do-backend.d.ts.map +1 -1
  6. package/dist/pg-proxy-do-backend.js +49 -7
  7. package/dist/pg-proxy-do-backend.js.map +1 -1
  8. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
  9. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
  10. package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
  11. package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
  12. package/dist/pg-sqlite-compiler/index.d.ts +12 -0
  13. package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
  14. package/dist/pg-sqlite-compiler/index.js +59 -0
  15. package/dist/pg-sqlite-compiler/index.js.map +1 -0
  16. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
  17. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
  18. package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
  19. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
  20. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
  21. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
  22. package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
  23. package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
  24. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
  25. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
  26. package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
  27. package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
  28. package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
  29. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
  30. package/dist/pg-sqlite-compiler/passes/index.js +39 -0
  31. package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
  32. package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
  33. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
  34. package/dist/pg-sqlite-compiler/passes/types.js +103 -0
  35. package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
  36. package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
  37. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
  38. package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
  39. package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
  40. package/dist/pg-sqlite-compiler/types.d.ts +55 -0
  41. package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
  42. package/dist/pg-sqlite-compiler/types.js +2 -0
  43. package/dist/pg-sqlite-compiler/types.js.map +1 -0
  44. package/package.json +8 -4
  45. package/src/admin/admin-data.test.ts +0 -348
  46. package/src/admin/http-proxy.ts +0 -252
  47. package/src/admin/log-store.ts +0 -192
  48. package/src/admin/server.ts +0 -471
  49. package/src/admin/ui.ts +0 -1322
  50. package/src/bench/proxy-throughput.bench.ts +0 -343
  51. package/src/bench/serial-mutations.bench.ts +0 -270
  52. package/src/browser.ts +0 -203
  53. package/src/cf-do/.wrangler/cache/cf.json +0 -1
  54. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  55. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  56. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  57. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
  58. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
  59. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
  60. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
  61. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
  62. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
  63. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
  64. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
  65. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
  66. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
  67. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
  68. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
  69. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
  70. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
  71. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
  72. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  73. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  74. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  75. package/src/cf-do/ARCHITECTURE.md +0 -83
  76. package/src/cf-do/watermark.test.ts +0 -103
  77. package/src/cf-do/watermark.ts +0 -118
  78. package/src/cf-do/worker.ts +0 -1033
  79. package/src/cf-do/wrangler.toml +0 -11
  80. package/src/cf-pglite/README.md +0 -19
  81. package/src/change-tracking.ts +0 -25
  82. package/src/child-process.test.ts +0 -147
  83. package/src/child-process.ts +0 -90
  84. package/src/cli-entry.ts +0 -72
  85. package/src/cli.test.ts +0 -38
  86. package/src/cli.ts +0 -1214
  87. package/src/config.ts +0 -150
  88. package/src/do-sql-tracking.test.ts +0 -19
  89. package/src/do-sql-tracking.ts +0 -19
  90. package/src/index.ts +0 -1215
  91. package/src/integration/integration.test.ts +0 -517
  92. package/src/integration/native-binary.guard.test.ts +0 -13
  93. package/src/integration/native-startup.test.ts +0 -44
  94. package/src/integration/replication-latency.test.ts +0 -428
  95. package/src/integration/restore-live-stress.test.ts +0 -433
  96. package/src/integration/restore-reset.test.ts +0 -400
  97. package/src/integration/restore.test.ts +0 -274
  98. package/src/integration/test-permissions.ts +0 -147
  99. package/src/load-config.ts +0 -46
  100. package/src/log.ts +0 -96
  101. package/src/mutex.ts +0 -47
  102. package/src/pg-proxy-browser.singledb.test.ts +0 -233
  103. package/src/pg-proxy-browser.ts +0 -2022
  104. package/src/pg-proxy-do-backend.test.ts +0 -3890
  105. package/src/pg-proxy-do-backend.ts +0 -7157
  106. package/src/pg-proxy.ts +0 -1087
  107. package/src/pglite-ipc.test.ts +0 -116
  108. package/src/pglite-ipc.ts +0 -266
  109. package/src/pglite-manager.ts +0 -557
  110. package/src/pglite-web-proxy.test.ts +0 -57
  111. package/src/pglite-web-proxy.ts +0 -221
  112. package/src/pglite-web-worker.ts +0 -152
  113. package/src/pglite-worker-thread.ts +0 -253
  114. package/src/port.ts +0 -25
  115. package/src/process-title.ts +0 -9
  116. package/src/recovery.ts +0 -155
  117. package/src/replication/change-tracker.test.ts +0 -357
  118. package/src/replication/change-tracker.ts +0 -279
  119. package/src/replication/handler.test.ts +0 -511
  120. package/src/replication/handler.ts +0 -1190
  121. package/src/replication/pgoutput-encoder.test.ts +0 -697
  122. package/src/replication/pgoutput-encoder.ts +0 -373
  123. package/src/replication/tcp-replication.test.ts +0 -876
  124. package/src/replication/zero-compat.test.ts +0 -1150
  125. package/src/restore-stress.test.ts +0 -188
  126. package/src/s3-local.ts +0 -203
  127. package/src/shim/hooks.mjs +0 -120
  128. package/src/shim/register.mjs +0 -4
  129. package/src/sqlite-mode/apply-mode.ts +0 -224
  130. package/src/sqlite-mode/index.ts +0 -15
  131. package/src/sqlite-mode/native-binary.ts +0 -89
  132. package/src/sqlite-mode/package-resolve.ts +0 -17
  133. package/src/sqlite-mode/resolve-mode.ts +0 -80
  134. package/src/sqlite-mode/shim-template.ts +0 -159
  135. package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
  136. package/src/sqlite-mode/types.ts +0 -30
  137. package/src/vite-plugin.ts +0 -67
  138. package/src/wasm-sqlite.test.ts +0 -537
  139. package/src/worker/browser-admin.ts +0 -52
  140. package/src/worker/browser-build-config.test.ts +0 -71
  141. package/src/worker/browser-build-config.ts +0 -109
  142. package/src/worker/browser-embed-admin.test.ts +0 -75
  143. package/src/worker/browser-embed.ts +0 -345
  144. package/src/worker/cf-patches.ts +0 -384
  145. package/src/worker/embed-integration.test.ts +0 -321
  146. package/src/worker/index.ts +0 -138
  147. package/src/worker/shims/fastify.test.ts +0 -255
  148. package/src/worker/shims/fastify.ts +0 -306
  149. package/src/worker/shims/http-service.test.ts +0 -355
  150. package/src/worker/shims/http-service.ts +0 -293
  151. package/src/worker/shims/node-stub.ts +0 -290
  152. package/src/worker/shims/oxfmt.ts +0 -3
  153. package/src/worker/shims/postgres-browser.ts +0 -59
  154. package/src/worker/shims/postgres-socket.test.ts +0 -576
  155. package/src/worker/shims/postgres-socket.ts +0 -310
  156. package/src/worker/shims/postgres.test.ts +0 -364
  157. package/src/worker/shims/postgres.ts +0 -1454
  158. package/src/worker/shims/sqlite-browser.test.ts +0 -233
  159. package/src/worker/shims/sqlite-browser.ts +0 -175
  160. package/src/worker/shims/sqlite.test.ts +0 -786
  161. package/src/worker/shims/sqlite.ts +0 -978
  162. package/src/worker/shims/stream-browser.ts +0 -15
  163. package/src/worker/shims/ws-browser.test.ts +0 -205
  164. package/src/worker/shims/ws-browser.ts +0 -248
  165. package/src/worker/shims/ws.test.ts +0 -288
  166. package/src/worker/shims/ws.ts +0 -467
  167. package/src/worker/shims/zero-process-env.ts +0 -11
  168. package/src/worker/types.ts +0 -75
  169. package/src/worker/worker-integration.test.ts +0 -223
  170. package/src/worker/worker.test.ts +0 -136
  171. package/src/worker/zero-cache-embed-cf.ts +0 -463
  172. package/src/worker/zero-cache-embed.ts +0 -277
@@ -1,384 +0,0 @@
1
- /**
2
- * zero-cache CF Workers patches.
3
- *
4
- * applies patches to @rocicorp/zero's internal files so zero-cache
5
- * can run in SINGLE_PROCESS mode on CF Workers where dynamic import()
6
- * doesn't work.
7
- *
8
- * five patches:
9
- * 1. worker-urls.js — replace file:// URLs with zero-worker:// identifiers
10
- * 2. server worker entrypoints — disable CLI auto-start blocks
11
- * 3. processes.js — replace dynamic import() with static worker module lookup
12
- * 4. write-worker-client.js — run zero-cache's replica writer in-process
13
- * 5. pgsql-parser — embed libpg-query wasm bytes for Workers
14
- *
15
- * usage in a post-build script:
16
- *
17
- * import { patchZeroCacheForCF } from 'orez/worker/cf-patches'
18
- * patchZeroCacheForCF('./node_modules')
19
- *
20
- * idempotent: safe to run multiple times.
21
- */
22
-
23
- import { readFileSync, writeFileSync, existsSync } from 'node:fs'
24
- import { resolve } from 'node:path'
25
-
26
- export function patchZeroCacheForCF(nodeModulesPath: string): void {
27
- const zcBase = resolve(nodeModulesPath, '@rocicorp', 'zero', 'out', 'zero-cache', 'src')
28
-
29
- patchWorkerUrls(zcBase)
30
- patchWorkerEntrypoints(zcBase)
31
- patchProcesses(zcBase)
32
- patchWriteWorkerClient(zcBase)
33
- patchPgsqlParserWasm(nodeModulesPath)
34
- }
35
-
36
- function patchWorkerUrls(zcBase: string): void {
37
- const workerUrlsPath = resolve(zcBase, 'server', 'worker-urls.js')
38
- if (!existsSync(workerUrlsPath)) {
39
- console.warn('[orez] worker-urls.js not found at', workerUrlsPath)
40
- return
41
- }
42
-
43
- const content = readFileSync(workerUrlsPath, 'utf-8')
44
-
45
- // skip if already patched
46
- if (content.includes('zero-worker://')) {
47
- return
48
- }
49
-
50
- writeFileSync(
51
- workerUrlsPath,
52
- `// patched by orez for CF Workers (replaces file:// URLs with identifiers)
53
- const u = (n) => new URL("zero-worker://" + n);
54
- export const MAIN_URL = u("main");
55
- export const CHANGE_STREAMER_URL = u("change-streamer");
56
- export const REAPER_URL = u("reaper");
57
- export const REPLICATOR_URL = u("replicator");
58
- export const SYNCER_URL = u("syncer");
59
- // write-worker is spawned via 'new Worker()' (node:worker_threads), not via
60
- // childWorker() — it uses its own URL → worker resolution path. we still expose
61
- // it here so write-worker-client.js can import it without throwing.
62
- export const WRITE_WORKER_URL = u("write-worker");
63
- `
64
- )
65
- console.log('[orez] patched zero-cache worker-urls.js')
66
- }
67
-
68
- function patchWorkerEntrypoints(zcBase: string): void {
69
- const entrypoints = ['main', 'change-streamer', 'reaper', 'replicator', 'syncer']
70
-
71
- for (const entrypoint of entrypoints) {
72
- const entrypointPath = resolve(zcBase, 'server', `${entrypoint}.js`)
73
- if (!existsSync(entrypointPath)) {
74
- console.warn('[orez] zero-cache worker entrypoint not found at', entrypointPath)
75
- continue
76
- }
77
-
78
- let code = readFileSync(entrypointPath, 'utf-8')
79
- if (code.includes('orez-disable-autostart')) {
80
- continue
81
- }
82
-
83
- const next = code.replace(
84
- /if \(!singleProcessMode\(\)\) exitAfter\(\(\) => runWorker\(must\(parentWorker\), process\.env(?:, \.\.\.process\.argv\.slice\(2\))?\)\);/g,
85
- '// orez-disable-autostart: childWorker invokes runWorker explicitly in CF embeds.'
86
- )
87
-
88
- if (next === code) {
89
- console.warn(
90
- `[orez] could not find auto-start guard in ${entrypoint}.js. ` +
91
- 'zero-cache version may have changed — check compatibility.'
92
- )
93
- continue
94
- }
95
-
96
- code = next
97
- writeFileSync(entrypointPath, code)
98
- console.log(`[orez] patched zero-cache ${entrypoint}.js (disabled auto-start)`)
99
- }
100
- }
101
-
102
- function patchProcesses(zcBase: string): void {
103
- const processesPath = resolve(zcBase, 'types', 'processes.js')
104
- if (!existsSync(processesPath)) {
105
- console.warn('[orez] processes.js not found at', processesPath)
106
- return
107
- }
108
-
109
- let code = readFileSync(processesPath, 'utf-8')
110
-
111
- // skip if already patched
112
- if (code.includes('__zc_workers')) {
113
- return
114
- }
115
-
116
- // add static imports of all zero-cache worker modules at the top.
117
- // these are relative to processes.js location in @rocicorp/zero.
118
- // NOTE: mutator.js and write-worker.js don't export a default `runWorker`
119
- // (mutator runs via auto-run guard, write-worker spawns via node:worker_threads
120
- // not via childWorker()), so they're not in the lookup table.
121
- const workerImports = `\
122
- // patched by orez for CF Workers (static imports replace dynamic import())
123
- import { default as __zc_main } from "../server/main.js";
124
- import { default as __zc_change_streamer } from "../server/change-streamer.js";
125
- import { default as __zc_reaper } from "../server/reaper.js";
126
- import { default as __zc_replicator } from "../server/replicator.js";
127
- import { default as __zc_syncer } from "../server/syncer.js";
128
- const __zc_workers = {
129
- "main": __zc_main,
130
- "change-streamer": __zc_change_streamer,
131
- "reaper": __zc_reaper,
132
- "replicator": __zc_replicator,
133
- "syncer": __zc_syncer,
134
- };
135
- `
136
-
137
- // replace the dynamic import in childWorker with a synchronous lookup.
138
- // original: import(moduleUrl.href).then(async ({ default: runWorker }) => ...
139
- // patched: lookup __zc_workers by name, then continue as before
140
- const dynamicImportPattern =
141
- 'import(moduleUrl.href).then(async ({ default: runWorker })'
142
- const staticLookup =
143
- '((async () => { ' +
144
- 'const _name = moduleUrl.hostname || moduleUrl.pathname.split("/").pop()?.replace(".js",""); ' +
145
- 'if (process.env.OREZ_DEBUG_WIRE === "1" || globalThis.__OREZ_DEBUG_WIRE__ === true) console.debug("[orez-zc-worker] start", _name, args); ' +
146
- 'const runWorker = __zc_workers[_name]; ' +
147
- 'if (!runWorker) throw new Error("orez: unknown zero-cache worker: " + _name + " (available: " + Object.keys(__zc_workers).join(", ") + ")"); ' +
148
- 'return { default: runWorker, name: _name }; ' +
149
- '})()).then(async ({ default: runWorker, name })'
150
-
151
- if (!code.includes(dynamicImportPattern)) {
152
- console.warn(
153
- '[orez] could not find dynamic import pattern in processes.js. ' +
154
- 'zero-cache version may have changed — check compatibility.'
155
- )
156
- return
157
- }
158
-
159
- code = workerImports + code.replace(dynamicImportPattern, staticLookup)
160
- writeFileSync(processesPath, code)
161
- console.log('[orez] patched zero-cache processes.js (static worker imports)')
162
- }
163
-
164
- function patchWriteWorkerClient(zcBase: string): void {
165
- const clientPath = resolve(zcBase, 'services', 'replicator', 'write-worker-client.js')
166
- if (!existsSync(clientPath)) {
167
- console.warn('[orez] write-worker-client.js not found at', clientPath)
168
- return
169
- }
170
-
171
- let code = readFileSync(clientPath, 'utf-8')
172
- if (
173
- code.includes('orez-inline-write-worker') &&
174
- code.includes('__orez_zero_sqlite_role')
175
- ) {
176
- return
177
- }
178
-
179
- if (
180
- !code.includes('orez-inline-write-worker') &&
181
- !code.includes('import { Worker } from "node:worker_threads";')
182
- ) {
183
- console.warn(
184
- '[orez] could not find node:worker_threads import in write-worker-client.js. ' +
185
- 'zero-cache version may have changed — check compatibility.'
186
- )
187
- return
188
- }
189
-
190
- writeFileSync(
191
- clientPath,
192
- `// patched by orez for CF Workers (orez-inline-write-worker)
193
- import { must } from "../../../../shared/src/must.js";
194
- import { Database } from "../../../../zqlite/src/db.js";
195
- import { createLogContext } from "../../server/logging.js";
196
- import { StatementRunner } from "../../db/statements.js";
197
- import { getSubscriptionState } from "./schema/replication-state.js";
198
- import { ChangeProcessor } from "./change-processor.js";
199
-
200
- function applyPragmas(db, pragmas) {
201
- db.pragma(\`busy_timeout = \${pragmas.busyTimeout}\`);
202
- db.pragma(\`analysis_limit = \${pragmas.analysisLimit}\`);
203
- if (pragmas.walAutocheckpoint !== void 0) {
204
- db.pragma(\`wal_autocheckpoint = \${pragmas.walAutocheckpoint}\`);
205
- }
206
- }
207
-
208
- function createAPI(onWriteError) {
209
- let db;
210
- let runner;
211
- let processor;
212
- let mode;
213
- let lc;
214
-
215
- function createProcessor() {
216
- processor = new ChangeProcessor(must(runner), must(mode), (_lc, err) => {
217
- onWriteError(err instanceof Error ? err : new Error(String(err)));
218
- });
219
- }
220
-
221
- return {
222
- init(dbPath, cpMode, pragmas, logConfig) {
223
- lc = createLogContext({ log: logConfig }, { worker: "write-worker" });
224
- const previousRole = globalThis.__orez_zero_sqlite_role;
225
- globalThis.__orez_zero_sqlite_role = "replica-writer";
226
- try {
227
- db = new Database(lc, dbPath);
228
- } finally {
229
- if (previousRole === void 0) {
230
- delete globalThis.__orez_zero_sqlite_role;
231
- } else {
232
- globalThis.__orez_zero_sqlite_role = previousRole;
233
- }
234
- }
235
- applyPragmas(db, pragmas);
236
- runner = new StatementRunner(db);
237
- mode = cpMode;
238
- createProcessor();
239
- },
240
- getSubscriptionState() {
241
- return getSubscriptionState(must(runner));
242
- },
243
- processMessage(downstream) {
244
- return must(processor).processMessage(must(lc), downstream);
245
- },
246
- abort() {
247
- must(processor).abort(must(lc));
248
- createProcessor();
249
- },
250
- stop() {
251
- db?.close();
252
- db = void 0;
253
- runner = void 0;
254
- processor = void 0;
255
- },
256
- };
257
- }
258
-
259
- class ThreadWriteWorkerClient {
260
- #api;
261
- #errorHandler = () => {};
262
- #writeError = null;
263
-
264
- constructor() {
265
- this.#api = createAPI((err) => {
266
- this.#writeError = err;
267
- this.#errorHandler(err);
268
- });
269
- }
270
-
271
- async #call(method, args) {
272
- if (this.#writeError) throw this.#writeError;
273
- try {
274
- const result = this.#api[method](...args);
275
- if (this.#writeError) throw this.#writeError;
276
- return result;
277
- } catch (err) {
278
- throw err instanceof Error ? err : new Error(String(err));
279
- }
280
- }
281
-
282
- init(dbPath, mode, pragmas, logConfig) {
283
- return this.#call("init", [dbPath, mode, pragmas, logConfig]);
284
- }
285
-
286
- getSubscriptionState() {
287
- return this.#call("getSubscriptionState", []);
288
- }
289
-
290
- processMessage(downstream) {
291
- return this.#call("processMessage", [downstream]);
292
- }
293
-
294
- abort() {
295
- if (!this.#writeError) {
296
- try {
297
- this.#api.abort();
298
- } catch {
299
- }
300
- }
301
- }
302
-
303
- async stop() {
304
- await this.#call("stop", []);
305
- }
306
-
307
- onError(handler) {
308
- this.#errorHandler = handler;
309
- }
310
- }
311
-
312
- export { ThreadWriteWorkerClient, applyPragmas };
313
- `
314
- )
315
- console.log('[orez] patched zero-cache write-worker-client.js (inline writer)')
316
- }
317
-
318
- function patchPgsqlParserWasm(nodeModulesPath: string): void {
319
- const parserIndexPath = resolve(nodeModulesPath, 'libpg-query', 'wasm', 'index.js')
320
- const wasmPath = resolve(nodeModulesPath, 'libpg-query', 'wasm', 'libpg-query.wasm')
321
-
322
- if (!existsSync(parserIndexPath) || !existsSync(wasmPath)) {
323
- console.warn('[orez] libpg-query wasm files not found under', nodeModulesPath)
324
- return
325
- }
326
-
327
- let code = readFileSync(parserIndexPath, 'utf-8')
328
- if (
329
- code.includes('orez-libpg-query-wasm-binary') &&
330
- code.includes('__orezLibPgQueryInit')
331
- ) {
332
- return
333
- }
334
- if (code.includes('orez-libpg-query-wasm-binary')) {
335
- console.warn(
336
- '[orez] libpg-query wasm loader already patched with an older shape. ' +
337
- 'Reinstall libpg-query or restore node_modules before re-patching.'
338
- )
339
- return
340
- }
341
-
342
- const pattern = 'const initPromise = PgQueryModule().then((module) => {'
343
- if (!code.includes(pattern)) {
344
- console.warn(
345
- '[orez] could not find PgQueryModule init in libpg-query wasm index. ' +
346
- 'pgsql-parser version may have changed — check compatibility.'
347
- )
348
- return
349
- }
350
-
351
- const wasmBase64 = readFileSync(wasmPath).toString('base64')
352
- const replacement = `\
353
- // orez-libpg-query-wasm-binary: embed parser wasm for CF Workers.
354
- const __orezLibPgQueryWasmBase64 = '${wasmBase64}';
355
- function __orezLibPgQueryWasmBinary() {
356
- const decode = globalThis.atob
357
- ? globalThis.atob(__orezLibPgQueryWasmBase64)
358
- : Buffer.from(__orezLibPgQueryWasmBase64, 'base64').toString('binary');
359
- const bytes = new Uint8Array(decode.length);
360
- for (let i = 0; i < decode.length; i++) bytes[i] = decode.charCodeAt(i);
361
- return bytes;
362
- }
363
- try {
364
- const g = globalThis;
365
- if (g.self && !g.self.location) g.self.location = { href: 'https://orez.local/libpg-query.js' };
366
- if (!g.location) g.location = { href: 'https://orez.local/libpg-query.js' };
367
- }
368
- catch {
369
- }
370
- const __orezLibPgQueryPreviousProcessType = globalThis.process?.type;
371
- if (globalThis.process && !globalThis.process.type) globalThis.process.type = 'renderer';
372
- const __orezLibPgQueryInit = PgQueryModule({ wasmBinary: __orezLibPgQueryWasmBinary() });
373
- if (globalThis.process && __orezLibPgQueryPreviousProcessType === undefined) {
374
- delete globalThis.process.type;
375
- }
376
- else if (globalThis.process) {
377
- globalThis.process.type = __orezLibPgQueryPreviousProcessType;
378
- }
379
- const initPromise = __orezLibPgQueryInit.then((module) => {`
380
-
381
- code = code.replace(pattern, replacement)
382
- writeFileSync(parserIndexPath, code)
383
- console.log('[orez] patched libpg-query wasm loader (embedded wasm bytes)')
384
- }
@@ -1,321 +0,0 @@
1
- /**
2
- * integration test for zero-cache embedded mode.
3
- *
4
- * validates that zero-cache can run in-process with SINGLE_PROCESS=1,
5
- * connected to PGlite via the TCP proxy. this is the same pipeline as
6
- * the full integration test but without child_process.fork().
7
- *
8
- * test flow:
9
- * 1. create PGlite instances (postgres, cvr, cdb)
10
- * 2. start TCP proxy
11
- * 3. start zero-cache in-process via startZeroCacheEmbed()
12
- * 4. connect WebSocket client and verify sync
13
- */
14
-
15
- import { mkdirSync } from 'node:fs'
16
- import { resolve } from 'node:path'
17
-
18
- import { describe, test, expect, beforeAll, afterAll } from 'vitest'
19
- import WebSocket from 'ws'
20
-
21
- import { getConfig, getConnectionString } from '../config.js'
22
- import {
23
- ensureTablesInPublications,
24
- installAllowAllPermissions,
25
- } from '../integration/test-permissions.js'
26
- import { startPgProxy } from '../pg-proxy.js'
27
- import { createPGliteInstances, type PGliteInstances } from '../pglite-manager.js'
28
- import { installChangeTracking } from '../replication/change-tracker.js'
29
- import { startZeroCacheEmbed, type ZeroCacheEmbed } from './zero-cache-embed.js'
30
-
31
- import type { PGlite } from '@electric-sql/pglite'
32
-
33
- const SYNC_PROTOCOL_VERSION = 49
34
-
35
- function encodeSecProtocols(
36
- initConnectionMessage: unknown,
37
- authToken: string | undefined
38
- ): string {
39
- const payload = JSON.stringify({ initConnectionMessage, authToken })
40
- return encodeURIComponent(Buffer.from(payload, 'utf-8').toString('base64'))
41
- }
42
-
43
- describe('zero-cache embed integration', { timeout: 120000 }, () => {
44
- let db: PGlite
45
- let instances: PGliteInstances
46
- let pgServer: ReturnType<Awaited<ReturnType<typeof startPgProxy>>>
47
- let embed: ZeroCacheEmbed
48
- let zeroPort: number
49
- let pgPort: number
50
- let dataDir: string
51
-
52
- beforeAll(async () => {
53
- // use random ports to avoid conflicts with other tests
54
- pgPort = 24000 + Math.floor(Math.random() * 1000)
55
- zeroPort = pgPort + 100
56
-
57
- dataDir = `.orez-embed-test-${Date.now()}`
58
-
59
- const config = getConfig({
60
- pgPort,
61
- zeroPort,
62
- dataDir,
63
- logLevel: 'info',
64
- useWorkerThreads: false,
65
- singleDb: false,
66
- })
67
-
68
- mkdirSync(dataDir, { recursive: true })
69
-
70
- // create PGlite instances
71
- instances = await createPGliteInstances(config)
72
- db = instances.postgres
73
-
74
- // create test table
75
- await db.exec(`
76
- CREATE TABLE IF NOT EXISTS foo (
77
- id TEXT PRIMARY KEY,
78
- value TEXT,
79
- num INTEGER
80
- )
81
- `)
82
-
83
- // set up publications
84
- const pubName = `orez_zero_public`
85
- process.env.ZERO_APP_PUBLICATIONS = pubName
86
- await db.exec(`CREATE PUBLICATION "${pubName}"`).catch(() => {})
87
- await db
88
- .exec(`ALTER PUBLICATION "${pubName}" ADD TABLE "public"."foo"`)
89
- .catch(() => {})
90
-
91
- // install change tracking
92
- await installChangeTracking(db)
93
-
94
- // install allow-all permissions for test
95
- await installAllowAllPermissions(db, ['foo'])
96
- await ensureTablesInPublications(db, ['foo'])
97
-
98
- // start TCP proxy
99
- pgServer = await startPgProxy(instances, config)
100
-
101
- // start zero-cache in-process
102
- const upstreamDb = getConnectionString(config, 'postgres')
103
- const cvrDb = getConnectionString(config, 'zero_cvr')
104
- const changeDb = getConnectionString(config, 'zero_cdb')
105
- const replicaFile = resolve(dataDir, 'zero-replica.db')
106
-
107
- console.log(`[embed-test] starting in-process zero-cache on port ${zeroPort}`)
108
- console.log(`[embed-test] upstream: ${upstreamDb}`)
109
-
110
- embed = await startZeroCacheEmbed({
111
- pglite: db,
112
- upstreamDb,
113
- cvrDb,
114
- changeDb,
115
- replicaFile,
116
- port: zeroPort,
117
- publications: [pubName],
118
- env: {
119
- ZERO_LOG_LEVEL: 'info',
120
- },
121
- })
122
-
123
- console.log(`[embed-test] zero-cache ready on port ${embed.port}`)
124
-
125
- // wait for HTTP health check
126
- await waitForZero(zeroPort, 30000)
127
- console.log(`[embed-test] health check passed`)
128
- }, 120000)
129
-
130
- afterAll(async () => {
131
- if (embed) await embed.stop()
132
- if (pgServer) pgServer.close()
133
- if (instances) {
134
- await instances.postgres.close().catch(() => {})
135
- await instances.cvr.close().catch(() => {})
136
- await instances.cdb.close().catch(() => {})
137
- }
138
- if (dataDir) {
139
- const { rmSync } = await import('node:fs')
140
- try {
141
- rmSync(dataDir, { recursive: true, force: true })
142
- } catch {}
143
- }
144
- })
145
-
146
- test('zero-cache is ready', () => {
147
- expect(embed.ready).toBe(true)
148
- expect(embed.port).toBe(zeroPort)
149
- })
150
-
151
- test('accepts WebSocket connections', async () => {
152
- const cg = `test-cg-${Date.now()}`
153
- const cid = `test-client-${Date.now()}`
154
- const secProtocol = encodeSecProtocols(
155
- [
156
- 'initConnection',
157
- {
158
- desiredQueriesPatch: [],
159
- clientSchema: {
160
- tables: {
161
- foo: {
162
- columns: {
163
- id: { type: 'string' },
164
- value: { type: 'string' },
165
- num: { type: 'number' },
166
- },
167
- primaryKey: ['id'],
168
- },
169
- },
170
- },
171
- },
172
- ],
173
- undefined
174
- )
175
- const ws = new WebSocket(
176
- `ws://localhost:${zeroPort}/sync/v${SYNC_PROTOCOL_VERSION}/connect` +
177
- `?clientGroupID=${cg}&clientID=${cid}&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`,
178
- secProtocol
179
- )
180
-
181
- // collect messages — attach listener before open to catch everything
182
- const messages: unknown[] = []
183
- ws.on('message', (data) => {
184
- messages.push(JSON.parse(data.toString()))
185
- })
186
-
187
- const connected = new Promise<void>((resolve, reject) => {
188
- ws.on('open', resolve)
189
- ws.on('error', reject)
190
- setTimeout(() => reject(new Error('ws connect timeout')), 10000)
191
- })
192
-
193
- await connected
194
-
195
- // wait for messages to arrive
196
- const deadline = Date.now() + 10000
197
- while (
198
- Date.now() < deadline &&
199
- !messages.some((m) => Array.isArray(m) && m[0] === 'connected')
200
- ) {
201
- await new Promise((r) => setTimeout(r, 100))
202
- }
203
-
204
- const connectedMsg = messages.find((m) => Array.isArray(m) && m[0] === 'connected')
205
- expect(connectedMsg).toMatchObject(['connected', { wsid: 'ws1' }])
206
- ws.close()
207
- })
208
-
209
- test('live replication: insert triggers poke', async () => {
210
- // insert data
211
- await db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
212
- 'embed-row',
213
- 'hello-embed',
214
- 42,
215
- ])
216
-
217
- // wait for replication to deliver
218
- await waitForReplicationCatchup(db)
219
- await new Promise((r) => setTimeout(r, 1000))
220
-
221
- // connect and subscribe
222
- const downstream: unknown[] = []
223
- const ws = connectAndSubscribe(zeroPort, downstream)
224
-
225
- // wait for poke with our data
226
- const deadline = Date.now() + 30000
227
- let found = false
228
- while (Date.now() < deadline && !found) {
229
- await new Promise((r) => setTimeout(r, 500))
230
- for (const msg of downstream) {
231
- if (Array.isArray(msg) && msg[0] === 'pokePart' && msg[1]?.rowsPatch) {
232
- for (const patch of msg[1].rowsPatch) {
233
- if (
234
- patch.op === 'put' &&
235
- patch.tableName === 'foo' &&
236
- patch.value?.id === 'embed-row'
237
- ) {
238
- found = true
239
- break
240
- }
241
- }
242
- }
243
- }
244
- }
245
-
246
- ws.close()
247
- expect(found).toBe(true)
248
- })
249
- })
250
-
251
- function connectAndSubscribe(port: number, downstream: unknown[]): WebSocket {
252
- const cg = `test-cg-${Date.now()}`
253
- const cid = `test-client-${Date.now()}`
254
- const secProtocol = encodeSecProtocols(
255
- [
256
- 'initConnection',
257
- {
258
- desiredQueriesPatch: [
259
- {
260
- op: 'put',
261
- hash: 'q1',
262
- ast: {
263
- table: 'foo',
264
- orderBy: [['id', 'asc']],
265
- },
266
- },
267
- ],
268
- clientSchema: {
269
- tables: {
270
- foo: {
271
- columns: {
272
- id: { type: 'string' },
273
- value: { type: 'string' },
274
- num: { type: 'number' },
275
- },
276
- primaryKey: ['id'],
277
- },
278
- },
279
- },
280
- },
281
- ],
282
- undefined
283
- )
284
- const ws = new WebSocket(
285
- `ws://localhost:${port}/sync/v${SYNC_PROTOCOL_VERSION}/connect` +
286
- `?clientGroupID=${cg}&clientID=${cid}&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`,
287
- secProtocol
288
- )
289
-
290
- ws.on('message', (data) => {
291
- downstream.push(JSON.parse(data.toString()))
292
- })
293
-
294
- return ws
295
- }
296
-
297
- async function waitForReplicationCatchup(
298
- pglite: PGlite,
299
- timeoutMs = 15000
300
- ): Promise<void> {
301
- const deadline = Date.now() + timeoutMs
302
- while (Date.now() < deadline) {
303
- const result = await pglite.query<{ count: string }>(
304
- `SELECT count(*)::text as count FROM _orez._zero_changes`
305
- )
306
- if (Number(result.rows[0]?.count) === 0) return
307
- await new Promise((r) => setTimeout(r, 100))
308
- }
309
- }
310
-
311
- async function waitForZero(port: number, timeoutMs = 30000) {
312
- const deadline = Date.now() + timeoutMs
313
- while (Date.now() < deadline) {
314
- try {
315
- const res = await fetch(`http://localhost:${port}/`)
316
- if (res.ok || res.status === 404) return
317
- } catch {}
318
- await new Promise((r) => setTimeout(r, 500))
319
- }
320
- throw new Error(`zero-cache not ready on port ${port} after ${timeoutMs}ms`)
321
- }