orez 0.9.7 → 0.10.0

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 (249) hide show
  1. package/README.md +27 -19
  2. package/dist/config.d.ts +0 -4
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +0 -1
  5. package/dist/config.js.map +1 -1
  6. package/dist/index.d.ts +0 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +5 -43
  9. package/dist/index.js.map +1 -1
  10. package/package.json +7 -71
  11. package/dist/cf-deploy/bundle.d.ts +0 -21
  12. package/dist/cf-deploy/bundle.d.ts.map +0 -1
  13. package/dist/cf-deploy/bundle.js +0 -347
  14. package/dist/cf-deploy/bundle.js.map +0 -1
  15. package/dist/cf-deploy/config.d.ts +0 -52
  16. package/dist/cf-deploy/config.d.ts.map +0 -1
  17. package/dist/cf-deploy/config.js +0 -23
  18. package/dist/cf-deploy/config.js.map +0 -1
  19. package/dist/cf-deploy/index.d.ts +0 -11
  20. package/dist/cf-deploy/index.d.ts.map +0 -1
  21. package/dist/cf-deploy/index.js +0 -17
  22. package/dist/cf-deploy/index.js.map +0 -1
  23. package/dist/cf-deploy/leaves.d.ts +0 -19
  24. package/dist/cf-deploy/leaves.d.ts.map +0 -1
  25. package/dist/cf-deploy/leaves.js +0 -109
  26. package/dist/cf-deploy/leaves.js.map +0 -1
  27. package/dist/cf-deploy/migration.d.ts +0 -40
  28. package/dist/cf-deploy/migration.d.ts.map +0 -1
  29. package/dist/cf-deploy/migration.js +0 -891
  30. package/dist/cf-deploy/migration.js.map +0 -1
  31. package/dist/cf-deploy/nativeMigrations.d.ts +0 -24
  32. package/dist/cf-deploy/nativeMigrations.d.ts.map +0 -1
  33. package/dist/cf-deploy/nativeMigrations.js +0 -185
  34. package/dist/cf-deploy/nativeMigrations.js.map +0 -1
  35. package/dist/cf-deploy/prune.d.ts +0 -14
  36. package/dist/cf-deploy/prune.d.ts.map +0 -1
  37. package/dist/cf-deploy/prune.js +0 -235
  38. package/dist/cf-deploy/prune.js.map +0 -1
  39. package/dist/cf-deploy/shims.d.ts +0 -13
  40. package/dist/cf-deploy/shims.d.ts.map +0 -1
  41. package/dist/cf-deploy/shims.js +0 -737
  42. package/dist/cf-deploy/shims.js.map +0 -1
  43. package/dist/cf-deploy/smoke.d.ts +0 -15
  44. package/dist/cf-deploy/smoke.d.ts.map +0 -1
  45. package/dist/cf-deploy/smoke.js +0 -141
  46. package/dist/cf-deploy/smoke.js.map +0 -1
  47. package/dist/cf-deploy/sources.d.ts +0 -9
  48. package/dist/cf-deploy/sources.d.ts.map +0 -1
  49. package/dist/cf-deploy/sources.js +0 -276
  50. package/dist/cf-deploy/sources.js.map +0 -1
  51. package/dist/cf-deploy/wrangler.d.ts +0 -6
  52. package/dist/cf-deploy/wrangler.d.ts.map +0 -1
  53. package/dist/cf-deploy/wrangler.js +0 -168
  54. package/dist/cf-deploy/wrangler.js.map +0 -1
  55. package/dist/cf-do/application-sql.d.ts +0 -49
  56. package/dist/cf-do/application-sql.d.ts.map +0 -1
  57. package/dist/cf-do/application-sql.js +0 -124
  58. package/dist/cf-do/application-sql.js.map +0 -1
  59. package/dist/cf-do/cdc.d.ts +0 -155
  60. package/dist/cf-do/cdc.d.ts.map +0 -1
  61. package/dist/cf-do/cdc.js +0 -719
  62. package/dist/cf-do/cdc.js.map +0 -1
  63. package/dist/cf-do/cursor-pull.d.ts +0 -27
  64. package/dist/cf-do/cursor-pull.d.ts.map +0 -1
  65. package/dist/cf-do/cursor-pull.js +0 -78
  66. package/dist/cf-do/cursor-pull.js.map +0 -1
  67. package/dist/cf-do/row-undo.d.ts +0 -36
  68. package/dist/cf-do/row-undo.d.ts.map +0 -1
  69. package/dist/cf-do/row-undo.js +0 -221
  70. package/dist/cf-do/row-undo.js.map +0 -1
  71. package/dist/cf-do/tx-journal.d.ts +0 -119
  72. package/dist/cf-do/tx-journal.d.ts.map +0 -1
  73. package/dist/cf-do/tx-journal.js +0 -718
  74. package/dist/cf-do/tx-journal.js.map +0 -1
  75. package/dist/cf-do/watermark.d.ts +0 -29
  76. package/dist/cf-do/watermark.d.ts.map +0 -1
  77. package/dist/cf-do/watermark.js +0 -105
  78. package/dist/cf-do/watermark.js.map +0 -1
  79. package/dist/cf-do/worker.d.ts +0 -287
  80. package/dist/cf-do/worker.d.ts.map +0 -1
  81. package/dist/cf-do/worker.js +0 -1961
  82. package/dist/cf-do/worker.js.map +0 -1
  83. package/dist/do-sql-tracking.d.ts +0 -104
  84. package/dist/do-sql-tracking.d.ts.map +0 -1
  85. package/dist/do-sql-tracking.js +0 -255
  86. package/dist/do-sql-tracking.js.map +0 -1
  87. package/dist/pg-proxy-do-backend.d.ts +0 -211
  88. package/dist/pg-proxy-do-backend.d.ts.map +0 -1
  89. package/dist/pg-proxy-do-backend.js +0 -7884
  90. package/dist/pg-proxy-do-backend.js.map +0 -1
  91. package/dist/pg-sqlite-compiler/catalog/seed.d.ts +0 -67
  92. package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +0 -1
  93. package/dist/pg-sqlite-compiler/catalog/seed.js +0 -436
  94. package/dist/pg-sqlite-compiler/catalog/seed.js.map +0 -1
  95. package/dist/pg-sqlite-compiler/compiler.d.ts +0 -17
  96. package/dist/pg-sqlite-compiler/compiler.d.ts.map +0 -1
  97. package/dist/pg-sqlite-compiler/compiler.js +0 -86
  98. package/dist/pg-sqlite-compiler/compiler.js.map +0 -1
  99. package/dist/pg-sqlite-compiler/index.d.ts +0 -2
  100. package/dist/pg-sqlite-compiler/index.d.ts.map +0 -1
  101. package/dist/pg-sqlite-compiler/index.js +0 -13
  102. package/dist/pg-sqlite-compiler/index.js.map +0 -1
  103. package/dist/pg-sqlite-compiler/passes/array.d.ts +0 -3
  104. package/dist/pg-sqlite-compiler/passes/array.d.ts.map +0 -1
  105. package/dist/pg-sqlite-compiler/passes/array.js +0 -223
  106. package/dist/pg-sqlite-compiler/passes/array.js.map +0 -1
  107. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +0 -48
  108. package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +0 -1
  109. package/dist/pg-sqlite-compiler/passes/ast-utils.js +0 -93
  110. package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +0 -1
  111. package/dist/pg-sqlite-compiler/passes/catalog.d.ts +0 -34
  112. package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +0 -1
  113. package/dist/pg-sqlite-compiler/passes/catalog.js +0 -30
  114. package/dist/pg-sqlite-compiler/passes/catalog.js.map +0 -1
  115. package/dist/pg-sqlite-compiler/passes/datetime.d.ts +0 -23
  116. package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +0 -1
  117. package/dist/pg-sqlite-compiler/passes/datetime.js +0 -55
  118. package/dist/pg-sqlite-compiler/passes/datetime.js.map +0 -1
  119. package/dist/pg-sqlite-compiler/passes/dml-cte.d.ts +0 -22
  120. package/dist/pg-sqlite-compiler/passes/dml-cte.d.ts.map +0 -1
  121. package/dist/pg-sqlite-compiler/passes/dml-cte.js +0 -161
  122. package/dist/pg-sqlite-compiler/passes/dml-cte.js.map +0 -1
  123. package/dist/pg-sqlite-compiler/passes/index.d.ts +0 -24
  124. package/dist/pg-sqlite-compiler/passes/index.d.ts.map +0 -1
  125. package/dist/pg-sqlite-compiler/passes/index.js +0 -52
  126. package/dist/pg-sqlite-compiler/passes/index.js.map +0 -1
  127. package/dist/pg-sqlite-compiler/passes/json-functions.d.ts +0 -3
  128. package/dist/pg-sqlite-compiler/passes/json-functions.d.ts.map +0 -1
  129. package/dist/pg-sqlite-compiler/passes/json-functions.js +0 -57
  130. package/dist/pg-sqlite-compiler/passes/json-functions.js.map +0 -1
  131. package/dist/pg-sqlite-compiler/passes/row-json.d.ts +0 -3
  132. package/dist/pg-sqlite-compiler/passes/row-json.d.ts.map +0 -1
  133. package/dist/pg-sqlite-compiler/passes/row-json.js +0 -230
  134. package/dist/pg-sqlite-compiler/passes/row-json.js.map +0 -1
  135. package/dist/pg-sqlite-compiler/passes/schema.d.ts +0 -4
  136. package/dist/pg-sqlite-compiler/passes/schema.d.ts.map +0 -1
  137. package/dist/pg-sqlite-compiler/passes/schema.js +0 -128
  138. package/dist/pg-sqlite-compiler/passes/schema.js.map +0 -1
  139. package/dist/pg-sqlite-compiler/passes/string-functions.d.ts +0 -11
  140. package/dist/pg-sqlite-compiler/passes/string-functions.d.ts.map +0 -1
  141. package/dist/pg-sqlite-compiler/passes/string-functions.js +0 -37
  142. package/dist/pg-sqlite-compiler/passes/string-functions.js.map +0 -1
  143. package/dist/pg-sqlite-compiler/passes/types.d.ts +0 -41
  144. package/dist/pg-sqlite-compiler/passes/types.d.ts.map +0 -1
  145. package/dist/pg-sqlite-compiler/passes/types.js +0 -115
  146. package/dist/pg-sqlite-compiler/passes/types.js.map +0 -1
  147. package/dist/pg-sqlite-compiler/passes/unsupported.d.ts +0 -3
  148. package/dist/pg-sqlite-compiler/passes/unsupported.d.ts.map +0 -1
  149. package/dist/pg-sqlite-compiler/passes/unsupported.js +0 -57
  150. package/dist/pg-sqlite-compiler/passes/unsupported.js.map +0 -1
  151. package/dist/pg-sqlite-compiler/test/oracle.d.ts +0 -34
  152. package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +0 -1
  153. package/dist/pg-sqlite-compiler/test/oracle.js +0 -204
  154. package/dist/pg-sqlite-compiler/test/oracle.js.map +0 -1
  155. package/dist/pg-sqlite-compiler/types.d.ts +0 -66
  156. package/dist/pg-sqlite-compiler/types.d.ts.map +0 -1
  157. package/dist/pg-sqlite-compiler/types.js +0 -2
  158. package/dist/pg-sqlite-compiler/types.js.map +0 -1
  159. package/dist/sync-browser-host/generated/sqlite3-browser.wasm +0 -0
  160. package/dist/sync-browser-host/generated/sync_wasm.js +0 -922
  161. package/dist/sync-browser-host/generated/sync_wasm_bg.wasm +0 -0
  162. package/dist/sync-browser-host/host.d.ts +0 -10
  163. package/dist/sync-browser-host/host.d.ts.map +0 -1
  164. package/dist/sync-browser-host/host.js +0 -496
  165. package/dist/sync-browser-host/host.js.map +0 -1
  166. package/dist/sync-browser-host/idb-snapshot.d.ts +0 -11
  167. package/dist/sync-browser-host/idb-snapshot.d.ts.map +0 -1
  168. package/dist/sync-browser-host/idb-snapshot.js +0 -122
  169. package/dist/sync-browser-host/idb-snapshot.js.map +0 -1
  170. package/dist/sync-browser-host/index.d.ts +0 -5
  171. package/dist/sync-browser-host/index.d.ts.map +0 -1
  172. package/dist/sync-browser-host/index.js +0 -5
  173. package/dist/sync-browser-host/index.js.map +0 -1
  174. package/dist/sync-browser-host/message-port.d.ts +0 -7
  175. package/dist/sync-browser-host/message-port.d.ts.map +0 -1
  176. package/dist/sync-browser-host/message-port.js +0 -188
  177. package/dist/sync-browser-host/message-port.js.map +0 -1
  178. package/dist/sync-browser-host/sqlite-adapter.d.ts +0 -67
  179. package/dist/sync-browser-host/sqlite-adapter.d.ts.map +0 -1
  180. package/dist/sync-browser-host/sqlite-adapter.js +0 -145
  181. package/dist/sync-browser-host/sqlite-adapter.js.map +0 -1
  182. package/dist/sync-browser-host/types.d.ts +0 -52
  183. package/dist/sync-browser-host/types.d.ts.map +0 -1
  184. package/dist/sync-browser-host/types.js +0 -2
  185. package/dist/sync-browser-host/types.js.map +0 -1
  186. package/dist/sync-cf-host.d.ts +0 -2
  187. package/dist/sync-cf-host.d.ts.map +0 -1
  188. package/dist/sync-cf-host.js +0 -2
  189. package/dist/sync-cf-host.js.map +0 -1
  190. package/dist/sync-executor.d.ts +0 -2
  191. package/dist/sync-executor.d.ts.map +0 -1
  192. package/dist/sync-executor.js +0 -2
  193. package/dist/sync-executor.js.map +0 -1
  194. package/dist/wake.d.ts +0 -22
  195. package/dist/wake.d.ts.map +0 -1
  196. package/dist/wake.js +0 -114
  197. package/dist/wake.js.map +0 -1
  198. package/dist/worker/cf-do-shim.d.ts +0 -55
  199. package/dist/worker/cf-do-shim.d.ts.map +0 -1
  200. package/dist/worker/cf-do-shim.js +0 -58
  201. package/dist/worker/cf-do-shim.js.map +0 -1
  202. package/dist/worker/cf-patches.d.ts +0 -43
  203. package/dist/worker/cf-patches.d.ts.map +0 -1
  204. package/dist/worker/cf-patches.js +0 -840
  205. package/dist/worker/cf-patches.js.map +0 -1
  206. package/dist/worker/local-sql-backend.d.ts +0 -35
  207. package/dist/worker/local-sql-backend.d.ts.map +0 -1
  208. package/dist/worker/local-sql-backend.js +0 -217
  209. package/dist/worker/local-sql-backend.js.map +0 -1
  210. package/dist/worker/zero-cache-embed-cf.d.ts +0 -92
  211. package/dist/worker/zero-cache-embed-cf.d.ts.map +0 -1
  212. package/dist/worker/zero-cache-embed-cf.js +0 -764
  213. package/dist/worker/zero-cache-embed-cf.js.map +0 -1
  214. package/dist/worker/zero-sql-write-circuit.d.ts +0 -46
  215. package/dist/worker/zero-sql-write-circuit.d.ts.map +0 -1
  216. package/dist/worker/zero-sql-write-circuit.js +0 -161
  217. package/dist/worker/zero-sql-write-circuit.js.map +0 -1
  218. package/dist/zero-http/encrypted-column-codec.d.ts +0 -46
  219. package/dist/zero-http/encrypted-column-codec.d.ts.map +0 -1
  220. package/dist/zero-http/encrypted-column-codec.js +0 -676
  221. package/dist/zero-http/encrypted-column-codec.js.map +0 -1
  222. package/dist/zero-http/encrypted-column-conformance.d.ts +0 -7
  223. package/dist/zero-http/encrypted-column-conformance.d.ts.map +0 -1
  224. package/dist/zero-http/encrypted-column-conformance.js +0 -161
  225. package/dist/zero-http/encrypted-column-conformance.js.map +0 -1
  226. package/dist/zero-http/fixture-schema.d.ts +0 -127
  227. package/dist/zero-http/fixture-schema.d.ts.map +0 -1
  228. package/dist/zero-http/fixture-schema.js +0 -56
  229. package/dist/zero-http/fixture-schema.js.map +0 -1
  230. package/dist/zero-http/mount.d.ts +0 -71
  231. package/dist/zero-http/mount.d.ts.map +0 -1
  232. package/dist/zero-http/mount.js +0 -594
  233. package/dist/zero-http/mount.js.map +0 -1
  234. package/dist/zero-http/payload-codec.d.ts +0 -91
  235. package/dist/zero-http/payload-codec.d.ts.map +0 -1
  236. package/dist/zero-http/payload-codec.js +0 -10
  237. package/dist/zero-http/payload-codec.js.map +0 -1
  238. package/dist/zero-http/server.d.ts +0 -16
  239. package/dist/zero-http/server.d.ts.map +0 -1
  240. package/dist/zero-http/server.js +0 -270
  241. package/dist/zero-http/server.js.map +0 -1
  242. package/dist/zero-http/test-harness.d.ts +0 -32
  243. package/dist/zero-http/test-harness.d.ts.map +0 -1
  244. package/dist/zero-http/test-harness.js +0 -77
  245. package/dist/zero-http/test-harness.js.map +0 -1
  246. package/dist/zero-http/transport.d.ts +0 -71
  247. package/dist/zero-http/transport.d.ts.map +0 -1
  248. package/dist/zero-http/transport.js +0 -1106
  249. package/dist/zero-http/transport.js.map +0 -1
@@ -1,764 +0,0 @@
1
- /**
2
- * zero-cache embedded runner for cloudflare workers.
3
- *
4
- * runs zero-cache in-process with SINGLE_PROCESS=1, using bundler aliases
5
- * to swap Node.js dependencies for CF-compatible shims:
6
- *
7
- * postgres → orez/worker/shims/postgres-browser
8
- * (real postgres package over MessagePort)
9
- * @rocicorp/zero-sqlite3 → orez/worker/shims/sqlite (DO SQLite)
10
- * fastify → orez/worker/shims/fastify (route capture)
11
- * ws → orez/worker/shims/ws (CF WebSocket)
12
- *
13
- * the postgres MessagePort proxy is backed by DoBackend, so zero-cache still
14
- * uses its real PG wire protocol, but storage is Cloudflare DO SQLite instead
15
- * of PGlite.
16
- *
17
- * usage in a Durable Object:
18
- *
19
- * import { startZeroCacheEmbedCF } from 'orez/worker'
20
- *
21
- * // in ensureInitialized():
22
- * const zc = await startZeroCacheEmbedCF({ ... })
23
- *
24
- * // in DO fetch():
25
- * return zc.handleRequest(request)
26
- *
27
- * the embed uses standard-accepted WebSockets. do not route these sockets
28
- * through Durable Object hibernation APIs; the in-process zero-cache proxy must
29
- * keep its live bridge for the connection lifetime.
30
- *
31
- * idle hibernation is achieved at a coarser grain instead: the DO watches
32
- * `connectionCount` and, once it hits 0 past a grace window, calls stop() to
33
- * tear the whole embed down. with no live bridge and no zero-cache timers left,
34
- * the DO is evicted and stops accruing GB-s; the next request cold-starts it
35
- * again from durable DO SQLite. see plans/cf-do-idle-hibernation.md and
36
- * `shouldHibernateIdleZeroCache` in ./zero-cache-do-idle.ts.
37
- */
38
- import { EventEmitter } from 'node:events';
39
- import { createBrowserProxy } from '../pg-proxy-browser.js';
40
- import { DoBackend, releaseDoBackendInstanceCaches } from '../pg-proxy-do-backend.js';
41
- import { deleteReplicationState, resetReplicationState, signalReplicationChange, } from '../replication/handler.js';
42
- import { apiURLForCFInstance, dispatcherFastifyForCFInstance, logCFInstance, postgresURLForCFInstance, registerCFInstanceRuntime, releaseCFInstanceRuntime, setCFInstanceEnv, setCFInstanceProxy, setCFInstanceRuntimeAbandon, setCFInstanceRuntimeStop, sqliteDirectoryForCFInstance, sqlitePathForCFInstance, stopCFInstanceRuntimeForReplacement, } from './cf-instance-runtime.js';
43
- import { DurableObjectWebSocketHandoff, } from './durable-object-websocket-handoff.js';
44
- import { sweepCFInstanceSqliteHandles } from './embed-generation.js';
45
- import { createLocalSqlBackend } from './local-sql-backend.js';
46
- import { cleanupInactiveSnapshotTablesForCFInstance } from './shims/sqlite.js';
47
- import { acquireZeroProcessEnv } from './shims/zero-process-env.js';
48
- // static import so wrangler follows zero-cache's dependency tree and shim aliases.
49
- import { abandonWorkerTree as _abandonWorkerTree, runWorker as _runWorker, } from './zero-cache-run-worker.js';
50
- const runWorkerFn = _runWorker;
51
- const abandonWorkerTreeFn = _abandonWorkerTree;
52
- const WORKER_SHUTDOWN_TIMEOUT_MS = 5_000;
53
- const WORKER_FORCE_SHUTDOWN_TIMEOUT_MS = 5_000;
54
- const STARTUP_CLEANUP_TIMEOUT_MS = 15_000;
55
- const ZERO_STARTUP_STOPPED_ERROR = 'OrezZeroStartupStoppedError';
56
- function releaseGenerationWhenComplete(generation) {
57
- if (generation.abandoned)
58
- return;
59
- if (generation.workerDone && generation.cleanupDone && !generation.cleanupFailed) {
60
- deleteReplicationState(generation.runtime.instanceId);
61
- releaseCFInstanceRuntime(generation.runtime);
62
- }
63
- }
64
- function abortReason(signal) {
65
- return signal.reason ?? new Error('zero-cache CF embed: operation aborted');
66
- }
67
- function isExpectedStartupStop(error) {
68
- return error instanceof Error && error.name === ZERO_STARTUP_STOPPED_ERROR;
69
- }
70
- function waitForAbortable(promise, signal) {
71
- if (signal.aborted)
72
- return Promise.reject(abortReason(signal));
73
- return new Promise((resolve, reject) => {
74
- let settled = false;
75
- const finish = (complete) => {
76
- if (settled)
77
- return;
78
- settled = true;
79
- signal.removeEventListener('abort', onAbort);
80
- complete();
81
- };
82
- const onAbort = () => finish(() => reject(abortReason(signal)));
83
- signal.addEventListener('abort', onAbort, { once: true });
84
- promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
85
- });
86
- }
87
- // tx-journal owner id for every pg session this embed opens. recovery at
88
- // embed boot targets exactly this owner, so it can never roll back another
89
- // client's live transaction on the shared upstream db (e.g. the app worker's
90
- // in-DO pg session).
91
- const EMBED_TX_OWNER = 'orez-embed';
92
- // roll back journaled transactions a dead embed generation left on the
93
- // remote SQL DO (upstream db sessions killed mid-transaction).
94
- // this request is deliberately not abortable: a service-binding abort can
95
- // reject locally while the target DO keeps running. teardown joins this
96
- // promise before releasing the runtime claim, so a replacement cannot open
97
- // transactions under the same owner until recovery has actually responded.
98
- async function recoverRemoteTransactions(url, owner, backendFetch) {
99
- const fetcher = backendFetch ?? fetch;
100
- const resp = await fetcher(url, {
101
- method: 'POST',
102
- headers: { 'Content-Type': 'application/json' },
103
- body: JSON.stringify({ owner }),
104
- });
105
- if (!resp.ok) {
106
- const text = await resp.text().catch(() => '');
107
- throw new Error(`zero-cache CF embed: tx recovery failed: HTTP ${resp.status} ${text.slice(0, 500)}`);
108
- }
109
- }
110
- function addProtocolSessionFactory(backend, createProtocolSession) {
111
- return Object.assign(backend, { createProtocolSession });
112
- }
113
- /**
114
- * start zero-cache in embedded CF Workers mode.
115
- *
116
- * must be called with a DO SQLite handle for zero-cache's replica storage and
117
- * a DoBackend target for upstream/CVR/change Postgres connections.
118
- */
119
- export async function startZeroCacheEmbedCF(opts) {
120
- const startupStartedAt = Date.now();
121
- const appId = opts.appId || 'zero';
122
- const publications = opts.publications?.join(',') || `orez_${appId}_public`;
123
- const readyTimeout = opts.readyTimeout ?? 30000;
124
- const pgUser = opts.pgUser || 'user';
125
- const pgPassword = opts.pgPassword || '';
126
- const backendUrl = opts.backendUrl || 'https://orez-do-backend.local';
127
- const backendNamespace = opts.backendNamespace || appId;
128
- if (!opts.apiFetch && (opts.env?.ZERO_MUTATE_URL || opts.env?.ZERO_QUERY_URL)) {
129
- throw new Error('zero-cache CF embed: apiFetch is required with ZERO_MUTATE_URL or ZERO_QUERY_URL');
130
- }
131
- if (!Number.isFinite(readyTimeout) || readyTimeout <= 0) {
132
- throw new Error('zero-cache CF embed: readyTimeout must be a positive number');
133
- }
134
- const startupDeadlineAt = startupStartedAt + readyTimeout;
135
- const startupAbort = new AbortController();
136
- const startupOperations = new Set();
137
- let startupPhase = 'entry';
138
- const emitStartupEvent = (event) => {
139
- try {
140
- opts.log?.({
141
- component: 'embed',
142
- instanceId: opts.instanceId,
143
- ...event,
144
- });
145
- }
146
- catch {
147
- // diagnostics cannot break startup
148
- }
149
- };
150
- const assertWithinStartupDeadline = (phase) => {
151
- if (startupAbort.signal.aborted)
152
- throw abortReason(startupAbort.signal);
153
- const now = Date.now();
154
- if (now < startupDeadlineAt)
155
- return;
156
- startupPhase = phase;
157
- const error = new Error(`zero-cache CF embed: startup timed out after ${readyTimeout}ms in phase ${phase}`);
158
- emitStartupEvent({
159
- elapsedMs: now - startupStartedAt,
160
- event: 'startup-phase-timeout',
161
- phase,
162
- timeoutMs: readyTimeout,
163
- });
164
- startupAbort.abort(error);
165
- throw error;
166
- };
167
- let startupTimer;
168
- const armStartupTimer = () => {
169
- startupTimer = setTimeout(() => {
170
- try {
171
- assertWithinStartupDeadline(startupPhase);
172
- }
173
- catch {
174
- return;
175
- }
176
- armStartupTimer();
177
- }, Math.max(0, startupDeadlineAt - Date.now()));
178
- };
179
- armStartupTimer();
180
- const trackStartupOperation = (operation) => {
181
- startupOperations.add(operation);
182
- void operation.then(() => startupOperations.delete(operation), () => startupOperations.delete(operation));
183
- return operation;
184
- };
185
- const runStartupPhase = async (phase, run) => {
186
- startupPhase = phase;
187
- assertWithinStartupDeadline(phase);
188
- const phaseStartedAt = Date.now();
189
- emitStartupEvent({
190
- elapsedMs: phaseStartedAt - startupStartedAt,
191
- event: 'startup-phase-start',
192
- phase,
193
- remainingMs: Math.max(0, startupDeadlineAt - phaseStartedAt),
194
- });
195
- try {
196
- const operation = trackStartupOperation(Promise.resolve().then(run));
197
- const result = await waitForAbortable(operation, startupAbort.signal);
198
- assertWithinStartupDeadline(phase);
199
- emitStartupEvent({
200
- elapsedMs: Date.now() - startupStartedAt,
201
- event: 'startup-phase-complete',
202
- phase,
203
- phaseElapsedMs: Date.now() - phaseStartedAt,
204
- remainingMs: Math.max(0, startupDeadlineAt - Date.now()),
205
- });
206
- return result;
207
- }
208
- catch (error) {
209
- emitStartupEvent({
210
- elapsedMs: Date.now() - startupStartedAt,
211
- error: error instanceof Error ? error.message : String(error),
212
- event: 'startup-phase-failed',
213
- phase,
214
- phaseElapsedMs: Date.now() - phaseStartedAt,
215
- });
216
- throw error;
217
- }
218
- };
219
- let registeredRuntime;
220
- try {
221
- await runStartupPhase('replacement-stop', () => stopCFInstanceRuntimeForReplacement(opts.instanceId, opts.doSqlite));
222
- await runStartupPhase('runtime-registration', async () => {
223
- registeredRuntime = registerCFInstanceRuntime({
224
- apiFetch: opts.apiFetch,
225
- doSqlite: opts.doSqlite,
226
- env: opts.env ?? {},
227
- instanceId: opts.instanceId,
228
- log: opts.log,
229
- pgPassword,
230
- pgUser,
231
- });
232
- });
233
- }
234
- catch (error) {
235
- if (registeredRuntime)
236
- releaseCFInstanceRuntime(registeredRuntime);
237
- clearTimeout(startupTimer);
238
- throw error;
239
- }
240
- if (!registeredRuntime) {
241
- clearTimeout(startupTimer);
242
- throw new Error('zero-cache CF embed: runtime registration did not complete');
243
- }
244
- const runtime = registeredRuntime;
245
- const generation = {
246
- abandoned: false,
247
- cleanupDone: false,
248
- cleanupFailed: false,
249
- runtime,
250
- workerDone: true,
251
- };
252
- const instanceId = runtime.instanceId;
253
- const zeroTaskId = `orez-cf-${runtime.encodedId}`;
254
- const globalRecord = globalThis;
255
- let processRecord;
256
- let releaseProcessEnv = null;
257
- const backendRoots = [];
258
- let proxy = null;
259
- let parent = null;
260
- let parentEmitter = null;
261
- let wrappedParent = null;
262
- let isReady = false;
263
- let stopping = false;
264
- let runWorkerPromise = null;
265
- let workerSettledPromise = null;
266
- let workerError;
267
- let workerFailed = false;
268
- let startupFailure;
269
- let shutdownPromise = null;
270
- let fastifyInstance = null;
271
- let debugEmbed = false;
272
- const webSocketHandoff = new DurableObjectWebSocketHandoff(() => fastifyInstance);
273
- const releaseProcessEnvWhenWorkerDone = () => {
274
- if (!generation.workerDone || !releaseProcessEnv)
275
- return;
276
- releaseProcessEnv();
277
- releaseProcessEnv = null;
278
- };
279
- const shutdown = () => {
280
- if (generation.abandoned)
281
- return Promise.resolve();
282
- if (shutdownPromise)
283
- return shutdownPromise;
284
- stopping = true;
285
- isReady = false;
286
- shutdownPromise = (async () => {
287
- const cleanupErrors = [];
288
- let resourceCleanupFailed = false;
289
- if (wrappedParent && runWorkerPromise && !generation.workerDone) {
290
- try {
291
- ;
292
- wrappedParent.kill('SIGTERM');
293
- }
294
- catch (err) {
295
- cleanupErrors.push(err);
296
- }
297
- }
298
- if (workerSettledPromise && !generation.workerDone) {
299
- let timeout;
300
- let workerStopped = await Promise.race([
301
- workerSettledPromise.then(() => true),
302
- new Promise((resolve) => {
303
- timeout = setTimeout(() => resolve(false), WORKER_SHUTDOWN_TIMEOUT_MS);
304
- }),
305
- ]);
306
- if (timeout)
307
- clearTimeout(timeout);
308
- if (!workerStopped) {
309
- logCFInstance(runtime, {
310
- component: 'embed',
311
- event: 'worker-force-stop',
312
- timeoutMs: WORKER_SHUTDOWN_TIMEOUT_MS,
313
- });
314
- try {
315
- ;
316
- wrappedParent.kill('SIGQUIT');
317
- }
318
- catch (err) {
319
- cleanupErrors.push(err);
320
- }
321
- timeout = undefined;
322
- workerStopped = await Promise.race([
323
- workerSettledPromise.then(() => true),
324
- new Promise((resolve) => {
325
- timeout = setTimeout(() => resolve(false), WORKER_FORCE_SHUTDOWN_TIMEOUT_MS);
326
- }),
327
- ]);
328
- if (timeout)
329
- clearTimeout(timeout);
330
- if (!workerStopped) {
331
- cleanupErrors.push(new Error(`zero-cache CF embed: worker did not terminate after SIGTERM (${WORKER_SHUTDOWN_TIMEOUT_MS}ms) and SIGQUIT (${WORKER_FORCE_SHUTDOWN_TIMEOUT_MS}ms)`));
332
- }
333
- }
334
- }
335
- if (generation.abandoned)
336
- return;
337
- if (workerFailed &&
338
- workerError !== startupFailure &&
339
- !isExpectedStartupStop(workerError)) {
340
- cleanupErrors.push(workerError);
341
- }
342
- await Promise.allSettled([...startupOperations]);
343
- if (proxy) {
344
- try {
345
- await proxy.close();
346
- }
347
- catch (err) {
348
- resourceCleanupFailed = true;
349
- cleanupErrors.push(err);
350
- }
351
- }
352
- const backendResults = await Promise.allSettled(backendRoots.map((backend) => Promise.resolve().then(() => backend.close())));
353
- for (const result of backendResults) {
354
- if (result.status === 'rejected') {
355
- resourceCleanupFailed = true;
356
- cleanupErrors.push(result.reason);
357
- }
358
- }
359
- releaseDoBackendInstanceCaches(instanceId);
360
- try {
361
- webSocketHandoff.closeAll();
362
- }
363
- catch (err) {
364
- resourceCleanupFailed = true;
365
- cleanupErrors.push(err);
366
- }
367
- try {
368
- sweepCFInstanceSqliteHandles(runtime);
369
- }
370
- catch (err) {
371
- resourceCleanupFailed = true;
372
- cleanupErrors.push(err);
373
- }
374
- releaseProcessEnvWhenWorkerDone();
375
- parentEmitter?.removeAllListeners();
376
- if (generation.workerDone)
377
- parent?.removeAllListeners();
378
- generation.cleanupDone = true;
379
- generation.cleanupFailed = resourceCleanupFailed;
380
- releaseGenerationWhenComplete(generation);
381
- if (cleanupErrors.length > 0) {
382
- throw new AggregateError(cleanupErrors, 'zero-cache CF embed: teardown failed');
383
- }
384
- })();
385
- return shutdownPromise;
386
- };
387
- const handleUnexpectedWorkerExit = () => {
388
- if (!isReady || stopping)
389
- return;
390
- isReady = false;
391
- if (!workerFailed) {
392
- workerFailed = true;
393
- workerError = new Error('zero-cache CF embed: runWorker exited after becoming ready');
394
- }
395
- void shutdown().catch((error) => {
396
- logCFInstance(runtime, {
397
- component: 'embed',
398
- error,
399
- event: 'unexpected-worker-exit-cleanup-failed',
400
- });
401
- console.error(`[orez-zero-cache-cf:${runtime.instanceId}] unexpected worker exit cleanup failed`, error);
402
- });
403
- };
404
- setCFInstanceRuntimeStop(runtime, shutdown);
405
- setCFInstanceRuntimeAbandon(runtime, () => {
406
- generation.abandoned = true;
407
- generation.cleanupDone = true;
408
- stopping = true;
409
- isReady = false;
410
- abandonWorkerTreeFn(zeroTaskId);
411
- if (releaseProcessEnv) {
412
- releaseProcessEnv();
413
- releaseProcessEnv = null;
414
- }
415
- deleteReplicationState(instanceId);
416
- releaseDoBackendInstanceCaches(instanceId);
417
- runtime.sqliteHandles.clear();
418
- parentEmitter?.removeAllListeners();
419
- parent?.removeAllListeners();
420
- });
421
- try {
422
- releaseProcessEnv = acquireZeroProcessEnv();
423
- processRecord = globalRecord.process;
424
- await runStartupPhase('stale-snapshot-cleanup', () => cleanupInactiveSnapshotTablesForCFInstance(runtime));
425
- const localSql = createLocalSqlBackend(opts.doSqlite);
426
- const txOwner = `${EMBED_TX_OWNER}:${runtime.encodedId}`;
427
- const instantiateBackend = (dbName) => new DoBackend(backendUrl, dbName, backendNamespace, {
428
- allowTransactionalDDL: true,
429
- fetch: dbName === 'postgres' ? opts.backendFetch : localSql.fetch,
430
- instanceId,
431
- log: opts.log,
432
- signal: startupAbort.signal,
433
- signalReplication: () => signalReplicationChange(instanceId),
434
- txOwner,
435
- });
436
- const createRootBackend = (dbName) => {
437
- const backend = instantiateBackend(dbName);
438
- backendRoots.push(backend);
439
- return backend;
440
- };
441
- localSql.recoverOrphanedTransactions();
442
- await runStartupPhase('remote-transaction-recovery', () => recoverRemoteTransactions(`${backendUrl.replace(/\/+$/, '')}/recover-txs?db=postgres&ns=${encodeURIComponent(backendNamespace)}`, txOwner, opts.backendFetch));
443
- const backends = {
444
- postgres: createRootBackend('postgres'),
445
- cvr: createRootBackend('zero_cvr'),
446
- cdb: createRootBackend('zero_cdb'),
447
- };
448
- const proxyBackends = {
449
- postgres: addProtocolSessionFactory(backends.postgres, () => instantiateBackend('postgres')),
450
- cvr: addProtocolSessionFactory(backends.cvr, () => instantiateBackend('zero_cvr')),
451
- cdb: addProtocolSessionFactory(backends.cdb, () => instantiateBackend('zero_cdb')),
452
- };
453
- const backendReadyResults = await runStartupPhase('backend-initialization', () => Promise.allSettled(Object.entries(backends).map(async ([database, backend]) => {
454
- const startedAt = Date.now();
455
- emitStartupEvent({
456
- database,
457
- elapsedMs: startedAt - startupStartedAt,
458
- event: 'startup-backend-start',
459
- phase: 'backend-initialization',
460
- });
461
- try {
462
- await backend.waitReady;
463
- assertWithinStartupDeadline('backend-initialization');
464
- emitStartupEvent({
465
- database,
466
- elapsedMs: Date.now() - startupStartedAt,
467
- event: 'startup-backend-complete',
468
- phase: 'backend-initialization',
469
- phaseElapsedMs: Date.now() - startedAt,
470
- });
471
- }
472
- catch (error) {
473
- emitStartupEvent({
474
- database,
475
- elapsedMs: Date.now() - startupStartedAt,
476
- error: error instanceof Error ? error.message : String(error),
477
- event: 'startup-backend-failed',
478
- phase: 'backend-initialization',
479
- phaseElapsedMs: Date.now() - startedAt,
480
- });
481
- throw error;
482
- }
483
- })));
484
- const backendReadyErrors = backendReadyResults.flatMap((result) => result.status === 'rejected' ? [result.reason] : []);
485
- if (backendReadyErrors.length === 1)
486
- throw backendReadyErrors[0];
487
- if (backendReadyErrors.length > 1) {
488
- throw new AggregateError(backendReadyErrors, 'zero-cache CF embed: backend setup failed');
489
- }
490
- proxy = await runStartupPhase('proxy-creation', () => createBrowserProxy({
491
- postgres: proxyBackends.postgres,
492
- cvr: proxyBackends.cvr,
493
- cdb: proxyBackends.cdb,
494
- postgresReplicas: [],
495
- }, {
496
- debugWire: opts.env?.OREZ_DEBUG_WIRE === '1',
497
- instanceId,
498
- pgUser,
499
- pgPassword,
500
- singleDb: false,
501
- logLevel: opts.env?.ZERO_LOG_LEVEL || 'info',
502
- }));
503
- setCFInstanceProxy(runtime, (port) => proxy?.handleConnection(port));
504
- const createdParent = new EventEmitter();
505
- parent = createdParent;
506
- parentEmitter = new EventEmitter();
507
- createdParent.send = (message, sendHandle) => {
508
- parentEmitter?.emit('message', message, sendHandle);
509
- return true;
510
- };
511
- createdParent.kill = (signal = 'SIGTERM') => {
512
- createdParent.emit(signal, signal);
513
- };
514
- createdParent.pid = processRecord.pid ?? 1;
515
- const env = {
516
- SINGLE_PROCESS: '1',
517
- NODE_ENV: 'development',
518
- ZERO_APP_ID: appId,
519
- ZERO_APP_PUBLICATIONS: publications,
520
- ZERO_ADMIN_PASSWORD: opts.env?.ZERO_ADMIN_PASSWORD || crypto.randomUUID(),
521
- ZERO_NUM_SYNC_WORKERS: opts.env?.ZERO_NUM_SYNC_WORKERS || '1',
522
- ZERO_ENABLE_QUERY_PLANNER: 'false',
523
- ZERO_UPSTREAM_MAX_CONNS: opts.env?.ZERO_UPSTREAM_MAX_CONNS || '2',
524
- ZERO_CVR_MAX_CONNS: opts.env?.ZERO_CVR_MAX_CONNS || '2',
525
- ZERO_CHANGE_MAX_CONNS: opts.env?.ZERO_CHANGE_MAX_CONNS || '2',
526
- ZERO_LOG_LEVEL: opts.env?.ZERO_LOG_LEVEL || 'warn',
527
- ...opts.env,
528
- ZERO_UPSTREAM_DB: postgresURLForCFInstance(instanceId, 'postgres', pgUser),
529
- ZERO_CVR_DB: postgresURLForCFInstance(instanceId, 'zero_cvr', pgUser),
530
- ZERO_CHANGE_DB: postgresURLForCFInstance(instanceId, 'zero_cdb', pgUser),
531
- ZERO_REPLICA_FILE: sqlitePathForCFInstance(instanceId),
532
- ZERO_STORAGE_DB_TMP_DIR: sqliteDirectoryForCFInstance(instanceId),
533
- ZERO_PORT: String(runtime.basePort),
534
- ZERO_TASK_ID: zeroTaskId,
535
- ZERO_SHADOW_SYNC_ENABLED: 'false',
536
- };
537
- if (opts.apiFetch) {
538
- for (const key of ['ZERO_MUTATE_URL', 'ZERO_QUERY_URL']) {
539
- if (env[key])
540
- env[key] = apiURLForCFInstance(instanceId, env[key]);
541
- }
542
- }
543
- setCFInstanceEnv(runtime, env);
544
- resetReplicationState(instanceId, env, opts.log);
545
- debugEmbed = env.OREZ_DEBUG_EMBED === '1';
546
- wrappedParent = new Proxy(createdParent, {
547
- get(target, prop, receiver) {
548
- if (prop === 'onMessageType') {
549
- return (type, handler) => {
550
- target.on('message', (data, sendHandle) => {
551
- if (Array.isArray(data) && data.length === 2 && data[0] === type) {
552
- handler(data[1], sendHandle);
553
- }
554
- });
555
- return receiver;
556
- };
557
- }
558
- if (prop === 'onceMessageType') {
559
- return (type, handler) => {
560
- const listener = (data, sendHandle) => {
561
- if (Array.isArray(data) && data.length === 2 && data[0] === type) {
562
- target.off('message', listener);
563
- handler(data[1], sendHandle);
564
- }
565
- };
566
- target.on('message', listener);
567
- return receiver;
568
- };
569
- }
570
- const value = Reflect.get(target, prop, target);
571
- if (typeof value !== 'function')
572
- return value;
573
- return (...args) => {
574
- const result = value.apply(target, args);
575
- return result === target ? receiver : result;
576
- };
577
- },
578
- });
579
- const readyPromise = new Promise((resolve) => {
580
- parentEmitter?.on('message', (msg) => {
581
- if (debugEmbed) {
582
- logCFInstance(runtime, {
583
- component: 'embed',
584
- event: 'parent-message',
585
- message: msg,
586
- });
587
- }
588
- if (!stopping && Array.isArray(msg) && msg[0] === 'ready') {
589
- isReady = true;
590
- resolve();
591
- }
592
- });
593
- });
594
- generation.workerDone = false;
595
- runWorkerPromise = Promise.resolve().then(() => runWorkerFn(wrappedParent, env));
596
- void runWorkerPromise.catch((err) => {
597
- if (debugEmbed) {
598
- logCFInstance(runtime, {
599
- component: 'embed',
600
- error: err,
601
- event: 'worker-error',
602
- });
603
- }
604
- });
605
- workerSettledPromise = runWorkerPromise.then(() => {
606
- generation.workerDone = true;
607
- releaseProcessEnvWhenWorkerDone();
608
- handleUnexpectedWorkerExit();
609
- if (generation.cleanupDone)
610
- parent?.removeAllListeners();
611
- releaseGenerationWhenComplete(generation);
612
- }, (error) => {
613
- workerError = error;
614
- workerFailed = true;
615
- generation.workerDone = true;
616
- releaseProcessEnvWhenWorkerDone();
617
- handleUnexpectedWorkerExit();
618
- if (generation.cleanupDone)
619
- parent?.removeAllListeners();
620
- releaseGenerationWhenComplete(generation);
621
- });
622
- const workerStartupPromise = runWorkerPromise.then(() => {
623
- if (!isReady) {
624
- throw new Error('zero-cache CF embed: runWorker exited before ready');
625
- }
626
- });
627
- await runStartupPhase('worker-readiness', () => Promise.race([readyPromise, workerStartupPromise]));
628
- fastifyInstance = dispatcherFastifyForCFInstance(instanceId);
629
- assertWithinStartupDeadline('startup-complete');
630
- }
631
- catch (startupError) {
632
- clearTimeout(startupTimer);
633
- if (!startupAbort.signal.aborted)
634
- startupAbort.abort(startupError);
635
- startupFailure = startupError;
636
- const cleanupAbort = new AbortController();
637
- const cleanupStartedAt = Date.now();
638
- emitStartupEvent({
639
- elapsedMs: cleanupStartedAt - startupStartedAt,
640
- event: 'startup-cleanup-start',
641
- phase: startupPhase,
642
- timeoutMs: STARTUP_CLEANUP_TIMEOUT_MS,
643
- });
644
- const cleanupTimer = setTimeout(() => {
645
- const error = new Error(`zero-cache CF embed: startup cleanup timed out after ${STARTUP_CLEANUP_TIMEOUT_MS}ms`);
646
- emitStartupEvent({
647
- elapsedMs: Date.now() - startupStartedAt,
648
- event: 'startup-cleanup-timeout',
649
- phase: startupPhase,
650
- timeoutMs: STARTUP_CLEANUP_TIMEOUT_MS,
651
- });
652
- cleanupAbort.abort(error);
653
- }, STARTUP_CLEANUP_TIMEOUT_MS);
654
- try {
655
- await waitForAbortable(shutdown(), cleanupAbort.signal);
656
- emitStartupEvent({
657
- elapsedMs: Date.now() - startupStartedAt,
658
- event: 'startup-cleanup-complete',
659
- phase: startupPhase,
660
- phaseElapsedMs: Date.now() - cleanupStartedAt,
661
- });
662
- }
663
- catch (cleanupError) {
664
- clearTimeout(cleanupTimer);
665
- throw new AggregateError([startupError, cleanupError], 'zero-cache CF embed: startup failed and teardown also failed', { cause: startupError });
666
- }
667
- clearTimeout(cleanupTimer);
668
- throw startupError;
669
- }
670
- clearTimeout(startupTimer);
671
- return {
672
- get ready() {
673
- return isReady;
674
- },
675
- get connectionCount() {
676
- return webSocketHandoff.activeConnections;
677
- },
678
- async handleRequest(request, ctx) {
679
- if (!isReady) {
680
- return new Response('zero-cache not ready', { status: 503 });
681
- }
682
- const url = new URL(request.url);
683
- const isUpgrade = request.headers.get('upgrade')?.toLowerCase() === 'websocket' ||
684
- request.headers.get('x-soot-ws-upgrade') === 'true';
685
- if (isUpgrade) {
686
- return handleWebSocketUpgrade(request, url, webSocketHandoff, ctx);
687
- }
688
- return handleHttpRequest(request, url, fastifyInstance);
689
- },
690
- stop: shutdown,
691
- };
692
- }
693
- // -- HTTP request handling --
694
- // routes through the Fastify shim's inject() method
695
- async function handleHttpRequest(request, url, fastify) {
696
- if (!fastify?.inject) {
697
- return new Response('fastify not available', { status: 503 });
698
- }
699
- const headers = {};
700
- request.headers.forEach((value, key) => {
701
- headers[key] = value;
702
- });
703
- let payload = null;
704
- if (request.method !== 'GET' && request.method !== 'HEAD' && request.body) {
705
- payload = await request.text();
706
- }
707
- const result = await fastify.inject({
708
- method: request.method,
709
- url: url.pathname + url.search,
710
- headers,
711
- payload,
712
- });
713
- return new Response(result.body, {
714
- status: result.statusCode,
715
- headers: result.headers,
716
- });
717
- }
718
- // -- WebSocket upgrade handling --
719
- // creates WebSocketPair, accepts the server socket with the standard CF
720
- // WebSocket API, and feeds zero-cache a process-lived proxy socket.
721
- function handleWebSocketUpgrade(request, url, webSocketHandoff, ctx) {
722
- const WsPair = globalThis.WebSocketPair;
723
- if (!WsPair) {
724
- return new Response('WebSocketPair not available', { status: 500 });
725
- }
726
- const pair = new WsPair();
727
- const [client, server] = Object.values(pair);
728
- // build a serializable request object for the handoff
729
- const headers = {};
730
- request.headers.forEach((value, key) => {
731
- headers[key] = value;
732
- });
733
- const message = {
734
- url: url.pathname + url.search,
735
- headers,
736
- method: 'GET',
737
- };
738
- if (!webSocketHandoff.accept(server, message, ctx)) {
739
- server.close(1011, 'zero-cache websocket route unavailable');
740
- return new Response('zero-cache websocket route unavailable', { status: 404 });
741
- }
742
- // return 101 with client socket
743
- // must echo Sec-WebSocket-Protocol — browsers reject the upgrade without it
744
- const secProtocol = request.headers.get('sec-websocket-protocol');
745
- const upgradeHeaders = {};
746
- if (secProtocol) {
747
- upgradeHeaders['Sec-WebSocket-Protocol'] = secProtocol;
748
- }
749
- try {
750
- return new Response(null, {
751
- status: 101,
752
- headers: upgradeHeaders,
753
- // @ts-expect-error CF Workers Response extension
754
- webSocket: client,
755
- });
756
- }
757
- catch {
758
- const resp = new Response(null, { status: 200 });
759
- resp.__orez_websocket = client;
760
- resp.__orez_ws_upgrade = true;
761
- return resp;
762
- }
763
- }
764
- //# sourceMappingURL=zero-cache-embed-cf.js.map