orez 0.2.26 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/cf-do/worker.d.ts.map +1 -1
  2. package/dist/cf-do/worker.js +9 -1
  3. package/dist/cf-do/worker.js.map +1 -1
  4. package/dist/pg-proxy-do-backend.d.ts +2 -0
  5. package/dist/pg-proxy-do-backend.d.ts.map +1 -1
  6. package/dist/pg-proxy-do-backend.js +49 -7
  7. package/dist/pg-proxy-do-backend.js.map +1 -1
  8. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +67 -0
  9. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +1 -0
  10. package/dist/pg-sqlite-compiler/catalog/seed.js +436 -0
  11. package/dist/pg-sqlite-compiler/catalog/seed.js.map +1 -0
  12. package/dist/pg-sqlite-compiler/index.d.ts +12 -0
  13. package/dist/pg-sqlite-compiler/index.d.ts.map +1 -0
  14. package/dist/pg-sqlite-compiler/index.js +59 -0
  15. package/dist/pg-sqlite-compiler/index.js.map +1 -0
  16. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +48 -0
  17. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +1 -0
  18. package/dist/pg-sqlite-compiler/passes/ast-utils.js +93 -0
  19. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +1 -0
  20. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +34 -0
  21. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +1 -0
  22. package/dist/pg-sqlite-compiler/passes/catalog.js +30 -0
  23. package/dist/pg-sqlite-compiler/passes/catalog.js.map +1 -0
  24. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +21 -0
  25. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +1 -0
  26. package/dist/pg-sqlite-compiler/passes/datetime.js +53 -0
  27. package/dist/pg-sqlite-compiler/passes/datetime.js.map +1 -0
  28. package/dist/pg-sqlite-compiler/passes/index.d.ts +21 -0
  29. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +1 -0
  30. package/dist/pg-sqlite-compiler/passes/index.js +39 -0
  31. package/dist/pg-sqlite-compiler/passes/index.js.map +1 -0
  32. package/dist/pg-sqlite-compiler/passes/types.d.ts +41 -0
  33. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +1 -0
  34. package/dist/pg-sqlite-compiler/passes/types.js +103 -0
  35. package/dist/pg-sqlite-compiler/passes/types.js.map +1 -0
  36. package/dist/pg-sqlite-compiler/test/oracle.d.ts +34 -0
  37. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +1 -0
  38. package/dist/pg-sqlite-compiler/test/oracle.js +204 -0
  39. package/dist/pg-sqlite-compiler/test/oracle.js.map +1 -0
  40. package/dist/pg-sqlite-compiler/types.d.ts +55 -0
  41. package/dist/pg-sqlite-compiler/types.d.ts.map +1 -0
  42. package/dist/pg-sqlite-compiler/types.js +2 -0
  43. package/dist/pg-sqlite-compiler/types.js.map +1 -0
  44. package/package.json +8 -4
  45. package/src/admin/admin-data.test.ts +0 -348
  46. package/src/admin/http-proxy.ts +0 -252
  47. package/src/admin/log-store.ts +0 -192
  48. package/src/admin/server.ts +0 -471
  49. package/src/admin/ui.ts +0 -1322
  50. package/src/bench/proxy-throughput.bench.ts +0 -343
  51. package/src/bench/serial-mutations.bench.ts +0 -270
  52. package/src/browser.ts +0 -203
  53. package/src/cf-do/.wrangler/cache/cf.json +0 -1
  54. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  55. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  56. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  57. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0f0f3bdf0abda097eb6f1246db4657d9fc622081362d894d82c1a1ce067b05b6.sqlite +0 -0
  58. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/1ddd3a4a48a11b51658444f5458a1fb175194b1d5b6a5bda20ef3fe3205b900c.sqlite +0 -0
  59. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/204a39120310d37e972c5914cfd71ad55c151bdb9e8ed289a5f8c5b052dd60e4.sqlite +0 -0
  60. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/3835f242df9728adba3d127a238793fd054ed3e51df3f60749ee744c469bf2a2.sqlite +0 -0
  61. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/4aa9c80eb716cf55b8995ccf7afab0b36c683e6da07d7c37a3f9c570136036df.sqlite +0 -0
  62. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/533e2fd1d6ea46e7a9a0017916ef341802d438d72583462755f2c1f8225e9bf2.sqlite +0 -0
  63. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/5ffa1aced1225ecaeac6366f7586aa3de92761cdff8711d81fbd81f248076abd.sqlite +0 -0
  64. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/686c3a9f0d7e59ed2ab607efd4b76d779c97cafeb3818380033bf7c7eb86c819.sqlite +0 -0
  65. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/6e8214e8dcfadd0deb52d64e5e9ca85c6b329ace11193909845995396914c473.sqlite +0 -0
  66. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/78d9ec9ff873d3fe3507ff53c2a6f6dfc408b4268eb0db3f2a146c0678965366.sqlite +0 -0
  67. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/7eff9f0ed7e27ad0d3f9d923de0682fab1928591172c1ba336c5f79a134a5d85.sqlite +0 -0
  68. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/836cda5b995b25867d722ed4f4c2292167e80351a3c6038db626648eb247dd8b.sqlite +0 -0
  69. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/91ef63b112209ab30172763acd8a0935106c248f7f1bcae5545ce37a9f201551.sqlite +0 -0
  70. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/a66ea4293a5f5938bc6d116edfa2522bb85bc37aea3541fbc09c3b613b9b32c0.sqlite +0 -0
  71. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/ceb2ab26b80590840b65651deb6e948d3bf81565c6751f3a58752cf4bf4aecae.sqlite +0 -0
  72. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  73. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  74. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  75. package/src/cf-do/ARCHITECTURE.md +0 -83
  76. package/src/cf-do/watermark.test.ts +0 -103
  77. package/src/cf-do/watermark.ts +0 -118
  78. package/src/cf-do/worker.ts +0 -1033
  79. package/src/cf-do/wrangler.toml +0 -11
  80. package/src/cf-pglite/README.md +0 -19
  81. package/src/change-tracking.ts +0 -25
  82. package/src/child-process.test.ts +0 -147
  83. package/src/child-process.ts +0 -90
  84. package/src/cli-entry.ts +0 -72
  85. package/src/cli.test.ts +0 -38
  86. package/src/cli.ts +0 -1214
  87. package/src/config.ts +0 -150
  88. package/src/do-sql-tracking.test.ts +0 -19
  89. package/src/do-sql-tracking.ts +0 -19
  90. package/src/index.ts +0 -1215
  91. package/src/integration/integration.test.ts +0 -517
  92. package/src/integration/native-binary.guard.test.ts +0 -13
  93. package/src/integration/native-startup.test.ts +0 -44
  94. package/src/integration/replication-latency.test.ts +0 -428
  95. package/src/integration/restore-live-stress.test.ts +0 -433
  96. package/src/integration/restore-reset.test.ts +0 -400
  97. package/src/integration/restore.test.ts +0 -274
  98. package/src/integration/test-permissions.ts +0 -147
  99. package/src/load-config.ts +0 -46
  100. package/src/log.ts +0 -96
  101. package/src/mutex.ts +0 -47
  102. package/src/pg-proxy-browser.singledb.test.ts +0 -233
  103. package/src/pg-proxy-browser.ts +0 -2022
  104. package/src/pg-proxy-do-backend.test.ts +0 -3890
  105. package/src/pg-proxy-do-backend.ts +0 -7157
  106. package/src/pg-proxy.ts +0 -1087
  107. package/src/pglite-ipc.test.ts +0 -116
  108. package/src/pglite-ipc.ts +0 -266
  109. package/src/pglite-manager.ts +0 -557
  110. package/src/pglite-web-proxy.test.ts +0 -57
  111. package/src/pglite-web-proxy.ts +0 -221
  112. package/src/pglite-web-worker.ts +0 -152
  113. package/src/pglite-worker-thread.ts +0 -253
  114. package/src/port.ts +0 -25
  115. package/src/process-title.ts +0 -9
  116. package/src/recovery.ts +0 -155
  117. package/src/replication/change-tracker.test.ts +0 -357
  118. package/src/replication/change-tracker.ts +0 -279
  119. package/src/replication/handler.test.ts +0 -511
  120. package/src/replication/handler.ts +0 -1190
  121. package/src/replication/pgoutput-encoder.test.ts +0 -697
  122. package/src/replication/pgoutput-encoder.ts +0 -373
  123. package/src/replication/tcp-replication.test.ts +0 -876
  124. package/src/replication/zero-compat.test.ts +0 -1150
  125. package/src/restore-stress.test.ts +0 -188
  126. package/src/s3-local.ts +0 -203
  127. package/src/shim/hooks.mjs +0 -120
  128. package/src/shim/register.mjs +0 -4
  129. package/src/sqlite-mode/apply-mode.ts +0 -224
  130. package/src/sqlite-mode/index.ts +0 -15
  131. package/src/sqlite-mode/native-binary.ts +0 -89
  132. package/src/sqlite-mode/package-resolve.ts +0 -17
  133. package/src/sqlite-mode/resolve-mode.ts +0 -80
  134. package/src/sqlite-mode/shim-template.ts +0 -159
  135. package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
  136. package/src/sqlite-mode/types.ts +0 -30
  137. package/src/vite-plugin.ts +0 -67
  138. package/src/wasm-sqlite.test.ts +0 -537
  139. package/src/worker/browser-admin.ts +0 -52
  140. package/src/worker/browser-build-config.test.ts +0 -71
  141. package/src/worker/browser-build-config.ts +0 -109
  142. package/src/worker/browser-embed-admin.test.ts +0 -75
  143. package/src/worker/browser-embed.ts +0 -345
  144. package/src/worker/cf-patches.ts +0 -384
  145. package/src/worker/embed-integration.test.ts +0 -321
  146. package/src/worker/index.ts +0 -138
  147. package/src/worker/shims/fastify.test.ts +0 -255
  148. package/src/worker/shims/fastify.ts +0 -306
  149. package/src/worker/shims/http-service.test.ts +0 -355
  150. package/src/worker/shims/http-service.ts +0 -293
  151. package/src/worker/shims/node-stub.ts +0 -290
  152. package/src/worker/shims/oxfmt.ts +0 -3
  153. package/src/worker/shims/postgres-browser.ts +0 -59
  154. package/src/worker/shims/postgres-socket.test.ts +0 -576
  155. package/src/worker/shims/postgres-socket.ts +0 -310
  156. package/src/worker/shims/postgres.test.ts +0 -364
  157. package/src/worker/shims/postgres.ts +0 -1454
  158. package/src/worker/shims/sqlite-browser.test.ts +0 -233
  159. package/src/worker/shims/sqlite-browser.ts +0 -175
  160. package/src/worker/shims/sqlite.test.ts +0 -786
  161. package/src/worker/shims/sqlite.ts +0 -978
  162. package/src/worker/shims/stream-browser.ts +0 -15
  163. package/src/worker/shims/ws-browser.test.ts +0 -205
  164. package/src/worker/shims/ws-browser.ts +0 -248
  165. package/src/worker/shims/ws.test.ts +0 -288
  166. package/src/worker/shims/ws.ts +0 -467
  167. package/src/worker/shims/zero-process-env.ts +0 -11
  168. package/src/worker/types.ts +0 -75
  169. package/src/worker/worker-integration.test.ts +0 -223
  170. package/src/worker/worker.test.ts +0 -136
  171. package/src/worker/zero-cache-embed-cf.ts +0 -463
  172. package/src/worker/zero-cache-embed.ts +0 -277
@@ -1,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
- }