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.
- package/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +9 -1
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/pg-proxy-do-backend.d.ts +2 -0
- package/dist/pg-proxy-do-backend.d.ts.map +1 -1
- package/dist/pg-proxy-do-backend.js +49 -7
- 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/package.json +8 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- 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/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.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/ARCHITECTURE.md +0 -83
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1033
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -38
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7157
- package/src/pg-proxy.ts +0 -1087
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* browser build configuration for zero-cache embed.
|
|
3
|
-
*
|
|
4
|
-
* provides the bundler alias map and polyfill configuration needed
|
|
5
|
-
* to bundle zero-cache for browser Web Workers.
|
|
6
|
-
*
|
|
7
|
-
* usage with esbuild:
|
|
8
|
-
*
|
|
9
|
-
* import { getBrowserAliases, getBrowserDefine } from 'orez/worker/browser-build-config'
|
|
10
|
-
*
|
|
11
|
-
* await esbuild.build({
|
|
12
|
-
* entryPoints: ['./my-worker.ts'],
|
|
13
|
-
* bundle: true,
|
|
14
|
-
* format: 'esm',
|
|
15
|
-
* alias: getBrowserAliases(),
|
|
16
|
-
* define: getBrowserDefine(),
|
|
17
|
-
* })
|
|
18
|
-
*
|
|
19
|
-
* usage with vite:
|
|
20
|
-
*
|
|
21
|
-
* import { getBrowserAliases } from 'orez/worker/browser-build-config'
|
|
22
|
-
*
|
|
23
|
-
* export default defineConfig({
|
|
24
|
-
* resolve: { alias: getBrowserAliases() },
|
|
25
|
-
* worker: { format: 'es' },
|
|
26
|
-
* })
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* bundler aliases that swap zero-cache's Node.js dependencies
|
|
31
|
-
* for browser-compatible shims.
|
|
32
|
-
*
|
|
33
|
-
* the consumer must have these packages installed:
|
|
34
|
-
* - orez (provides postgres/sqlite/fastify/ws shims)
|
|
35
|
-
* - events (EventEmitter polyfill)
|
|
36
|
-
* - buffer (Buffer polyfill)
|
|
37
|
-
* - process (process polyfill)
|
|
38
|
-
*
|
|
39
|
-
* optional (only needed if zero-cache code uses them):
|
|
40
|
-
* - crypto-browserify, stream-browserify, path-browserify, os-browserify
|
|
41
|
-
*/
|
|
42
|
-
export function getBrowserAliases(): Record<string, string> {
|
|
43
|
-
return {
|
|
44
|
-
// -- orez shims for zero-cache dependencies --
|
|
45
|
-
// postgres-browser uses the real postgres package with MessagePort transport
|
|
46
|
-
// to pg-proxy-browser, matching orez-node's wire protocol architecture.
|
|
47
|
-
// falls back to old PGlite-wrapping shim if postgres-browser isn't available.
|
|
48
|
-
postgres: 'orez/worker/shims/postgres-browser',
|
|
49
|
-
'@rocicorp/zero-sqlite3': 'orez/worker/shims/sqlite',
|
|
50
|
-
fastify: 'orez/worker/shims/fastify',
|
|
51
|
-
ws: 'orez/worker/shims/ws',
|
|
52
|
-
oxfmt: 'orez/worker/shims/oxfmt',
|
|
53
|
-
|
|
54
|
-
// -- Node.js built-in polyfills --
|
|
55
|
-
// these are needed because zero-cache imports node: modules.
|
|
56
|
-
// the bundler replaces them with browser-compatible packages.
|
|
57
|
-
'node:events': 'events',
|
|
58
|
-
'node:buffer': 'buffer',
|
|
59
|
-
'node:process': 'process/browser',
|
|
60
|
-
'node:crypto': 'orez/worker/shims/node-stub',
|
|
61
|
-
'crypto-browserify': 'orez/worker/shims/node-stub',
|
|
62
|
-
'node:stream': 'orez/worker/shims/stream-browser',
|
|
63
|
-
'node:path': 'path-browserify',
|
|
64
|
-
'node:os': 'orez/worker/shims/node-stub',
|
|
65
|
-
|
|
66
|
-
// -- stubs for Node.js modules that zero-cache imports but doesn't --
|
|
67
|
-
// -- use in SINGLE_PROCESS mode --
|
|
68
|
-
'node:http': 'orez/worker/shims/node-stub',
|
|
69
|
-
'node:net': 'orez/worker/shims/node-stub',
|
|
70
|
-
'node:tls': 'orez/worker/shims/node-stub',
|
|
71
|
-
'node:child_process': 'orez/worker/shims/node-stub',
|
|
72
|
-
'node:fs': 'orez/worker/shims/node-stub',
|
|
73
|
-
'node:fs/promises': 'orez/worker/shims/node-stub',
|
|
74
|
-
'node:url': 'orez/worker/shims/node-stub',
|
|
75
|
-
'node:util': 'orez/worker/shims/node-stub',
|
|
76
|
-
'node:assert': 'orez/worker/shims/node-stub',
|
|
77
|
-
'node:inspector/promises': 'orez/worker/shims/node-stub',
|
|
78
|
-
'node:v8': 'orez/worker/shims/node-stub',
|
|
79
|
-
'node:zlib': 'orez/worker/shims/node-stub',
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* esbuild define map for browser builds.
|
|
85
|
-
* replaces Node.js globals with browser equivalents.
|
|
86
|
-
*/
|
|
87
|
-
export function getBrowserDefine(): Record<string, string> {
|
|
88
|
-
return {
|
|
89
|
-
'process.env.NODE_ENV': '"development"',
|
|
90
|
-
'process.env.SINGLE_PROCESS': '"1"',
|
|
91
|
-
'process.versions.node': '"20.0.0"',
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* combined config for esbuild builds.
|
|
97
|
-
* merges aliases, define, and common settings.
|
|
98
|
-
*/
|
|
99
|
-
export function getBrowserBuildConfig() {
|
|
100
|
-
return {
|
|
101
|
-
alias: getBrowserAliases(),
|
|
102
|
-
define: getBrowserDefine(),
|
|
103
|
-
// recommended esbuild settings for browser worker bundles
|
|
104
|
-
format: 'esm' as const,
|
|
105
|
-
platform: 'browser' as const,
|
|
106
|
-
target: 'es2022',
|
|
107
|
-
bundle: true,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { handleDisabledBrowserAdminRequest } from './browser-admin.js'
|
|
4
|
-
|
|
5
|
-
describe('disabled browser admin api', () => {
|
|
6
|
-
it('ignores non-admin routes', () => {
|
|
7
|
-
expect(
|
|
8
|
-
handleDisabledBrowserAdminRequest({
|
|
9
|
-
method: 'GET',
|
|
10
|
-
url: '/sync/v1/connect',
|
|
11
|
-
})
|
|
12
|
-
).toBeNull()
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('serves empty logs for disabled browser admin', () => {
|
|
16
|
-
const response = handleDisabledBrowserAdminRequest({
|
|
17
|
-
method: 'GET',
|
|
18
|
-
url: '/__orez/api/logs?limit=100',
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
expect(response?.status).toBe(200)
|
|
22
|
-
expect(response?.headers['content-type']).toBe('application/json')
|
|
23
|
-
expect(JSON.parse(response?.body ?? '')).toEqual({
|
|
24
|
-
entries: [],
|
|
25
|
-
cursor: 0,
|
|
26
|
-
admin: 'disabled',
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
it('serves status for disabled browser admin', () => {
|
|
31
|
-
const response = handleDisabledBrowserAdminRequest({
|
|
32
|
-
method: 'GET',
|
|
33
|
-
url: 'http://localhost:7849/__orez/api/status',
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
expect(response?.status).toBe(200)
|
|
37
|
-
expect(JSON.parse(response?.body ?? '')).toEqual({
|
|
38
|
-
ready: true,
|
|
39
|
-
admin: 'disabled',
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('handles preflight and disallows writes', () => {
|
|
44
|
-
expect(
|
|
45
|
-
handleDisabledBrowserAdminRequest({
|
|
46
|
-
method: 'OPTIONS',
|
|
47
|
-
url: '/__orez/api/logs',
|
|
48
|
-
})?.status
|
|
49
|
-
).toBe(200)
|
|
50
|
-
|
|
51
|
-
const response = handleDisabledBrowserAdminRequest({
|
|
52
|
-
method: 'POST',
|
|
53
|
-
url: '/__orez/api/actions/restart-zero',
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
expect(response?.status).toBe(405)
|
|
57
|
-
expect(JSON.parse(response?.body ?? '')).toEqual({
|
|
58
|
-
error: 'method not allowed',
|
|
59
|
-
admin: 'disabled',
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('keeps unknown admin routes explicit', () => {
|
|
64
|
-
const response = handleDisabledBrowserAdminRequest({
|
|
65
|
-
method: 'GET',
|
|
66
|
-
url: '/__orez/api/actions/restart-zero',
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
expect(response?.status).toBe(404)
|
|
70
|
-
expect(JSON.parse(response?.body ?? '')).toEqual({
|
|
71
|
-
error: 'not found',
|
|
72
|
-
admin: 'disabled',
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
})
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
// NOTE THIS IS NOT OREZ NODE THIS IS NOT A GOOD REFERENCE BECAUSE ITS OUR EARLY GUESS AT WHAT COULD WORK
|
|
2
|
-
// DO NOT STUDY THIS, THE OTHER STUFF IN SRC IS WHERE YOU EANT TO LOOK
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* zero-cache embedded runner for browser Web Workers.
|
|
6
|
-
*
|
|
7
|
-
* same pattern as the CF embed but for browser environments:
|
|
8
|
-
*
|
|
9
|
-
* postgres → orez/worker/shims/postgres (PGlite-backed)
|
|
10
|
-
* @rocicorp/zero-sqlite3 → orez/worker/shims/sqlite (sql.js or in-memory)
|
|
11
|
-
* fastify → orez/worker/shims/fastify (route capture)
|
|
12
|
-
* ws → orez/worker/shims/ws (MessagePort/WebSocket)
|
|
13
|
-
*
|
|
14
|
-
* the consumer's bundler (esbuild/vite) must configure these aliases
|
|
15
|
-
* plus Node.js polyfills. use getBrowserBuildConfig() for the alias map.
|
|
16
|
-
*
|
|
17
|
-
* usage:
|
|
18
|
-
*
|
|
19
|
-
* import { startZeroCacheEmbedBrowser } from 'orez/worker/browser-embed'
|
|
20
|
-
*
|
|
21
|
-
* const zc = await startZeroCacheEmbedBrowser({
|
|
22
|
-
* pglite: pg,
|
|
23
|
-
* sqlite: sqlJsDb, // optional — sql.js Database instance
|
|
24
|
-
* })
|
|
25
|
-
*
|
|
26
|
-
* // connect a Zero client via WebSocket-like object
|
|
27
|
-
* zc.handleWebSocket(wsOrPort)
|
|
28
|
-
*
|
|
29
|
-
* // or handle HTTP requests (push/pull)
|
|
30
|
-
* const result = await zc.handleHttp({ method: 'GET', url: '/' })
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import EventEmitter from 'node:events'
|
|
34
|
-
|
|
35
|
-
// static import so the bundler can follow the dependency tree.
|
|
36
|
-
// @ts-expect-error — internal zero-cache module, no type declarations
|
|
37
|
-
import { runWorker as _runWorker } from '@rocicorp/zero/out/zero-cache/src/server/runner/run-worker.js'
|
|
38
|
-
|
|
39
|
-
import { handleDisabledBrowserAdminRequest } from './browser-admin.js'
|
|
40
|
-
|
|
41
|
-
import type { HttpRequest, HttpResponse } from './browser-admin.js'
|
|
42
|
-
import type { PGlite } from '@electric-sql/pglite'
|
|
43
|
-
|
|
44
|
-
export type { HttpRequest, HttpResponse } from './browser-admin.js'
|
|
45
|
-
|
|
46
|
-
const runWorkerFn = _runWorker as (
|
|
47
|
-
parent: unknown,
|
|
48
|
-
env: Record<string, string>
|
|
49
|
-
) => Promise<void>
|
|
50
|
-
|
|
51
|
-
export interface ZeroCacheEmbedBrowserOptions {
|
|
52
|
-
/** PGlite instance */
|
|
53
|
-
pglite: PGlite
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* sql.js Database instance for SQLite replica storage.
|
|
57
|
-
* if not provided, looks for globalThis.__orez_sqljs_db,
|
|
58
|
-
* then falls back to an in-memory stub (limited functionality).
|
|
59
|
-
*/
|
|
60
|
-
sqlite?: unknown
|
|
61
|
-
|
|
62
|
-
/** zero app ID (default: 'zero') */
|
|
63
|
-
appId?: string
|
|
64
|
-
|
|
65
|
-
/** publication names */
|
|
66
|
-
publications?: string[]
|
|
67
|
-
|
|
68
|
-
/** additional env vars passed to zero-cache */
|
|
69
|
-
env?: Record<string, string>
|
|
70
|
-
|
|
71
|
-
/** timeout in ms waiting for zero-cache ready (default: 30000) */
|
|
72
|
-
readyTimeout?: number
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* intercept browser-mode orez admin routes before they reach zero-cache.
|
|
76
|
-
*
|
|
77
|
-
* browser embeds do not run the node admin dashboard or log store. leaving
|
|
78
|
-
* `/__orez/*` to fall through to zero-cache makes disabled admin look like
|
|
79
|
-
* an app/zero route miss. keep the contract explicit by returning empty
|
|
80
|
-
* admin responses for the small read-only surface and 404 for the rest.
|
|
81
|
-
* default: true.
|
|
82
|
-
*/
|
|
83
|
-
disableAdminApi?: boolean
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** WebSocket-like object — matches CF WebSocket, browser WebSocket, or MessagePort adapter */
|
|
87
|
-
interface WsLike {
|
|
88
|
-
readyState: number
|
|
89
|
-
send(data: string | ArrayBuffer | ArrayBufferView): void
|
|
90
|
-
close(code?: number, reason?: string): void
|
|
91
|
-
addEventListener(type: string, handler: (event: any) => void): void
|
|
92
|
-
removeEventListener(type: string, handler: (event: any) => void): void
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface ZeroCacheEmbedBrowser {
|
|
96
|
-
/** whether zero-cache is ready */
|
|
97
|
-
readonly ready: boolean
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* handle a WebSocket connection from a Zero client.
|
|
101
|
-
* accepts any WebSocket-like object (browser WebSocket, MessagePort adapter, etc.)
|
|
102
|
-
* feeds it into zero-cache's handoff mechanism.
|
|
103
|
-
*/
|
|
104
|
-
handleWebSocket(ws: WsLike, url?: string): void
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* handle an HTTP request (push/pull/health).
|
|
108
|
-
* for environments without the Fetch API Request/Response.
|
|
109
|
-
*/
|
|
110
|
-
handleHttp(request: HttpRequest): Promise<HttpResponse>
|
|
111
|
-
|
|
112
|
-
/** stop zero-cache */
|
|
113
|
-
stop(): Promise<void>
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export async function startZeroCacheEmbedBrowser(
|
|
117
|
-
opts: ZeroCacheEmbedBrowserOptions
|
|
118
|
-
): Promise<ZeroCacheEmbedBrowser> {
|
|
119
|
-
const appId = opts.appId || 'zero'
|
|
120
|
-
const publications = opts.publications?.join(',') || `orez_${appId}_public`
|
|
121
|
-
const readyTimeout = opts.readyTimeout ?? 30000
|
|
122
|
-
const disableAdminApi = opts.disableAdminApi ?? true
|
|
123
|
-
|
|
124
|
-
// set up sqlite storage from sql.js or in-memory
|
|
125
|
-
if (opts.sqlite) {
|
|
126
|
-
// consumer provided a sql.js Database — create adapter
|
|
127
|
-
const { createSqlJsStorage } = await import('./shims/sqlite-browser.js')
|
|
128
|
-
;(globalThis as any).__orez_do_sqlite = createSqlJsStorage(opts.sqlite as any)
|
|
129
|
-
} else if (!(globalThis as any).__orez_do_sqlite) {
|
|
130
|
-
// no sqlite provided — use in-memory stub
|
|
131
|
-
const { createInMemoryStorage } = await import('./shims/sqlite-browser.js')
|
|
132
|
-
;(globalThis as any).__orez_do_sqlite = createInMemoryStorage()
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// set up PGlite for postgres shim
|
|
136
|
-
;(globalThis as any).__orez_pglite = opts.pglite
|
|
137
|
-
|
|
138
|
-
// ensure process globals exist (browser has no process)
|
|
139
|
-
;(globalThis as any).process ??= {}
|
|
140
|
-
;(globalThis as any).process.env ??= {}
|
|
141
|
-
;(globalThis as any).process.pid ??= 1
|
|
142
|
-
;(globalThis as any).process.argv ??= []
|
|
143
|
-
;(globalThis as any).process.kill ??= () => {}
|
|
144
|
-
|
|
145
|
-
// CRITICAL: set SINGLE_PROCESS before zero-cache runs
|
|
146
|
-
;(globalThis as any).process.env.SINGLE_PROCESS = '1'
|
|
147
|
-
;(globalThis as any).process.env.NODE_ENV = 'development'
|
|
148
|
-
|
|
149
|
-
// create fake parent EventEmitter for zero-cache's runWorker()
|
|
150
|
-
const parent = new EventEmitter() as EventEmitter & {
|
|
151
|
-
send: (msg: unknown) => boolean
|
|
152
|
-
kill: (signal?: string) => void
|
|
153
|
-
pid: number
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const parentEmitter = new EventEmitter()
|
|
157
|
-
|
|
158
|
-
parent.send = (message: unknown) => {
|
|
159
|
-
parentEmitter.emit('message', message)
|
|
160
|
-
return true
|
|
161
|
-
}
|
|
162
|
-
parent.kill = (signal = 'SIGTERM') => {
|
|
163
|
-
parent.emit(signal, signal)
|
|
164
|
-
}
|
|
165
|
-
parent.pid = 1
|
|
166
|
-
|
|
167
|
-
// shim process.exit
|
|
168
|
-
const origExit = (globalThis as any).process.exit
|
|
169
|
-
;(globalThis as any).process.exit = (code?: number) => {
|
|
170
|
-
parent.emit('exit', code ?? 0)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// build env for zero-cache
|
|
174
|
-
const env: Record<string, string> = {
|
|
175
|
-
...((globalThis as any).process.env as Record<string, string>),
|
|
176
|
-
SINGLE_PROCESS: '1',
|
|
177
|
-
NODE_ENV: 'development',
|
|
178
|
-
ZERO_UPSTREAM_DB: 'pglite://localhost/postgres',
|
|
179
|
-
ZERO_CVR_DB: 'pglite://localhost/zero_cvr',
|
|
180
|
-
ZERO_CHANGE_DB: 'pglite://localhost/zero_cdb',
|
|
181
|
-
ZERO_REPLICA_FILE: ':browser-sqlite:',
|
|
182
|
-
ZERO_PORT: '0',
|
|
183
|
-
ZERO_APP_ID: appId,
|
|
184
|
-
ZERO_APP_PUBLICATIONS: publications,
|
|
185
|
-
ZERO_LOG_LEVEL: opts.env?.ZERO_LOG_LEVEL || 'info',
|
|
186
|
-
ZERO_NUM_SYNC_WORKERS: opts.env?.ZERO_NUM_SYNC_WORKERS || '1',
|
|
187
|
-
ZERO_ENABLE_QUERY_PLANNER: 'false',
|
|
188
|
-
...opts.env,
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// wrap parent with onMessageType/onceMessageType helpers
|
|
192
|
-
const wrappedParent = new Proxy(parent, {
|
|
193
|
-
get(target, prop, receiver) {
|
|
194
|
-
if (prop === 'onMessageType') {
|
|
195
|
-
return (type: string, handler: (msg: unknown) => void) => {
|
|
196
|
-
target.on('message', (data: unknown) => {
|
|
197
|
-
if (Array.isArray(data) && data.length === 2 && data[0] === type) {
|
|
198
|
-
handler(data[1])
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
return receiver
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (prop === 'onceMessageType') {
|
|
205
|
-
return (type: string, handler: (msg: unknown) => void) => {
|
|
206
|
-
const listener = (data: unknown) => {
|
|
207
|
-
if (Array.isArray(data) && data.length === 2 && data[0] === type) {
|
|
208
|
-
target.off('message', listener)
|
|
209
|
-
handler(data[1])
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
target.on('message', listener)
|
|
213
|
-
return receiver
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return Reflect.get(target, prop, receiver)
|
|
217
|
-
},
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
// state
|
|
221
|
-
let isReady = false
|
|
222
|
-
let runWorkerPromise: Promise<void> | null = null
|
|
223
|
-
let fastifyInstance: any = null
|
|
224
|
-
|
|
225
|
-
// wait for "ready" message
|
|
226
|
-
const readyPromise = new Promise<void>((resolve, reject) => {
|
|
227
|
-
const timeout = setTimeout(() => {
|
|
228
|
-
reject(
|
|
229
|
-
new Error(
|
|
230
|
-
`zero-cache browser embed: timed out waiting for ready after ${readyTimeout}ms`
|
|
231
|
-
)
|
|
232
|
-
)
|
|
233
|
-
}, readyTimeout)
|
|
234
|
-
|
|
235
|
-
parentEmitter.on('message', (msg: unknown) => {
|
|
236
|
-
if (Array.isArray(msg) && msg[0] === 'ready') {
|
|
237
|
-
clearTimeout(timeout)
|
|
238
|
-
isReady = true
|
|
239
|
-
resolve()
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
// start zero-cache
|
|
245
|
-
runWorkerPromise = runWorkerFn(wrappedParent, env).catch((err) => {
|
|
246
|
-
if (!isReady) {
|
|
247
|
-
throw err
|
|
248
|
-
}
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
await readyPromise
|
|
252
|
-
|
|
253
|
-
// wait for fastify instance — ZeroDispatcher is constructed AFTER the ready
|
|
254
|
-
// signal fires, so __orez_fastify_instance may not be set yet
|
|
255
|
-
fastifyInstance = (globalThis as any).__orez_fastify_instance
|
|
256
|
-
if (!fastifyInstance) {
|
|
257
|
-
for (let i = 0; i < 100; i++) {
|
|
258
|
-
await new Promise((r) => setTimeout(r, 50))
|
|
259
|
-
fastifyInstance = (globalThis as any).__orez_fastify_instance
|
|
260
|
-
if (fastifyInstance) break
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return {
|
|
265
|
-
get ready() {
|
|
266
|
-
return isReady
|
|
267
|
-
},
|
|
268
|
-
|
|
269
|
-
async handleWebSocket(ws: WsLike, url = '/', headers?: Record<string, string>) {
|
|
270
|
-
// lazily resolve fastify instances — ZeroDispatcher is constructed AFTER the
|
|
271
|
-
// ready signal fires, so instances may not all be registered yet.
|
|
272
|
-
// poll until we have instances with ws routes (tryHandoff will check).
|
|
273
|
-
let instances: any[] = []
|
|
274
|
-
for (let i = 0; i < 100; i++) {
|
|
275
|
-
instances = (globalThis as any).__orez_fastify_instances || []
|
|
276
|
-
fastifyInstance = (globalThis as any).__orez_fastify_instance
|
|
277
|
-
// wait until we have at least one instance with 2+ message listeners
|
|
278
|
-
// (the dispatcher's instance has both the shim handler + installWebSocketHandoff)
|
|
279
|
-
if (instances.some((inst: any) => inst?.server?.listenerCount?.('message') >= 2))
|
|
280
|
-
break
|
|
281
|
-
await new Promise((r) => setTimeout(r, 50))
|
|
282
|
-
}
|
|
283
|
-
if (!isReady) return
|
|
284
|
-
|
|
285
|
-
const handoffMsg = {
|
|
286
|
-
message: {
|
|
287
|
-
url,
|
|
288
|
-
headers: headers || {},
|
|
289
|
-
method: 'GET',
|
|
290
|
-
},
|
|
291
|
-
head: new Uint8Array(0),
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// try all fastify instances via tryHandoff, stop at first match
|
|
295
|
-
let handled = false
|
|
296
|
-
for (const inst of instances) {
|
|
297
|
-
if (inst?.tryHandoff?.(handoffMsg, ws)) {
|
|
298
|
-
handled = true
|
|
299
|
-
break
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// fallback: emit directly on the last instance's server
|
|
304
|
-
if (!handled && fastifyInstance?.server) {
|
|
305
|
-
fastifyInstance.server.emit('message', ['handoff', handoffMsg], ws)
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
async handleHttp(request: HttpRequest): Promise<HttpResponse> {
|
|
310
|
-
if (disableAdminApi) {
|
|
311
|
-
const adminResponse = handleDisabledBrowserAdminRequest(request)
|
|
312
|
-
if (adminResponse) return adminResponse
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (!isReady || !fastifyInstance?.inject) {
|
|
316
|
-
return { status: 503, headers: {}, body: 'not ready' }
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const result = await fastifyInstance.inject({
|
|
320
|
-
method: request.method,
|
|
321
|
-
url: request.url,
|
|
322
|
-
headers: request.headers || {},
|
|
323
|
-
payload: request.body,
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
return {
|
|
327
|
-
status: result.statusCode,
|
|
328
|
-
headers: result.headers,
|
|
329
|
-
body: result.body,
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
async stop() {
|
|
334
|
-
isReady = false
|
|
335
|
-
wrappedParent.kill('SIGTERM')
|
|
336
|
-
if (runWorkerPromise) {
|
|
337
|
-
await Promise.race([runWorkerPromise, new Promise((r) => setTimeout(r, 5000))])
|
|
338
|
-
}
|
|
339
|
-
if (origExit) {
|
|
340
|
-
;(globalThis as any).process.exit = origExit
|
|
341
|
-
}
|
|
342
|
-
delete (globalThis as any).process.env.SINGLE_PROCESS
|
|
343
|
-
},
|
|
344
|
-
}
|
|
345
|
-
}
|