orez 0.2.25 → 0.2.27

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 (175) hide show
  1. package/dist/cf-do/watermark.d.ts +21 -0
  2. package/dist/cf-do/watermark.d.ts.map +1 -0
  3. package/dist/cf-do/watermark.js +93 -0
  4. package/dist/cf-do/watermark.js.map +1 -0
  5. package/dist/cf-do/worker.d.ts +48 -22
  6. package/dist/cf-do/worker.d.ts.map +1 -1
  7. package/dist/cf-do/worker.js +650 -269
  8. package/dist/cf-do/worker.js.map +1 -1
  9. package/dist/config.js +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/do-sql-tracking.d.ts +6 -0
  12. package/dist/do-sql-tracking.d.ts.map +1 -0
  13. package/dist/do-sql-tracking.js +14 -0
  14. package/dist/do-sql-tracking.js.map +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +28 -14
  17. package/dist/index.js.map +1 -1
  18. package/dist/pg-proxy-browser.js +6 -6
  19. package/dist/pg-proxy-browser.js.map +1 -1
  20. package/dist/pg-proxy-do-backend.d.ts +98 -17
  21. package/dist/pg-proxy-do-backend.d.ts.map +1 -1
  22. package/dist/pg-proxy-do-backend.js +6075 -454
  23. package/dist/pg-proxy-do-backend.js.map +1 -1
  24. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
  25. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
  26. package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
  27. package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
  28. package/dist/pg-sqlite-compiler/index.d.ts +12 -0
  29. package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
  30. package/dist/pg-sqlite-compiler/index.js +59 -0
  31. package/dist/pg-sqlite-compiler/index.js.map +1 -0
  32. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
  33. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
  34. package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
  35. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
  36. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
  37. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
  38. package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
  39. package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
  40. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
  41. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
  42. package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
  43. package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
  44. package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
  45. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
  46. package/dist/pg-sqlite-compiler/passes/index.js +39 -0
  47. package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
  48. package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
  49. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
  50. package/dist/pg-sqlite-compiler/passes/types.js +103 -0
  51. package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
  52. package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
  53. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
  54. package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
  55. package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
  56. package/dist/pg-sqlite-compiler/types.d.ts +55 -0
  57. package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
  58. package/dist/pg-sqlite-compiler/types.js +2 -0
  59. package/dist/pg-sqlite-compiler/types.js.map +1 -0
  60. package/dist/replication/change-tracker.d.ts.map +1 -1
  61. package/dist/replication/change-tracker.js +18 -1
  62. package/dist/replication/change-tracker.js.map +1 -1
  63. package/dist/replication/handler.d.ts.map +1 -1
  64. package/dist/replication/handler.js +7 -2
  65. package/dist/replication/handler.js.map +1 -1
  66. package/dist/replication/pgoutput-encoder.d.ts.map +1 -1
  67. package/dist/replication/pgoutput-encoder.js +72 -30
  68. package/dist/replication/pgoutput-encoder.js.map +1 -1
  69. package/dist/worker/browser-build-config.d.ts.map +1 -1
  70. package/dist/worker/browser-build-config.js +2 -1
  71. package/dist/worker/browser-build-config.js.map +1 -1
  72. package/dist/worker/cf-patches.d.ts +5 -2
  73. package/dist/worker/cf-patches.d.ts.map +1 -1
  74. package/dist/worker/cf-patches.js +238 -4
  75. package/dist/worker/cf-patches.js.map +1 -1
  76. package/dist/worker/shims/node-stub.d.ts +35 -0
  77. package/dist/worker/shims/node-stub.d.ts.map +1 -1
  78. package/dist/worker/shims/node-stub.js +53 -1
  79. package/dist/worker/shims/node-stub.js.map +1 -1
  80. package/dist/worker/shims/oxfmt.d.ts +4 -0
  81. package/dist/worker/shims/oxfmt.d.ts.map +1 -0
  82. package/dist/worker/shims/oxfmt.js +4 -0
  83. package/dist/worker/shims/oxfmt.js.map +1 -0
  84. package/dist/worker/shims/postgres-socket.js +1 -1
  85. package/dist/worker/shims/postgres-socket.js.map +1 -1
  86. package/dist/worker/shims/sqlite.d.ts +1 -0
  87. package/dist/worker/shims/sqlite.d.ts.map +1 -1
  88. package/dist/worker/shims/sqlite.js +229 -9
  89. package/dist/worker/shims/sqlite.js.map +1 -1
  90. package/dist/worker/shims/ws.d.ts.map +1 -1
  91. package/dist/worker/shims/ws.js +45 -0
  92. package/dist/worker/shims/ws.js.map +1 -1
  93. package/dist/worker/shims/zero-process-env.d.ts +2 -0
  94. package/dist/worker/shims/zero-process-env.d.ts.map +1 -0
  95. package/dist/worker/shims/zero-process-env.js +9 -0
  96. package/dist/worker/shims/zero-process-env.js.map +1 -0
  97. package/dist/worker/zero-cache-embed-cf.d.ts +29 -12
  98. package/dist/worker/zero-cache-embed-cf.d.ts.map +1 -1
  99. package/dist/worker/zero-cache-embed-cf.js +83 -14
  100. package/dist/worker/zero-cache-embed-cf.js.map +1 -1
  101. package/package.json +11 -2
  102. package/src/cf-do/.wrangler/cache/cf.json +1 -0
  103. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  104. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  105. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  106. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
  107. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  108. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  109. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  110. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +11 -0
  111. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +134 -0
  112. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +11 -0
  113. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +134 -0
  114. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +1059 -0
  115. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +8 -0
  116. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +1059 -0
  117. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +8 -0
  118. package/src/cf-do/ARCHITECTURE.md +93 -0
  119. package/src/cf-do/CHAT_E2E.md +213 -0
  120. package/src/cf-do/watermark.test.ts +103 -0
  121. package/src/cf-do/watermark.ts +118 -0
  122. package/src/cf-do/worker.ts +1041 -0
  123. package/src/cf-do/wrangler.toml +11 -0
  124. package/src/cli.test.ts +3 -1
  125. package/src/config.ts +1 -1
  126. package/src/do-sql-tracking.test.ts +19 -0
  127. package/src/do-sql-tracking.ts +19 -0
  128. package/src/index.ts +29 -14
  129. package/src/pg-proxy-browser.ts +6 -6
  130. package/src/pg-proxy-do-backend.test.ts +3890 -0
  131. package/src/pg-proxy-do-backend.ts +6833 -482
  132. package/src/pg-sqlite-compiler/README.md +53 -0
  133. package/src/pg-sqlite-compiler/catalog/seed.ts +524 -0
  134. package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +307 -0
  135. package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +377 -0
  136. package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +12 -0
  137. package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +447 -0
  138. package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +32 -0
  139. package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +397 -0
  140. package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +337 -0
  141. package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +337 -0
  142. package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +537 -0
  143. package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +1837 -0
  144. package/src/pg-sqlite-compiler/index.ts +73 -0
  145. package/src/pg-sqlite-compiler/integration.test.ts +136 -0
  146. package/src/pg-sqlite-compiler/passes/ast-utils.ts +113 -0
  147. package/src/pg-sqlite-compiler/passes/catalog.ts +65 -0
  148. package/src/pg-sqlite-compiler/passes/datetime.ts +74 -0
  149. package/src/pg-sqlite-compiler/passes/index.ts +49 -0
  150. package/src/pg-sqlite-compiler/passes/types.ts +156 -0
  151. package/src/pg-sqlite-compiler/smoke.test.ts +69 -0
  152. package/src/pg-sqlite-compiler/test/catalog.test.ts +171 -0
  153. package/src/pg-sqlite-compiler/test/corpus.test.ts +161 -0
  154. package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +102 -0
  155. package/src/pg-sqlite-compiler/test/oracle.ts +237 -0
  156. package/src/pg-sqlite-compiler/test/types.test.ts +109 -0
  157. package/src/pg-sqlite-compiler/types.ts +63 -0
  158. package/src/replication/change-tracker.ts +16 -1
  159. package/src/replication/handler.test.ts +35 -0
  160. package/src/replication/handler.ts +7 -2
  161. package/src/replication/pgoutput-encoder.test.ts +71 -2
  162. package/src/replication/pgoutput-encoder.ts +65 -30
  163. package/src/worker/browser-build-config.test.ts +12 -0
  164. package/src/worker/browser-build-config.ts +2 -1
  165. package/src/worker/cf-patches.ts +274 -4
  166. package/src/worker/shims/node-stub.ts +53 -1
  167. package/src/worker/shims/oxfmt.ts +3 -0
  168. package/src/worker/shims/postgres-socket.ts +1 -1
  169. package/src/worker/shims/sqlite.test.ts +145 -0
  170. package/src/worker/shims/sqlite.ts +256 -9
  171. package/src/worker/shims/ws.ts +45 -0
  172. package/src/worker/shims/zero-process-env.ts +11 -0
  173. package/src/worker/zero-cache-embed-cf.ts +114 -18
  174. package/src/query-rewrites.test.ts +0 -30
  175. package/src/query-rewrites.ts +0 -152
@@ -9,21 +9,32 @@
9
9
 
10
10
  // postgres epoch: 2000-01-01 in microseconds from unix epoch
11
11
  const PG_EPOCH_MICROS = 946684800000000n
12
+ const PG_TYPE_BOOL = 16
13
+ const PG_TYPE_TIMESTAMP = 1114
14
+ const PG_TYPE_TIMESTAMPTZ = 1184
12
15
 
13
16
  // shared encoder instance - avoids per-call allocation
14
17
  const encoder = new TextEncoder()
15
18
 
16
- // table oid tracking
17
- const tableOids = new Map<string, number>()
18
- let nextOid = 16384
19
+ function flattenRelationName(tableName: string): string {
20
+ const dot = tableName.indexOf('.')
21
+ if (dot < 0) return tableName
22
+ const schema = tableName.slice(0, dot)
23
+ const name = tableName.slice(dot + 1)
24
+ if (schema === 'public') return name
25
+ if (schema === '_orez' && name === '_zero_changes') return '_zero_changes'
26
+ if (schema === '_orez' && name === '_zero_replication_slots')
27
+ return '_orez__zero_replication_slots'
28
+ if (schema === '_orez') return `_orez__${name}`
29
+ if (schema === '_zero') return `_zero_${name}`
30
+ return `${schema}_${name}`
31
+ }
19
32
 
20
33
  function getTableOid(tableName: string): number {
21
- let oid = tableOids.get(tableName)
22
- if (!oid) {
23
- oid = nextOid++
24
- tableOids.set(tableName, oid)
25
- }
26
- return oid
34
+ let hash = 0
35
+ const key = `table:${flattenRelationName(tableName)}`
36
+ for (let i = 0; i < key.length; i++) hash = (hash * 33 + key.charCodeAt(i)) >>> 0
37
+ return 50_000 + (hash % 10_000_000)
27
38
  }
28
39
 
29
40
  export interface ColumnInfo {
@@ -42,6 +53,50 @@ export function inferColumns(row: Record<string, unknown>): ColumnInfo[] {
42
53
  }))
43
54
  }
44
55
 
56
+ function postgresBooleanText(value: unknown): string | null {
57
+ if (typeof value === 'boolean') return value ? 't' : 'f'
58
+ if (typeof value === 'number') return value === 0 ? 'f' : 't'
59
+ if (typeof value === 'bigint') return value === 0n ? 'f' : 't'
60
+ if (typeof value !== 'string') return null
61
+ switch (value.trim().toLowerCase()) {
62
+ case 't':
63
+ case 'true':
64
+ case '1':
65
+ return 't'
66
+ case 'f':
67
+ case 'false':
68
+ case '0':
69
+ return 'f'
70
+ default:
71
+ return null
72
+ }
73
+ }
74
+
75
+ function postgresTupleTextValue(value: unknown, column: ColumnInfo): string {
76
+ if (column.typeOid === PG_TYPE_BOOL) {
77
+ const booleanText = postgresBooleanText(value)
78
+ if (booleanText !== null) return booleanText
79
+ }
80
+
81
+ if (typeof value === 'boolean') return value ? 't' : 'f'
82
+ if (typeof value === 'object') return JSON.stringify(value)
83
+
84
+ let strVal = String(value)
85
+ // normalize ISO timestamps to postgres text format.
86
+ // to_jsonb() produces "2026-03-19T07:20:11.643" but postgres
87
+ // pgoutput sends "2026-03-19 07:20:11.643" (space, no T).
88
+ // mismatch causes zero-cache to see different values during
89
+ // mutation reconciliation, triggering unnecessary rebases.
90
+ if (
91
+ (column.typeOid === PG_TYPE_TIMESTAMP || column.typeOid === PG_TYPE_TIMESTAMPTZ) &&
92
+ typeof value === 'string' &&
93
+ value.length >= 19
94
+ ) {
95
+ strVal = strVal.replace('T', ' ')
96
+ }
97
+ return strVal
98
+ }
99
+
45
100
  // reusable scratch buffer for building messages (64KB, grows if needed)
46
101
  let scratch = new Uint8Array(65536)
47
102
  let scratchView = new DataView(scratch.buffer)
@@ -175,27 +230,7 @@ function encodeTupleDataInto(
175
230
  ensureScratch(pos + 1)
176
231
  scratch[pos++] = 0x6e // 'n' for null
177
232
  } else {
178
- // convert to postgresql text format
179
- let strVal: string
180
- if (typeof val === 'boolean') {
181
- strVal = val ? 't' : 'f'
182
- } else if (typeof val === 'object') {
183
- strVal = JSON.stringify(val)
184
- } else {
185
- strVal = String(val)
186
- // normalize ISO timestamps to postgres text format.
187
- // to_jsonb() produces "2026-03-19T07:20:11.643" but postgres
188
- // pgoutput sends "2026-03-19 07:20:11.643" (space, no T).
189
- // mismatch causes zero-cache to see different values during
190
- // mutation reconciliation, triggering unnecessary rebases.
191
- if (
192
- (col.typeOid === 1114 || col.typeOid === 1184) &&
193
- typeof val === 'string' &&
194
- val.length >= 19
195
- ) {
196
- strVal = strVal.replace('T', ' ')
197
- }
198
- }
233
+ const strVal = postgresTupleTextValue(val, col)
199
234
  const bytes = encoder.encode(strVal)
200
235
  ensureScratch(pos + 1 + 4 + bytes.length)
201
236
  scratch[pos++] = 0x74 // 't' for text
@@ -5,6 +5,7 @@ import {
5
5
  getBrowserDefine,
6
6
  getBrowserBuildConfig,
7
7
  } from './browser-build-config.js'
8
+ import { getHeapStatistics } from './shims/node-stub.js'
8
9
 
9
10
  describe('browser build config', () => {
10
11
  describe('getBrowserAliases', () => {
@@ -19,6 +20,7 @@ describe('browser build config', () => {
19
20
  expect(aliases['@rocicorp/zero-sqlite3']).toBe('orez/worker/shims/sqlite')
20
21
  expect(aliases.fastify).toBe('orez/worker/shims/fastify')
21
22
  expect(aliases.ws).toBe('orez/worker/shims/ws')
23
+ expect(aliases.oxfmt).toBe('orez/worker/shims/oxfmt')
22
24
  })
23
25
 
24
26
  it('includes Node.js polyfills', () => {
@@ -26,6 +28,7 @@ describe('browser build config', () => {
26
28
  expect(aliases['node:events']).toBe('events')
27
29
  expect(aliases['node:stream']).toBe('orez/worker/shims/stream-browser')
28
30
  expect(aliases['node:path']).toBe('path-browserify')
31
+ expect(aliases['node:os']).toBe('orez/worker/shims/node-stub')
29
32
  })
30
33
 
31
34
  it('includes Node.js stubs', () => {
@@ -35,6 +38,7 @@ describe('browser build config', () => {
35
38
  expect(aliases['node:child_process']).toBe('orez/worker/shims/node-stub')
36
39
  expect(aliases['node:http']).toBe('orez/worker/shims/node-stub')
37
40
  expect(aliases['node:crypto']).toBe('orez/worker/shims/node-stub')
41
+ expect(aliases['node:v8']).toBe('orez/worker/shims/node-stub')
38
42
  })
39
43
  })
40
44
 
@@ -56,4 +60,12 @@ describe('browser build config', () => {
56
60
  expect(config.bundle).toBe(true)
57
61
  })
58
62
  })
63
+
64
+ describe('node:v8 shim', () => {
65
+ it('reports a positive worker heap budget', () => {
66
+ const stats = getHeapStatistics()
67
+ expect(stats.heap_size_limit).toBe(128 * 1024 * 1024)
68
+ expect(stats.heap_size_limit - stats.used_heap_size).toBeGreaterThan(0)
69
+ })
70
+ })
59
71
  })
@@ -49,6 +49,7 @@ export function getBrowserAliases(): Record<string, string> {
49
49
  '@rocicorp/zero-sqlite3': 'orez/worker/shims/sqlite',
50
50
  fastify: 'orez/worker/shims/fastify',
51
51
  ws: 'orez/worker/shims/ws',
52
+ oxfmt: 'orez/worker/shims/oxfmt',
52
53
 
53
54
  // -- Node.js built-in polyfills --
54
55
  // these are needed because zero-cache imports node: modules.
@@ -60,7 +61,7 @@ export function getBrowserAliases(): Record<string, string> {
60
61
  'crypto-browserify': 'orez/worker/shims/node-stub',
61
62
  'node:stream': 'orez/worker/shims/stream-browser',
62
63
  'node:path': 'path-browserify',
63
- 'node:os': 'os-browserify/browser',
64
+ 'node:os': 'orez/worker/shims/node-stub',
64
65
 
65
66
  // -- stubs for Node.js modules that zero-cache imports but doesn't --
66
67
  // -- use in SINGLE_PROCESS mode --
@@ -5,9 +5,12 @@
5
5
  * can run in SINGLE_PROCESS mode on CF Workers where dynamic import()
6
6
  * doesn't work.
7
7
  *
8
- * two patches:
8
+ * five patches:
9
9
  * 1. worker-urls.js — replace file:// URLs with zero-worker:// identifiers
10
- * 2. processes.js replace dynamic import() with static worker module lookup
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
11
14
  *
12
15
  * usage in a post-build script:
13
16
  *
@@ -24,7 +27,10 @@ export function patchZeroCacheForCF(nodeModulesPath: string): void {
24
27
  const zcBase = resolve(nodeModulesPath, '@rocicorp', 'zero', 'out', 'zero-cache', 'src')
25
28
 
26
29
  patchWorkerUrls(zcBase)
30
+ patchWorkerEntrypoints(zcBase)
27
31
  patchProcesses(zcBase)
32
+ patchWriteWorkerClient(zcBase)
33
+ patchPgsqlParserWasm(nodeModulesPath)
28
34
  }
29
35
 
30
36
  function patchWorkerUrls(zcBase: string): void {
@@ -50,11 +56,49 @@ export const CHANGE_STREAMER_URL = u("change-streamer");
50
56
  export const REAPER_URL = u("reaper");
51
57
  export const REPLICATOR_URL = u("replicator");
52
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");
53
63
  `
54
64
  )
55
65
  console.log('[orez] patched zero-cache worker-urls.js')
56
66
  }
57
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
+
58
102
  function patchProcesses(zcBase: string): void {
59
103
  const processesPath = resolve(zcBase, 'types', 'processes.js')
60
104
  if (!existsSync(processesPath)) {
@@ -71,6 +115,9 @@ function patchProcesses(zcBase: string): void {
71
115
 
72
116
  // add static imports of all zero-cache worker modules at the top.
73
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.
74
121
  const workerImports = `\
75
122
  // patched by orez for CF Workers (static imports replace dynamic import())
76
123
  import { default as __zc_main } from "../server/main.js";
@@ -95,10 +142,11 @@ const __zc_workers = {
95
142
  const staticLookup =
96
143
  '((async () => { ' +
97
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); ' +
98
146
  'const runWorker = __zc_workers[_name]; ' +
99
147
  'if (!runWorker) throw new Error("orez: unknown zero-cache worker: " + _name + " (available: " + Object.keys(__zc_workers).join(", ") + ")"); ' +
100
- 'return { default: runWorker }; ' +
101
- '})()).then(async ({ default: runWorker })'
148
+ 'return { default: runWorker, name: _name }; ' +
149
+ '})()).then(async ({ default: runWorker, name })'
102
150
 
103
151
  if (!code.includes(dynamicImportPattern)) {
104
152
  console.warn(
@@ -112,3 +160,225 @@ const __zc_workers = {
112
160
  writeFileSync(processesPath, code)
113
161
  console.log('[orez] patched zero-cache processes.js (static worker imports)')
114
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
+ }
@@ -64,9 +64,38 @@ export function platform() {
64
64
  export function tmpdir() {
65
65
  return '/tmp'
66
66
  }
67
+ export function homedir() {
68
+ return '/tmp'
69
+ }
67
70
  export function availableParallelism() {
68
71
  return 1
69
72
  }
73
+ export function loadavg() {
74
+ return [0, 0, 0]
75
+ }
76
+ export function uptime() {
77
+ return 0
78
+ }
79
+ export function totalmem() {
80
+ return 128 * 1024 * 1024
81
+ }
82
+ export function freemem() {
83
+ return 64 * 1024 * 1024
84
+ }
85
+ export function cpus() {
86
+ return [{ model: 'worker', speed: 0 }]
87
+ }
88
+ export function networkInterfaces() {
89
+ return {
90
+ lo: [
91
+ {
92
+ address: '127.0.0.1',
93
+ family: 'IPv4',
94
+ internal: true,
95
+ },
96
+ ],
97
+ }
98
+ }
70
99
 
71
100
  // stub for node:crypto
72
101
  export function timingSafeEqual(a: unknown, b: unknown) {
@@ -94,7 +123,23 @@ export class Session {
94
123
 
95
124
  // stub for node:v8
96
125
  export function getHeapStatistics() {
97
- return { total_heap_size: 0, used_heap_size: 0 }
126
+ const heapSizeLimit = 128 * 1024 * 1024
127
+ return {
128
+ total_heap_size: 64 * 1024 * 1024,
129
+ total_heap_size_executable: 0,
130
+ total_physical_size: 64 * 1024 * 1024,
131
+ total_available_size: 64 * 1024 * 1024,
132
+ used_heap_size: 32 * 1024 * 1024,
133
+ heap_size_limit: heapSizeLimit,
134
+ malloced_memory: 0,
135
+ peak_malloced_memory: 0,
136
+ does_zap_garbage: 0,
137
+ number_of_native_contexts: 1,
138
+ number_of_detached_contexts: 0,
139
+ total_global_handles_size: 0,
140
+ used_global_handles_size: 0,
141
+ external_memory: 0,
142
+ }
98
143
  }
99
144
 
100
145
  // stub for node:zlib
@@ -197,9 +242,16 @@ export default {
197
242
  hostname,
198
243
  platform,
199
244
  tmpdir,
245
+ homedir,
200
246
  availableParallelism,
247
+ loadavg,
248
+ uptime,
249
+ totalmem,
250
+ freemem,
251
+ cpus,
201
252
  arch,
202
253
  release,
254
+ networkInterfaces,
203
255
  existsSync,
204
256
  readFileSync,
205
257
  writeFileSync,
@@ -0,0 +1,3 @@
1
+ export async function format(_file: string, content: string) {
2
+ return { code: content }
3
+ }
@@ -151,7 +151,7 @@ class MessagePortSocket extends EventEmitter {
151
151
  copy.set(bytes)
152
152
 
153
153
  try {
154
- this.port.postMessage(copy.buffer, [copy.buffer])
154
+ this.port.postMessage(copy.buffer)
155
155
  } catch (err) {
156
156
  queueMicrotask(() => this.emit('error', err))
157
157
  if (typeof encoding === 'function') encoding()