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.
- package/dist/cf-do/watermark.d.ts +21 -0
- package/dist/cf-do/watermark.d.ts.map +1 -0
- package/dist/cf-do/watermark.js +93 -0
- package/dist/cf-do/watermark.js.map +1 -0
- package/dist/cf-do/worker.d.ts +48 -22
- package/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +650 -269
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/do-sql-tracking.d.ts +6 -0
- package/dist/do-sql-tracking.d.ts.map +1 -0
- package/dist/do-sql-tracking.js +14 -0
- package/dist/do-sql-tracking.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -14
- package/dist/index.js.map +1 -1
- package/dist/pg-proxy-browser.js +6 -6
- package/dist/pg-proxy-browser.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +98 -17
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +6075 -454
- package/dist/pg-proxy-do-backend.js.map +1 -1
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
- package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
- package/dist/pg-sqlite-compiler/index.d.ts +12 -0
- package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/index.js +59 -0
- package/dist/pg-sqlite-compiler/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
- package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
- package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
- package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
- package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/index.js +39 -0
- package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
- package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/passes/types.js +103 -0
- package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
- package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
- package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
- package/dist/pg-sqlite-compiler/types.d.ts +55 -0
- package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
- package/dist/pg-sqlite-compiler/types.js +2 -0
- package/dist/pg-sqlite-compiler/types.js.map +1 -0
- package/dist/replication/change-tracker.d.ts.map +1 -1
- package/dist/replication/change-tracker.js +18 -1
- package/dist/replication/change-tracker.js.map +1 -1
- package/dist/replication/handler.d.ts.map +1 -1
- package/dist/replication/handler.js +7 -2
- package/dist/replication/handler.js.map +1 -1
- package/dist/replication/pgoutput-encoder.d.ts.map +1 -1
- package/dist/replication/pgoutput-encoder.js +72 -30
- package/dist/replication/pgoutput-encoder.js.map +1 -1
- package/dist/worker/browser-build-config.d.ts.map +1 -1
- package/dist/worker/browser-build-config.js +2 -1
- package/dist/worker/browser-build-config.js.map +1 -1
- package/dist/worker/cf-patches.d.ts +5 -2
- package/dist/worker/cf-patches.d.ts.map +1 -1
- package/dist/worker/cf-patches.js +238 -4
- package/dist/worker/cf-patches.js.map +1 -1
- package/dist/worker/shims/node-stub.d.ts +35 -0
- package/dist/worker/shims/node-stub.d.ts.map +1 -1
- package/dist/worker/shims/node-stub.js +53 -1
- package/dist/worker/shims/node-stub.js.map +1 -1
- package/dist/worker/shims/oxfmt.d.ts +4 -0
- package/dist/worker/shims/oxfmt.d.ts.map +1 -0
- package/dist/worker/shims/oxfmt.js +4 -0
- package/dist/worker/shims/oxfmt.js.map +1 -0
- package/dist/worker/shims/postgres-socket.js +1 -1
- package/dist/worker/shims/postgres-socket.js.map +1 -1
- package/dist/worker/shims/sqlite.d.ts +1 -0
- package/dist/worker/shims/sqlite.d.ts.map +1 -1
- package/dist/worker/shims/sqlite.js +229 -9
- package/dist/worker/shims/sqlite.js.map +1 -1
- package/dist/worker/shims/ws.d.ts.map +1 -1
- package/dist/worker/shims/ws.js +45 -0
- package/dist/worker/shims/ws.js.map +1 -1
- package/dist/worker/shims/zero-process-env.d.ts +2 -0
- package/dist/worker/shims/zero-process-env.d.ts.map +1 -0
- package/dist/worker/shims/zero-process-env.js +9 -0
- package/dist/worker/shims/zero-process-env.js.map +1 -0
- package/dist/worker/zero-cache-embed-cf.d.ts +29 -12
- package/dist/worker/zero-cache-embed-cf.d.ts.map +1 -1
- package/dist/worker/zero-cache-embed-cf.js +83 -14
- package/dist/worker/zero-cache-embed-cf.js.map +1 -1
- package/package.json +11 -2
- package/src/cf-do/.wrangler/cache/cf.json +1 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +11 -0
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +134 -0
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +11 -0
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +134 -0
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +1059 -0
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +8 -0
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +1059 -0
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +8 -0
- package/src/cf-do/ARCHITECTURE.md +93 -0
- package/src/cf-do/CHAT_E2E.md +213 -0
- package/src/cf-do/watermark.test.ts +103 -0
- package/src/cf-do/watermark.ts +118 -0
- package/src/cf-do/worker.ts +1041 -0
- package/src/cf-do/wrangler.toml +11 -0
- package/src/cli.test.ts +3 -1
- package/src/config.ts +1 -1
- package/src/do-sql-tracking.test.ts +19 -0
- package/src/do-sql-tracking.ts +19 -0
- package/src/index.ts +29 -14
- package/src/pg-proxy-browser.ts +6 -6
- package/src/pg-proxy-do-backend.test.ts +3890 -0
- package/src/pg-proxy-do-backend.ts +6833 -482
- package/src/pg-sqlite-compiler/README.md +53 -0
- package/src/pg-sqlite-compiler/catalog/seed.ts +524 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +307 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +377 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +12 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +447 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +32 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +397 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +337 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +337 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +537 -0
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +1837 -0
- package/src/pg-sqlite-compiler/index.ts +73 -0
- package/src/pg-sqlite-compiler/integration.test.ts +136 -0
- package/src/pg-sqlite-compiler/passes/ast-utils.ts +113 -0
- package/src/pg-sqlite-compiler/passes/catalog.ts +65 -0
- package/src/pg-sqlite-compiler/passes/datetime.ts +74 -0
- package/src/pg-sqlite-compiler/passes/index.ts +49 -0
- package/src/pg-sqlite-compiler/passes/types.ts +156 -0
- package/src/pg-sqlite-compiler/smoke.test.ts +69 -0
- package/src/pg-sqlite-compiler/test/catalog.test.ts +171 -0
- package/src/pg-sqlite-compiler/test/corpus.test.ts +161 -0
- package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +102 -0
- package/src/pg-sqlite-compiler/test/oracle.ts +237 -0
- package/src/pg-sqlite-compiler/test/types.test.ts +109 -0
- package/src/pg-sqlite-compiler/types.ts +63 -0
- package/src/replication/change-tracker.ts +16 -1
- package/src/replication/handler.test.ts +35 -0
- package/src/replication/handler.ts +7 -2
- package/src/replication/pgoutput-encoder.test.ts +71 -2
- package/src/replication/pgoutput-encoder.ts +65 -30
- package/src/worker/browser-build-config.test.ts +12 -0
- package/src/worker/browser-build-config.ts +2 -1
- package/src/worker/cf-patches.ts +274 -4
- package/src/worker/shims/node-stub.ts +53 -1
- package/src/worker/shims/oxfmt.ts +3 -0
- package/src/worker/shims/postgres-socket.ts +1 -1
- package/src/worker/shims/sqlite.test.ts +145 -0
- package/src/worker/shims/sqlite.ts +256 -9
- package/src/worker/shims/ws.ts +45 -0
- package/src/worker/shims/zero-process-env.ts +11 -0
- package/src/worker/zero-cache-embed-cf.ts +114 -18
- package/src/query-rewrites.test.ts +0 -30
- 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
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
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
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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': '
|
|
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 --
|
package/src/worker/cf-patches.ts
CHANGED
|
@@ -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
|
-
*
|
|
8
|
+
* five patches:
|
|
9
9
|
* 1. worker-urls.js — replace file:// URLs with zero-worker:// identifiers
|
|
10
|
-
* 2.
|
|
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
|
-
|
|
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,
|
|
@@ -151,7 +151,7 @@ class MessagePortSocket extends EventEmitter {
|
|
|
151
151
|
copy.set(bytes)
|
|
152
152
|
|
|
153
153
|
try {
|
|
154
|
-
this.port.postMessage(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()
|