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.
- package/README.md +27 -19
- package/dist/config.d.ts +0 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -1
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -43
- package/dist/index.js.map +1 -1
- package/package.json +7 -71
- package/dist/cf-deploy/bundle.d.ts +0 -21
- package/dist/cf-deploy/bundle.d.ts.map +0 -1
- package/dist/cf-deploy/bundle.js +0 -347
- package/dist/cf-deploy/bundle.js.map +0 -1
- package/dist/cf-deploy/config.d.ts +0 -52
- package/dist/cf-deploy/config.d.ts.map +0 -1
- package/dist/cf-deploy/config.js +0 -23
- package/dist/cf-deploy/config.js.map +0 -1
- package/dist/cf-deploy/index.d.ts +0 -11
- package/dist/cf-deploy/index.d.ts.map +0 -1
- package/dist/cf-deploy/index.js +0 -17
- package/dist/cf-deploy/index.js.map +0 -1
- package/dist/cf-deploy/leaves.d.ts +0 -19
- package/dist/cf-deploy/leaves.d.ts.map +0 -1
- package/dist/cf-deploy/leaves.js +0 -109
- package/dist/cf-deploy/leaves.js.map +0 -1
- package/dist/cf-deploy/migration.d.ts +0 -40
- package/dist/cf-deploy/migration.d.ts.map +0 -1
- package/dist/cf-deploy/migration.js +0 -891
- package/dist/cf-deploy/migration.js.map +0 -1
- package/dist/cf-deploy/nativeMigrations.d.ts +0 -24
- package/dist/cf-deploy/nativeMigrations.d.ts.map +0 -1
- package/dist/cf-deploy/nativeMigrations.js +0 -185
- package/dist/cf-deploy/nativeMigrations.js.map +0 -1
- package/dist/cf-deploy/prune.d.ts +0 -14
- package/dist/cf-deploy/prune.d.ts.map +0 -1
- package/dist/cf-deploy/prune.js +0 -235
- package/dist/cf-deploy/prune.js.map +0 -1
- package/dist/cf-deploy/shims.d.ts +0 -13
- package/dist/cf-deploy/shims.d.ts.map +0 -1
- package/dist/cf-deploy/shims.js +0 -737
- package/dist/cf-deploy/shims.js.map +0 -1
- package/dist/cf-deploy/smoke.d.ts +0 -15
- package/dist/cf-deploy/smoke.d.ts.map +0 -1
- package/dist/cf-deploy/smoke.js +0 -141
- package/dist/cf-deploy/smoke.js.map +0 -1
- package/dist/cf-deploy/sources.d.ts +0 -9
- package/dist/cf-deploy/sources.d.ts.map +0 -1
- package/dist/cf-deploy/sources.js +0 -276
- package/dist/cf-deploy/sources.js.map +0 -1
- package/dist/cf-deploy/wrangler.d.ts +0 -6
- package/dist/cf-deploy/wrangler.d.ts.map +0 -1
- package/dist/cf-deploy/wrangler.js +0 -168
- package/dist/cf-deploy/wrangler.js.map +0 -1
- package/dist/cf-do/application-sql.d.ts +0 -49
- package/dist/cf-do/application-sql.d.ts.map +0 -1
- package/dist/cf-do/application-sql.js +0 -124
- package/dist/cf-do/application-sql.js.map +0 -1
- package/dist/cf-do/cdc.d.ts +0 -155
- package/dist/cf-do/cdc.d.ts.map +0 -1
- package/dist/cf-do/cdc.js +0 -719
- package/dist/cf-do/cdc.js.map +0 -1
- package/dist/cf-do/cursor-pull.d.ts +0 -27
- package/dist/cf-do/cursor-pull.d.ts.map +0 -1
- package/dist/cf-do/cursor-pull.js +0 -78
- package/dist/cf-do/cursor-pull.js.map +0 -1
- package/dist/cf-do/row-undo.d.ts +0 -36
- package/dist/cf-do/row-undo.d.ts.map +0 -1
- package/dist/cf-do/row-undo.js +0 -221
- package/dist/cf-do/row-undo.js.map +0 -1
- package/dist/cf-do/tx-journal.d.ts +0 -119
- package/dist/cf-do/tx-journal.d.ts.map +0 -1
- package/dist/cf-do/tx-journal.js +0 -718
- package/dist/cf-do/tx-journal.js.map +0 -1
- package/dist/cf-do/watermark.d.ts +0 -29
- package/dist/cf-do/watermark.d.ts.map +0 -1
- package/dist/cf-do/watermark.js +0 -105
- package/dist/cf-do/watermark.js.map +0 -1
- package/dist/cf-do/worker.d.ts +0 -287
- package/dist/cf-do/worker.d.ts.map +0 -1
- package/dist/cf-do/worker.js +0 -1961
- package/dist/cf-do/worker.js.map +0 -1
- package/dist/do-sql-tracking.d.ts +0 -104
- package/dist/do-sql-tracking.d.ts.map +0 -1
- package/dist/do-sql-tracking.js +0 -255
- package/dist/do-sql-tracking.js.map +0 -1
- package/dist/pg-proxy-do-backend.d.ts +0 -211
- package/dist/pg-proxy-do-backend.d.ts.map +0 -1
- package/dist/pg-proxy-do-backend.js +0 -7884
- package/dist/pg-proxy-do-backend.js.map +0 -1
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts +0 -67
- package/dist/pg-sqlite-compiler/catalog/seed.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/catalog/seed.js +0 -436
- package/dist/pg-sqlite-compiler/catalog/seed.js.map +0 -1
- package/dist/pg-sqlite-compiler/compiler.d.ts +0 -17
- package/dist/pg-sqlite-compiler/compiler.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/compiler.js +0 -86
- package/dist/pg-sqlite-compiler/compiler.js.map +0 -1
- package/dist/pg-sqlite-compiler/index.d.ts +0 -2
- package/dist/pg-sqlite-compiler/index.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/index.js +0 -13
- package/dist/pg-sqlite-compiler/index.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/array.d.ts +0 -3
- package/dist/pg-sqlite-compiler/passes/array.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/array.js +0 -223
- package/dist/pg-sqlite-compiler/passes/array.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts +0 -48
- package/dist/pg-sqlite-compiler/passes/ast-utils.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/ast-utils.js +0 -93
- package/dist/pg-sqlite-compiler/passes/ast-utils.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts +0 -34
- package/dist/pg-sqlite-compiler/passes/catalog.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/catalog.js +0 -30
- package/dist/pg-sqlite-compiler/passes/catalog.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts +0 -23
- package/dist/pg-sqlite-compiler/passes/datetime.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/datetime.js +0 -55
- package/dist/pg-sqlite-compiler/passes/datetime.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/dml-cte.d.ts +0 -22
- package/dist/pg-sqlite-compiler/passes/dml-cte.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/dml-cte.js +0 -161
- package/dist/pg-sqlite-compiler/passes/dml-cte.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/index.d.ts +0 -24
- package/dist/pg-sqlite-compiler/passes/index.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/index.js +0 -52
- package/dist/pg-sqlite-compiler/passes/index.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/json-functions.d.ts +0 -3
- package/dist/pg-sqlite-compiler/passes/json-functions.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/json-functions.js +0 -57
- package/dist/pg-sqlite-compiler/passes/json-functions.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/row-json.d.ts +0 -3
- package/dist/pg-sqlite-compiler/passes/row-json.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/row-json.js +0 -230
- package/dist/pg-sqlite-compiler/passes/row-json.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/schema.d.ts +0 -4
- package/dist/pg-sqlite-compiler/passes/schema.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/schema.js +0 -128
- package/dist/pg-sqlite-compiler/passes/schema.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/string-functions.d.ts +0 -11
- package/dist/pg-sqlite-compiler/passes/string-functions.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/string-functions.js +0 -37
- package/dist/pg-sqlite-compiler/passes/string-functions.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/types.d.ts +0 -41
- package/dist/pg-sqlite-compiler/passes/types.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/types.js +0 -115
- package/dist/pg-sqlite-compiler/passes/types.js.map +0 -1
- package/dist/pg-sqlite-compiler/passes/unsupported.d.ts +0 -3
- package/dist/pg-sqlite-compiler/passes/unsupported.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/passes/unsupported.js +0 -57
- package/dist/pg-sqlite-compiler/passes/unsupported.js.map +0 -1
- package/dist/pg-sqlite-compiler/test/oracle.d.ts +0 -34
- package/dist/pg-sqlite-compiler/test/oracle.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/test/oracle.js +0 -204
- package/dist/pg-sqlite-compiler/test/oracle.js.map +0 -1
- package/dist/pg-sqlite-compiler/types.d.ts +0 -66
- package/dist/pg-sqlite-compiler/types.d.ts.map +0 -1
- package/dist/pg-sqlite-compiler/types.js +0 -2
- package/dist/pg-sqlite-compiler/types.js.map +0 -1
- package/dist/sync-browser-host/generated/sqlite3-browser.wasm +0 -0
- package/dist/sync-browser-host/generated/sync_wasm.js +0 -922
- package/dist/sync-browser-host/generated/sync_wasm_bg.wasm +0 -0
- package/dist/sync-browser-host/host.d.ts +0 -10
- package/dist/sync-browser-host/host.d.ts.map +0 -1
- package/dist/sync-browser-host/host.js +0 -496
- package/dist/sync-browser-host/host.js.map +0 -1
- package/dist/sync-browser-host/idb-snapshot.d.ts +0 -11
- package/dist/sync-browser-host/idb-snapshot.d.ts.map +0 -1
- package/dist/sync-browser-host/idb-snapshot.js +0 -122
- package/dist/sync-browser-host/idb-snapshot.js.map +0 -1
- package/dist/sync-browser-host/index.d.ts +0 -5
- package/dist/sync-browser-host/index.d.ts.map +0 -1
- package/dist/sync-browser-host/index.js +0 -5
- package/dist/sync-browser-host/index.js.map +0 -1
- package/dist/sync-browser-host/message-port.d.ts +0 -7
- package/dist/sync-browser-host/message-port.d.ts.map +0 -1
- package/dist/sync-browser-host/message-port.js +0 -188
- package/dist/sync-browser-host/message-port.js.map +0 -1
- package/dist/sync-browser-host/sqlite-adapter.d.ts +0 -67
- package/dist/sync-browser-host/sqlite-adapter.d.ts.map +0 -1
- package/dist/sync-browser-host/sqlite-adapter.js +0 -145
- package/dist/sync-browser-host/sqlite-adapter.js.map +0 -1
- package/dist/sync-browser-host/types.d.ts +0 -52
- package/dist/sync-browser-host/types.d.ts.map +0 -1
- package/dist/sync-browser-host/types.js +0 -2
- package/dist/sync-browser-host/types.js.map +0 -1
- package/dist/sync-cf-host.d.ts +0 -2
- package/dist/sync-cf-host.d.ts.map +0 -1
- package/dist/sync-cf-host.js +0 -2
- package/dist/sync-cf-host.js.map +0 -1
- package/dist/sync-executor.d.ts +0 -2
- package/dist/sync-executor.d.ts.map +0 -1
- package/dist/sync-executor.js +0 -2
- package/dist/sync-executor.js.map +0 -1
- package/dist/wake.d.ts +0 -22
- package/dist/wake.d.ts.map +0 -1
- package/dist/wake.js +0 -114
- package/dist/wake.js.map +0 -1
- package/dist/worker/cf-do-shim.d.ts +0 -55
- package/dist/worker/cf-do-shim.d.ts.map +0 -1
- package/dist/worker/cf-do-shim.js +0 -58
- package/dist/worker/cf-do-shim.js.map +0 -1
- package/dist/worker/cf-patches.d.ts +0 -43
- package/dist/worker/cf-patches.d.ts.map +0 -1
- package/dist/worker/cf-patches.js +0 -840
- package/dist/worker/cf-patches.js.map +0 -1
- package/dist/worker/local-sql-backend.d.ts +0 -35
- package/dist/worker/local-sql-backend.d.ts.map +0 -1
- package/dist/worker/local-sql-backend.js +0 -217
- package/dist/worker/local-sql-backend.js.map +0 -1
- package/dist/worker/zero-cache-embed-cf.d.ts +0 -92
- package/dist/worker/zero-cache-embed-cf.d.ts.map +0 -1
- package/dist/worker/zero-cache-embed-cf.js +0 -764
- package/dist/worker/zero-cache-embed-cf.js.map +0 -1
- package/dist/worker/zero-sql-write-circuit.d.ts +0 -46
- package/dist/worker/zero-sql-write-circuit.d.ts.map +0 -1
- package/dist/worker/zero-sql-write-circuit.js +0 -161
- package/dist/worker/zero-sql-write-circuit.js.map +0 -1
- package/dist/zero-http/encrypted-column-codec.d.ts +0 -46
- package/dist/zero-http/encrypted-column-codec.d.ts.map +0 -1
- package/dist/zero-http/encrypted-column-codec.js +0 -676
- package/dist/zero-http/encrypted-column-codec.js.map +0 -1
- package/dist/zero-http/encrypted-column-conformance.d.ts +0 -7
- package/dist/zero-http/encrypted-column-conformance.d.ts.map +0 -1
- package/dist/zero-http/encrypted-column-conformance.js +0 -161
- package/dist/zero-http/encrypted-column-conformance.js.map +0 -1
- package/dist/zero-http/fixture-schema.d.ts +0 -127
- package/dist/zero-http/fixture-schema.d.ts.map +0 -1
- package/dist/zero-http/fixture-schema.js +0 -56
- package/dist/zero-http/fixture-schema.js.map +0 -1
- package/dist/zero-http/mount.d.ts +0 -71
- package/dist/zero-http/mount.d.ts.map +0 -1
- package/dist/zero-http/mount.js +0 -594
- package/dist/zero-http/mount.js.map +0 -1
- package/dist/zero-http/payload-codec.d.ts +0 -91
- package/dist/zero-http/payload-codec.d.ts.map +0 -1
- package/dist/zero-http/payload-codec.js +0 -10
- package/dist/zero-http/payload-codec.js.map +0 -1
- package/dist/zero-http/server.d.ts +0 -16
- package/dist/zero-http/server.d.ts.map +0 -1
- package/dist/zero-http/server.js +0 -270
- package/dist/zero-http/server.js.map +0 -1
- package/dist/zero-http/test-harness.d.ts +0 -32
- package/dist/zero-http/test-harness.d.ts.map +0 -1
- package/dist/zero-http/test-harness.js +0 -77
- package/dist/zero-http/test-harness.js.map +0 -1
- package/dist/zero-http/transport.d.ts +0 -71
- package/dist/zero-http/transport.d.ts.map +0 -1
- package/dist/zero-http/transport.js +0 -1106
- 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
|