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
package/src/browser.ts DELETED
@@ -1,203 +0,0 @@
1
- /**
2
- * orez browser entry — aligned to orez-node architecture.
3
- *
4
- * mirrors index.ts startup sequence:
5
- * 1. create PGlite Web Workers (3 instances)
6
- * 2. install change tracking
7
- * 3. start pg-proxy-browser (wire protocol proxy)
8
- * 4. start zero-cache (SINGLE_PROCESS=1, connects to proxy)
9
- *
10
- * intended to run inside a Web Worker. PGlite instances run in
11
- * separate Web Workers (like orez-node's worker threads).
12
- */
13
-
14
- import { PGliteWebProxy } from './pglite-web-proxy.js'
15
- import { installChangeTracking } from './replication/change-tracker.js'
16
- import { resetReplicationState } from './replication/handler.js'
17
-
18
- import type { PGlite } from '@electric-sql/pglite'
19
-
20
- export interface OrezBrowserConfig {
21
- /** app ID for zero-cache (default: 'zero') */
22
- appId?: string
23
-
24
- /** publication names for replication */
25
- publications?: string[]
26
-
27
- /** URL for PGlite web worker script */
28
- pgliteWorkerUrl: string
29
-
30
- /** URL for bedrock-sqlite WASM */
31
- bedrockSqliteUrl?: string
32
-
33
- /** init SQL to run on postgres instance after creation */
34
- initSql?: string
35
-
36
- /** log level */
37
- logLevel?: string
38
-
39
- /**
40
- * intercept browser-mode orez admin routes before they reach zero-cache.
41
- * default: true.
42
- */
43
- disableAdminApi?: boolean
44
- }
45
-
46
- export interface OrezBrowserInstance {
47
- /** PGlite proxies (for direct queries from project-server) */
48
- instances: {
49
- postgres: PGliteWebProxy
50
- cvr: PGliteWebProxy
51
- cdb: PGliteWebProxy
52
- }
53
-
54
- /** signal the replication handler that changes are available */
55
- signalReplication(): void
56
-
57
- /** handle a WebSocket connection from a Zero client */
58
- handleWebSocket(ws: any, url?: string, headers?: Record<string, string>): void
59
-
60
- /** handle an HTTP request (push/pull) */
61
- handleHttp(request: {
62
- method: string
63
- url: string
64
- headers?: Record<string, string>
65
- body?: string | null
66
- }): Promise<{ status: number; headers: Record<string, string>; body: string }>
67
-
68
- /** stop everything */
69
- stop(): Promise<void>
70
- }
71
-
72
- export async function startOrezBrowser(
73
- config: OrezBrowserConfig
74
- ): Promise<OrezBrowserInstance> {
75
- const appId = config.appId || 'zero'
76
- const publications = config.publications?.join(',') || `orez_${appId}_public`
77
-
78
- // step 1: create PGlite Web Workers (3 instances, like orez-node)
79
- const pgPostgresWorker = new Worker(config.pgliteWorkerUrl, {
80
- type: 'module',
81
- name: 'pglite-postgres',
82
- })
83
- const pgCvrWorker = new Worker(config.pgliteWorkerUrl, {
84
- type: 'module',
85
- name: 'pglite-cvr',
86
- })
87
- const pgCdbWorker = new Worker(config.pgliteWorkerUrl, {
88
- type: 'module',
89
- name: 'pglite-cdb',
90
- })
91
-
92
- // init each PGlite worker
93
- pgPostgresWorker.postMessage({
94
- type: 'init',
95
- dataDir: 'idb://orez-postgres',
96
- name: 'postgres',
97
- })
98
- pgCvrWorker.postMessage({ type: 'init', dataDir: 'idb://orez-cvr', name: 'cvr' })
99
- pgCdbWorker.postMessage({ type: 'init', dataDir: 'idb://orez-cdb', name: 'cdb' })
100
-
101
- // create proxies (like orez-node's PGliteWorkerProxy)
102
- const pgPostgres = new PGliteWebProxy(pgPostgresWorker, 'postgres')
103
- const pgCvr = new PGliteWebProxy(pgCvrWorker, 'cvr')
104
- const pgCdb = new PGliteWebProxy(pgCdbWorker, 'cdb')
105
-
106
- await Promise.all([pgPostgres.waitReady, pgCvr.waitReady, pgCdb.waitReady])
107
- console.debug('[orez-browser] all 3 PGlite workers ready')
108
-
109
- // step 2: install change tracking (like orez-node)
110
- await installChangeTracking(pgPostgres as unknown as PGlite)
111
- console.debug('[orez-browser] change tracking installed')
112
-
113
- // run user init SQL if provided
114
- if (config.initSql) {
115
- await pgPostgres.exec(config.initSql)
116
- console.debug('[orez-browser] init SQL complete')
117
- }
118
-
119
- // create publication
120
- try {
121
- const pubs = await pgPostgres.query<{ count: string }>(
122
- `SELECT count(*) as count FROM pg_publication WHERE pubname = $1`,
123
- [publications]
124
- )
125
- if (Number(pubs.rows[0]?.count) === 0) {
126
- await pgPostgres.exec(`CREATE PUBLICATION "${publications}"`)
127
- }
128
- } catch {}
129
-
130
- // step 3: start pg-proxy-browser
131
- // the proxy handles wire protocol, replication, mutexes — like orez-node's pg-proxy.
132
- // zero-cache's postgres shim routes queries through this proxy.
133
- const { createBrowserProxy } = await import('./pg-proxy-browser.js')
134
- const proxy = await createBrowserProxy(
135
- {
136
- postgres: pgPostgres as unknown as PGlite,
137
- cvr: pgCvr as unknown as PGlite,
138
- cdb: pgCdb as unknown as PGlite,
139
- postgresReplicas: [],
140
- },
141
- { pgPassword: '', pgUser: 'user' }
142
- )
143
- console.debug('[orez-browser] pg-proxy-browser started')
144
-
145
- // step 4: start zero-cache (SINGLE_PROCESS=1)
146
- // zero-cache uses the real postgres package with a MessagePort socket
147
- // that connects to pg-proxy-browser. identical to orez-node where
148
- // zero-cache uses real postgres over TCP to pg-proxy.
149
- //
150
- // set up the proxy connect function for postgres-browser.ts shim
151
- ;(globalThis as any).__orez_proxy_connect = (port: MessagePort) => {
152
- proxy.handleConnection(port)
153
- }
154
- ;(globalThis as any).__orez_proxy_password = ''
155
- ;(globalThis as any).__orez_proxy_user = 'user'
156
-
157
- // PGlite global still needed for browser-embed's sqlite setup
158
- ;(globalThis as any).__orez_pglite = pgPostgres
159
-
160
- // start zero-cache via browser-embed's runWorker
161
- const { startZeroCacheEmbedBrowser } = await import('./worker/browser-embed.js')
162
- const zc = await startZeroCacheEmbedBrowser({
163
- pglite: pgPostgres as unknown as PGlite,
164
- appId,
165
- publications: config.publications,
166
- disableAdminApi: config.disableAdminApi ?? true,
167
- env: {
168
- ZERO_LOG_LEVEL: config.logLevel || 'info',
169
- },
170
- })
171
- console.debug('[orez-browser] zero-cache started')
172
-
173
- // step 5: expose API
174
- const { signalReplicationChange } = await import('./replication/handler.js')
175
-
176
- return {
177
- instances: { postgres: pgPostgres, cvr: pgCvr, cdb: pgCdb },
178
-
179
- signalReplication() {
180
- signalReplicationChange()
181
- },
182
-
183
- handleWebSocket(ws: any, url = '/', headers?: Record<string, string>) {
184
- zc.handleWebSocket(ws, url)
185
- },
186
-
187
- async handleHttp(request: {
188
- method: string
189
- url: string
190
- headers?: Record<string, string>
191
- body?: string | null
192
- }) {
193
- return zc.handleHttp(request)
194
- },
195
-
196
- async stop() {
197
- await zc.stop()
198
- proxy.close()
199
- resetReplicationState()
200
- await Promise.all([pgPostgres.close(), pgCvr.close(), pgCdb.close()])
201
- },
202
- }
203
- }
@@ -1 +0,0 @@
1
- {"httpProtocol":"HTTP/1.1","clientAcceptEncoding":"gzip, deflate, br","requestPriority":"","edgeRequestKeepAliveStatus":1,"requestHeaderNames":{},"clientTcpRtt":74,"clientQuicRtt":0,"colo":"LAX","asn":21928,"asOrganization":"T-Mobile USA, Inc.","country":"US","isEUCountry":false,"city":"Honolulu","continent":"NA","region":"Hawaii","regionCode":"HI","timezone":"Pacific/Honolulu","longitude":"-157.85833","latitude":"21.30694","postalCode":"96802","metroCode":"744","tlsVersion":"TLSv1.3","tlsCipher":"AEAD-AES256-GCM-SHA384","tlsClientRandom":"8lAacoE+Gg3H5AKn8BQNpAGr3fgr0OV1txs9E+GEKXk=","tlsClientCiphersSha1":"JZtiTn8H/ntxORk+XXvU2EvNoz8=","tlsClientExtensionsSha1":"Y7DIC8A6G0/aXviZ8ie/xDbJb7g=","tlsClientExtensionsSha1Le":"6e+q3vPm88rSgMTN/h7WTTxQ2wQ=","tlsExportedAuthenticator":{"clientHandshake":"88b6a0bf9d7b4c6bc26b9f11255fe3f55b2bae485c86026892687913505e6a337a54221c8ec52b88741035fde8e80b91","serverHandshake":"a611a55711347da40c0167874992b47d75a4de9ed1c0bc731dd1f80051efde3b73a8baff4e484f30b284d662cd503dcb","clientFinished":"81b77db43ca14e8934ebd5f4c6d3656a1ccc726f04a10164cf7d599128d7dfbd3fa15f932068a583f288a0327d69a5f5","serverFinished":"68efa1737e629e56c92cbb680f96730052e8269e6248b62dd26a62d3ea7be55991e9f0ea85e6bebf95dbc6adbb41334e"},"tlsClientHelloLength":"386","tlsClientAuth":{"certPresented":"0","certVerified":"NONE","certRevoked":"0","certIssuerDN":"","certSubjectDN":"","certIssuerDNRFC2253":"","certSubjectDNRFC2253":"","certIssuerDNLegacy":"","certSubjectDNLegacy":"","certSerial":"","certIssuerSerial":"","certSKI":"","certIssuerSKI":"","certFingerprintSHA1":"","certFingerprintSHA256":"","certNotBefore":"","certNotAfter":"","certRFC9440":"","certRFC9440TooLarge":false,"certChainRFC9440":"","certChainRFC9440TooLarge":false},"verifiedBotCategory":"","edgeL4":{"deliveryRate":52536},"botManagement":{"corporateProxy":false,"verifiedBot":false,"jsDetection":{"passed":false},"staticResource":false,"detectionIds":{},"score":99}}
@@ -1,83 +0,0 @@
1
- # orez/cf-do architecture - READ FIRST
2
-
3
- ## The fundamental constraint
4
-
5
- **Cloudflare Durable Objects have a 128 MB memory budget per instance.**
6
-
7
- PGlite + WASM Postgres + extension binaries pushes real deployments over that
8
- budget. That is the dead end. zero-cache is still essential: it owns the sync
9
- protocol, IVM/CVR machinery, replication handling, and client semantics.
10
-
11
- The orez Cloudflare path is therefore:
12
-
13
- - **yes** real `@rocicorp/zero` zero-cache
14
- - **yes** Durable Object SQLite as the durable storage engine
15
- - **yes** orez `DoBackend` serving Postgres-protocol semantics to zero-cache
16
- - **no** PGlite
17
- - **no** `pglite.wasm`, `pglite.data`, extension `.so` files, or WASM Postgres
18
-
19
- ## Production shape
20
-
21
- ```
22
- ┌────────────────────────────────────────────────────────────────┐
23
- │ Worker │
24
- │ │
25
- │ /sync/v* and /api/zero/* ─────► ZERO_CACHE_DO singleton │
26
- │ everything else ──────────────► ASSETS │
27
- └────────────────────────────────────────────────────────────────┘
28
-
29
- ┌────────────────────────────────────────────────────────────────┐
30
- │ ZERO_CACHE_DO Durable Object │
31
- │ │
32
- │ startZeroCacheEmbedCF() runs real zero-cache in-process │
33
- │ │ │
34
- │ ├─ zero-cache replica/CVR/CDB SQLite │
35
- │ │ @rocicorp/zero-sqlite3 -> orez worker SQLite shim │
36
- │ │ backed by ctx.storage.sql │
37
- │ │ │
38
- │ └─ zero-cache upstream Postgres connections │
39
- │ postgres -> orez postgres browser shim │
40
- │ DoBackend -> ZERO_SQL_DO /exec and /batch │
41
- └────────────────────────────────────────────────────────────────┘
42
-
43
- ┌────────────────────────────────────────────────────────────────┐
44
- │ ZERO_SQL_DO Durable Object │
45
- │ │
46
- │ ZeroDO raw SQL endpoints │
47
- │ /exec, /batch, /changes, /notify, /__orez/* │
48
- │ ctx.storage.sql │
49
- │ _orez.changes populated by SQL tracking triggers │
50
- └────────────────────────────────────────────────────────────────┘
51
- ```
52
-
53
- All app traffic for a deployed project must use singleton DO IDs for both
54
- `ZERO_CACHE_DO` and `ZERO_SQL_DO`; otherwise browser sessions will not share the
55
- same zero-cache process and durable SQLite state.
56
-
57
- ## Important files
58
-
59
- - `src/worker/zero-cache-embed-cf.ts` - starts real zero-cache inside a Durable
60
- Object and wires its storage/network dependencies to CF-safe shims.
61
- - `src/worker/cf-patches.ts` - patches zero-cache internals so its worker
62
- graph and writer run in the Workers runtime.
63
- - `src/pg-proxy-do-backend.ts` - translates Postgres protocol operations from
64
- zero-cache into DO SQL endpoint requests.
65
- - `src/cf-do/worker.ts` - `ZeroDO`, the generic DO SQL backend. It also still
66
- contains a bespoke Zero sync protocol handler used for development and
67
- protocol experiments, but the production Soot deploy path uses real
68
- zero-cache through `startZeroCacheEmbedCF()`.
69
- - `src/do-sql-tracking.ts` and `src/replication/*` - change tracking and
70
- logical replication support over `_orez.changes`.
71
-
72
- ## What not to do
73
-
74
- - Do not import `@electric-sql/pglite` into the Cloudflare DO deploy path.
75
- - Do not bundle PGlite WASM/data/extensions into the deploy template.
76
- - Do not replace zero-cache with the bespoke handler for production sync.
77
- - Do not add a second fallback path that silently switches between PGlite,
78
- bespoke sync, and zero-cache. There should be one production path:
79
- zero-cache -> orez Postgres protocol -> DO SQLite.
80
-
81
- If a future change needs more Postgres behavior, implement it in
82
- `DoBackend`/the SQL translator or the DO SQL backend. Do not put PGlite back in
83
- the request path.
@@ -1,103 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
-
3
- import { DurableWatermarkState } from './watermark.js'
4
-
5
- class FakeResult {
6
- constructor(private readonly rows: Array<Record<string, unknown>> = []) {}
7
-
8
- one() {
9
- return this.rows[0]
10
- }
11
-
12
- toArray() {
13
- return this.rows
14
- }
15
- }
16
-
17
- class FakeSql {
18
- changes: Array<{ watermark: number }> = []
19
- state = 0
20
- sequence = { name: '_orez___zero_watermark', last_value: 1, is_called: 0 }
21
-
22
- exec(sql: string, ...params: unknown[]) {
23
- if (sql.startsWith('CREATE TABLE IF NOT EXISTS')) return new FakeResult()
24
-
25
- if (sql.startsWith('SELECT last_value FROM "_zero_change_state"')) {
26
- return new FakeResult([{ last_value: this.state }])
27
- }
28
-
29
- if (sql.startsWith('INSERT OR IGNORE INTO "_zero_change_state"')) {
30
- return new FakeResult()
31
- }
32
-
33
- if (sql.startsWith('UPDATE "_zero_change_state" SET last_value = ?')) {
34
- this.state = Number(params[0])
35
- return new FakeResult()
36
- }
37
-
38
- if (
39
- sql.startsWith('SELECT COALESCE(MAX(watermark), 0) AS watermark FROM _zero_changes')
40
- ) {
41
- const watermark = Math.max(0, ...this.changes.map((change) => change.watermark))
42
- return new FakeResult([{ watermark }])
43
- }
44
-
45
- if (sql.includes('sqlite_master') && sql.includes('%zero_watermark%')) {
46
- return new FakeResult([{ name: this.sequence.name }])
47
- }
48
-
49
- if (sql.startsWith('SELECT last_value, is_called FROM "_orez___zero_watermark"')) {
50
- return new FakeResult([
51
- {
52
- last_value: this.sequence.last_value,
53
- is_called: this.sequence.is_called,
54
- },
55
- ])
56
- }
57
-
58
- if (sql.startsWith('INSERT OR IGNORE INTO "_orez___zero_watermark"')) {
59
- return new FakeResult()
60
- }
61
-
62
- if (sql.startsWith('UPDATE "_orez___zero_watermark" SET last_value = ?')) {
63
- this.sequence.last_value = Number(params[0])
64
- this.sequence.is_called = 1
65
- return new FakeResult()
66
- }
67
-
68
- throw new Error(`unexpected fake sql: ${sql}`)
69
- }
70
- }
71
-
72
- describe('DurableWatermarkState', () => {
73
- it('does not reuse watermarks after consumed changes are purged', () => {
74
- const sql = new FakeSql()
75
- const watermarks = new DurableWatermarkState(sql)
76
-
77
- expect(watermarks.current()).toBe(0)
78
-
79
- const first = watermarks.next()
80
- sql.changes.push({ watermark: first })
81
- watermarks.mark(first)
82
- expect(first).toBe(1)
83
-
84
- sql.changes = []
85
-
86
- const second = watermarks.next()
87
- sql.changes.push({ watermark: second })
88
- watermarks.mark(second)
89
-
90
- expect(second).toBe(2)
91
- expect(sql.sequence).toMatchObject({ last_value: 2, is_called: 1 })
92
- })
93
-
94
- it('synchronizes from existing change rows and sequence state', () => {
95
- const sql = new FakeSql()
96
- sql.changes.push({ watermark: 7 })
97
- const watermarks = new DurableWatermarkState(sql)
98
-
99
- expect(watermarks.current()).toBe(7)
100
- expect(sql.state).toBe(7)
101
- expect(sql.sequence).toMatchObject({ last_value: 7, is_called: 1 })
102
- })
103
- })
@@ -1,118 +0,0 @@
1
- export interface DurableSqlResult {
2
- one(): Record<string, unknown> | undefined
3
- toArray(): Array<Record<string, unknown>>
4
- }
5
-
6
- export interface DurableSqlStorage {
7
- exec(sql: string, ...params: unknown[]): DurableSqlResult
8
- }
9
-
10
- const WATERMARK_STATE_TABLE = '_zero_change_state'
11
-
12
- function quoteIdent(name: string): string {
13
- return `"${name.replace(/"/g, '""')}"`
14
- }
15
-
16
- function finitePositiveNumber(value: unknown): number {
17
- const number = Number(value ?? 0)
18
- return Number.isFinite(number) && number > 0 ? number : 0
19
- }
20
-
21
- export class DurableWatermarkState {
22
- constructor(private readonly sql: DurableSqlStorage) {}
23
-
24
- ensureTables(): void {
25
- this.sql.exec(
26
- "CREATE TABLE IF NOT EXISTS _zero_changes (watermark INTEGER PRIMARY KEY AUTOINCREMENT, table_name TEXT NOT NULL, op TEXT NOT NULL CHECK (op IN ('INSERT', 'UPDATE', 'DELETE')), row_data TEXT, old_data TEXT, created_at INTEGER NOT NULL DEFAULT (unixepoch()))"
27
- )
28
- this.sql.exec(
29
- `CREATE TABLE IF NOT EXISTS ${quoteIdent(WATERMARK_STATE_TABLE)} (id INTEGER PRIMARY KEY CHECK (id = 1), last_value INTEGER NOT NULL DEFAULT 0)`
30
- )
31
- this.setWatermarkState(this.watermarkState())
32
- }
33
-
34
- next(): number {
35
- return this.current() + 1
36
- }
37
-
38
- mark(watermark: number): void {
39
- this.setWatermarkState(watermark)
40
- this.updateWatermarkSequences(watermark)
41
- }
42
-
43
- current(): number {
44
- this.ensureTables()
45
- const state = this.watermarkState()
46
- const row = this.sql
47
- .exec('SELECT COALESCE(MAX(watermark), 0) AS watermark FROM _zero_changes')
48
- .one() as { watermark?: unknown } | undefined
49
- const tableWatermark = finitePositiveNumber(row?.watermark)
50
- const sequenceWatermark = this.watermarkSequenceValue()
51
- const watermark = Math.max(state, tableWatermark, sequenceWatermark)
52
- if (watermark > state) this.setWatermarkState(watermark)
53
- if (watermark > sequenceWatermark) this.updateWatermarkSequences(watermark)
54
- return watermark
55
- }
56
-
57
- private watermarkState(): number {
58
- try {
59
- const table = quoteIdent(WATERMARK_STATE_TABLE)
60
- const row = this.sql.exec(`SELECT last_value FROM ${table} WHERE id = 1`).one() as
61
- | { last_value?: unknown }
62
- | undefined
63
- return finitePositiveNumber(row?.last_value)
64
- } catch {
65
- return 0
66
- }
67
- }
68
-
69
- private setWatermarkState(watermark: number): void {
70
- const table = quoteIdent(WATERMARK_STATE_TABLE)
71
- this.sql.exec(`INSERT OR IGNORE INTO ${table} (id, last_value) VALUES (1, 0)`)
72
- this.sql.exec(`UPDATE ${table} SET last_value = ? WHERE id = 1`, watermark)
73
- }
74
-
75
- private watermarkSequenceValue(): number {
76
- let watermark = 0
77
- for (const name of this.watermarkSequenceTables()) {
78
- try {
79
- const row = this.sql
80
- .exec(`SELECT last_value, is_called FROM ${quoteIdent(name)} WHERE dummy = 1`)
81
- .one() as { last_value?: unknown; is_called?: unknown } | undefined
82
- if (!row || !row.is_called) continue
83
- watermark = Math.max(watermark, finitePositiveNumber(row.last_value))
84
- } catch {
85
- /* not an orez sequence table */
86
- }
87
- }
88
- return watermark
89
- }
90
-
91
- private watermarkSequenceTables(): string[] {
92
- return this.sql
93
- .exec(
94
- "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%zero_watermark%'"
95
- )
96
- .toArray()
97
- .map((row) => String(row.name || ''))
98
- .filter(Boolean)
99
- }
100
-
101
- private updateWatermarkSequences(watermark: number): void {
102
- for (const name of this.watermarkSequenceTables()) {
103
- const table = quoteIdent(name)
104
- try {
105
- this.sql.exec(
106
- `INSERT OR IGNORE INTO ${table} (dummy, last_value, is_called) VALUES (1, ?, 1)`,
107
- watermark
108
- )
109
- this.sql.exec(
110
- `UPDATE ${table} SET last_value = ?, is_called = 1 WHERE dummy = 1`,
111
- watermark
112
- )
113
- } catch {
114
- /* not an orez sequence table */
115
- }
116
- }
117
- }
118
- }