orez-sync-cf-host 0.11.7 → 0.12.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 +21 -13
- package/dist/config.d.ts +1 -2
- package/dist/config.js +3 -6
- package/dist/generated/package.json +1 -1
- package/dist/generated/sync_wasm.d.ts +1 -7
- package/dist/generated/sync_wasm.js +3 -26
- package/dist/generated/sync_wasm_bg.js +3 -26
- package/dist/generated/sync_wasm_bg.wasm +0 -0
- package/dist/host.js +22 -152
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/query-patch.d.ts +17 -26
- package/dist/query-patch.js +47 -35
- package/dist/types.d.ts +10 -33
- package/dist/types.js +1 -1
- package/dist/wasm.d.ts +0 -1
- package/dist/wasm.js +0 -1
- package/package.json +6 -8
- package/dist/visibility.d.ts +0 -12
- package/dist/visibility.js +0 -30
package/README.md
CHANGED
|
@@ -5,19 +5,27 @@ engine. The package exposes a consumer integration surface (`createSyncWorker`,
|
|
|
5
5
|
`createSyncDurableObject`) and a harness deployment built
|
|
6
6
|
from `src/harness-worker.ts`. Consumers provide a JSON Zero schema, application
|
|
7
7
|
DDL/seed initializer, normalized-claims authenticator, an
|
|
8
|
-
`orez-sync-executor` mutator registry, and
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
`orez-sync-executor` mutator registry or delegated push route, and the app's
|
|
9
|
+
ordinary Zero `defineQueries` registry.
|
|
10
|
+
|
|
11
|
+
The Worker authenticates and authorizes at the consumer edge, then forwards
|
|
12
|
+
normalized claims inside the binding request body, where request-header
|
|
13
|
+
observability cannot record them. It never logs tokens, mutation arguments, or
|
|
14
|
+
row contents. Pull runs `engine_handle_query_pull` inside `transactionSync`;
|
|
15
|
+
local push delegates validation, replay, LMID advancement, CRUD, and committed
|
|
16
|
+
effects to `orez-sync-executor` inside `ctx.storage.transaction`. Every SQL
|
|
17
|
+
cursor is materialized before an await. Mutators may await only their
|
|
18
|
+
application transaction operations; timers, fetches, and other external work
|
|
19
|
+
belong in
|
|
18
20
|
`ctx.defer`, which runs only after commit. Application failures use the
|
|
19
21
|
required second transaction to advance the LMID marker.
|
|
20
22
|
|
|
23
|
+
Every pull is query-driven. Set `config.queries` to the same registry used by
|
|
24
|
+
the client. The host resolves desired named queries in-process and passes
|
|
25
|
+
authenticated claims as their context, so permission scoping remains inside the
|
|
26
|
+
query definitions. Membership-driven puts and dels keep pull work proportional
|
|
27
|
+
to query results without projecting the full namespace.
|
|
28
|
+
|
|
21
29
|
Transaction-query `ILIKE` folding is ASCII-only on Durable Object SQLite;
|
|
22
30
|
non-ASCII case pairs can diverge from PostgreSQL.
|
|
23
31
|
|
|
@@ -154,7 +162,7 @@ convergence.
|
|
|
154
162
|
sockets, durable database size, engine watermark/floor, and aggregate counters.
|
|
155
163
|
After the configured idle gap (5 seconds in the harness deployment), the local
|
|
156
164
|
deterministic model resets in-memory state and changes boot ID while retaining
|
|
157
|
-
SQLite state and sockets. This models the harness
|
|
165
|
+
SQLite state and sockets. This models the harness idle-teardown pattern; it
|
|
158
166
|
does not claim that a real platform eviction happens on a 5-second schedule.
|
|
159
167
|
|
|
160
168
|
## Authenticated operator controls
|
|
@@ -224,8 +232,8 @@ A successful delegated mutation response has a hard causality contract: its
|
|
|
224
232
|
committed application effects must already be readable from the configured
|
|
225
233
|
upstream `/changes` feed before the application worker returns. The sync host
|
|
226
234
|
then ingests through those effects, journals the acknowledged LMID after them,
|
|
227
|
-
and only then returns the push response. This keeps
|
|
228
|
-
|
|
235
|
+
and only then returns the push response. This keeps change-log order intact so
|
|
236
|
+
an acknowledgement cannot reach a client before its effects.
|
|
229
237
|
Chat and Soot use an application-to-data service path that provides this
|
|
230
238
|
ordering today. A future topology that cannot provide it must extend the
|
|
231
239
|
delegated response with an upstream watermark receipt and make the host ingest
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SyncHostConfig, SyncHostEnv } from './types.js';
|
|
2
2
|
import type { Schema } from '@rocicorp/zero';
|
|
3
|
-
export declare function validatePullCaps(caps: PullCaps): PullCaps;
|
|
4
3
|
export declare function validateSyncHostConfig<Env extends SyncHostEnv, S extends Schema = Schema>(config: SyncHostConfig<Env, S>): SyncHostConfig<Env, S>;
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
export function validatePullCaps(caps) {
|
|
2
|
-
if (!Number.isSafeInteger(caps.maxChangeRows) || caps.maxChangeRows < 1) {
|
|
3
|
-
throw new TypeError('caps.maxChangeRows must be a positive safe integer');
|
|
4
|
-
}
|
|
5
|
-
return caps;
|
|
6
|
-
}
|
|
7
1
|
export function validateSyncHostConfig(config) {
|
|
8
2
|
if (typeof config.authorizeWake !== 'function') {
|
|
9
3
|
throw new TypeError('sync host config authorizeWake is required');
|
|
@@ -14,6 +8,9 @@ export function validateSyncHostConfig(config) {
|
|
|
14
8
|
if (typeof config.authorize !== 'function') {
|
|
15
9
|
throw new TypeError('sync host config authorize is required');
|
|
16
10
|
}
|
|
11
|
+
if (!config.queries || typeof config.queries !== 'object') {
|
|
12
|
+
throw new TypeError("sync host config queries is required: pass the app's Zero query registry (defineQueries result)");
|
|
13
|
+
}
|
|
17
14
|
const hasMutators = config.mutators !== undefined;
|
|
18
15
|
const hasDelegate = config.mutateUrl !== undefined;
|
|
19
16
|
if (hasMutators === hasDelegate) {
|
|
@@ -63,12 +63,7 @@ export function engine_finalize(db: any, client_group_id: string, client_id: str
|
|
|
63
63
|
export function engine_finalize_snapshot_generation(db: any, schema: any, generation: string, watermark: string): any;
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Production pull entry.
|
|
67
|
-
*/
|
|
68
|
-
export function engine_handle_pull(db: any, schema: any, visibility: any, caps: any, retain_changes: string, body: any, user_id: string): any;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Query-aware pull entry point. Desired-query ASTs are already resolved and
|
|
66
|
+
* Production pull entry point. Desired-query ASTs are already resolved and
|
|
72
67
|
* validated by the consumer host before crossing this boundary.
|
|
73
68
|
*/
|
|
74
69
|
export function engine_handle_query_pull(db: any, schema: any, retain_changes: string, body: any, user_id: string): any;
|
|
@@ -128,7 +123,6 @@ export interface InitOutput {
|
|
|
128
123
|
readonly engine_compile_query: (a: any, b: any, c: any) => [number, number, number];
|
|
129
124
|
readonly engine_finalize: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number];
|
|
130
125
|
readonly engine_finalize_snapshot_generation: (a: any, b: any, c: number, d: number, e: number, f: number) => [number, number, number];
|
|
131
|
-
readonly engine_handle_pull: (a: any, b: any, c: any, d: any, e: number, f: number, g: any, h: number, i: number) => [number, number, number];
|
|
132
126
|
readonly engine_handle_query_pull: (a: any, b: any, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
|
|
133
127
|
readonly engine_init_query_schema: (a: any) => [number, number];
|
|
134
128
|
readonly engine_init_schema: (a: any, b: any) => [number, number];
|
|
@@ -197,30 +197,7 @@ export function engine_finalize_snapshot_generation(db, schema, generation, wate
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
|
-
* Production pull entry.
|
|
201
|
-
* @param {any} db
|
|
202
|
-
* @param {any} schema
|
|
203
|
-
* @param {any} visibility
|
|
204
|
-
* @param {any} caps
|
|
205
|
-
* @param {string} retain_changes
|
|
206
|
-
* @param {any} body
|
|
207
|
-
* @param {string} user_id
|
|
208
|
-
* @returns {any}
|
|
209
|
-
*/
|
|
210
|
-
export function engine_handle_pull(db, schema, visibility, caps, retain_changes, body, user_id) {
|
|
211
|
-
const ptr0 = passStringToWasm0(retain_changes, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
212
|
-
const len0 = WASM_VECTOR_LEN;
|
|
213
|
-
const ptr1 = passStringToWasm0(user_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
214
|
-
const len1 = WASM_VECTOR_LEN;
|
|
215
|
-
const ret = wasm.engine_handle_pull(db, schema, visibility, caps, ptr0, len0, body, ptr1, len1);
|
|
216
|
-
if (ret[2]) {
|
|
217
|
-
throw takeFromExternrefTable0(ret[1]);
|
|
218
|
-
}
|
|
219
|
-
return takeFromExternrefTable0(ret[0]);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Query-aware pull entry point. Desired-query ASTs are already resolved and
|
|
200
|
+
* Production pull entry point. Desired-query ASTs are already resolved and
|
|
224
201
|
* validated by the consumer host before crossing this boundary.
|
|
225
202
|
* @param {any} db
|
|
226
203
|
* @param {any} schema
|
|
@@ -512,7 +489,7 @@ function __wbg_get_imports() {
|
|
|
512
489
|
const ret = Object.entries(arg0);
|
|
513
490
|
return ret;
|
|
514
491
|
},
|
|
515
|
-
|
|
492
|
+
__wbg_exec_58f139058b7f1498: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
516
493
|
arg0.exec(getStringFromWasm0(arg1, arg2), arg3);
|
|
517
494
|
}, arguments); },
|
|
518
495
|
__wbg_get_507a50627bffa49b: function(arg0, arg1) {
|
|
@@ -622,7 +599,7 @@ function __wbg_get_imports() {
|
|
|
622
599
|
__wbg_prototypesetcall_4770620bbe4688a0: function(arg0, arg1, arg2) {
|
|
623
600
|
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
624
601
|
},
|
|
625
|
-
|
|
602
|
+
__wbg_query_03a8dbd1d5cf6385: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
626
603
|
const ret = arg0.query(getStringFromWasm0(arg1, arg2), arg3);
|
|
627
604
|
return ret;
|
|
628
605
|
}, arguments); },
|
|
@@ -195,30 +195,7 @@ export function engine_finalize_snapshot_generation(db, schema, generation, wate
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
|
-
* Production pull entry.
|
|
199
|
-
* @param {any} db
|
|
200
|
-
* @param {any} schema
|
|
201
|
-
* @param {any} visibility
|
|
202
|
-
* @param {any} caps
|
|
203
|
-
* @param {string} retain_changes
|
|
204
|
-
* @param {any} body
|
|
205
|
-
* @param {string} user_id
|
|
206
|
-
* @returns {any}
|
|
207
|
-
*/
|
|
208
|
-
export function engine_handle_pull(db, schema, visibility, caps, retain_changes, body, user_id) {
|
|
209
|
-
const ptr0 = passStringToWasm0(retain_changes, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
210
|
-
const len0 = WASM_VECTOR_LEN;
|
|
211
|
-
const ptr1 = passStringToWasm0(user_id, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
212
|
-
const len1 = WASM_VECTOR_LEN;
|
|
213
|
-
const ret = wasm.engine_handle_pull(db, schema, visibility, caps, ptr0, len0, body, ptr1, len1);
|
|
214
|
-
if (ret[2]) {
|
|
215
|
-
throw takeFromExternrefTable0(ret[1]);
|
|
216
|
-
}
|
|
217
|
-
return takeFromExternrefTable0(ret[0]);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Query-aware pull entry point. Desired-query ASTs are already resolved and
|
|
198
|
+
* Production pull entry point. Desired-query ASTs are already resolved and
|
|
222
199
|
* validated by the consumer host before crossing this boundary.
|
|
223
200
|
* @param {any} db
|
|
224
201
|
* @param {any} schema
|
|
@@ -507,7 +484,7 @@ export function __wbg_entries_015dc610cd81ede0(arg0) {
|
|
|
507
484
|
const ret = Object.entries(arg0);
|
|
508
485
|
return ret;
|
|
509
486
|
}
|
|
510
|
-
export function
|
|
487
|
+
export function __wbg_exec_58f139058b7f1498() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
511
488
|
arg0.exec(getStringFromWasm0(arg1, arg2), arg3);
|
|
512
489
|
}, arguments); }
|
|
513
490
|
export function __wbg_get_507a50627bffa49b(arg0, arg1) {
|
|
@@ -617,7 +594,7 @@ export function __wbg_next_71f2aa1cb3d1e37e() { return handleError(function (arg
|
|
|
617
594
|
export function __wbg_prototypesetcall_4770620bbe4688a0(arg0, arg1, arg2) {
|
|
618
595
|
Uint8Array.prototype.set.call(getArrayU8FromWasm0(arg0, arg1), arg2);
|
|
619
596
|
}
|
|
620
|
-
export function
|
|
597
|
+
export function __wbg_query_03a8dbd1d5cf6385() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
621
598
|
const ret = arg0.query(getStringFromWasm0(arg1, arg2), arg3);
|
|
622
599
|
return ret;
|
|
623
600
|
}, arguments); }
|
|
Binary file
|
package/dist/host.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { DurableObject } from 'cloudflare:workers';
|
|
2
2
|
import { createSyncExecutor } from 'orez-sync-executor/core';
|
|
3
3
|
import { createSocketHost } from 'orez-sync-executor/realtime';
|
|
4
|
-
import {
|
|
4
|
+
import { validateSyncHostConfig } from './config.js';
|
|
5
5
|
import { createQueryCompiler } from './query-compiler.js';
|
|
6
6
|
import { resolveQueryPatch } from './query-patch.js';
|
|
7
7
|
import { decodeSqlParams, SqlStorageDirect, SqlStorageMutatorTransaction, SqlStorageSyncDb, } from './sql-storage-adapter.js';
|
|
8
|
-
import { engine_authorize_realtime_subscription, engine_apply_snapshot_changes, engine_apply_snapshot_page, engine_apply_upstream, engine_begin_snapshot_generation, engine_finalize, engine_finalize_snapshot_generation,
|
|
8
|
+
import { engine_authorize_realtime_subscription, engine_apply_snapshot_changes, engine_apply_snapshot_page, engine_apply_upstream, engine_begin_snapshot_generation, engine_finalize, engine_finalize_snapshot_generation, engine_handle_query_pull, engine_init_query_schema, engine_init_schema, engine_invalidate, engine_memory_bytes, engine_preflight, engine_prune, engine_push_validate, engine_read_snapshot_progress, engine_state, engine_version, } from './wasm.js';
|
|
9
9
|
import { IngestBreakerError, IngestCircuitBreaker, retryDelayMs, shouldRetryDelegatedPush, } from './write-safeguards.js';
|
|
10
10
|
const NAMESPACE_HEADER = 'x-orez-sync-namespace';
|
|
11
11
|
const UPSTREAM_PATH_HEADER = 'x-orez-sync-upstream-path';
|
|
@@ -16,10 +16,6 @@ const UPSTREAM_PATH_HEADER = 'x-orez-sync-upstream-path';
|
|
|
16
16
|
const IDENTITY_HEADER = 'x-orez-sync-identity';
|
|
17
17
|
const DEFAULT_SNAPSHOT_PAGE_ROWS = 2_000;
|
|
18
18
|
const MIN_SNAPSHOT_PAGE_ROWS = 100;
|
|
19
|
-
const DEFAULT_CAPS = {
|
|
20
|
-
maxChangeRows: 10_000,
|
|
21
|
-
maxChangeBytes: 2_000_000,
|
|
22
|
-
};
|
|
23
19
|
function freshCounters() {
|
|
24
20
|
return {
|
|
25
21
|
pulls: 0,
|
|
@@ -90,28 +86,6 @@ async function requestObject(request) {
|
|
|
90
86
|
throw requestError('request body must be a JSON object');
|
|
91
87
|
return value;
|
|
92
88
|
}
|
|
93
|
-
// clients always ship their desired queries and treat the server's got ack as
|
|
94
|
-
// authoritative. the query-aware engine acks through its own tracking; a
|
|
95
|
-
// non-query-aware host syncs every visible row, so its ack is an echo of the
|
|
96
|
-
// hash-level desired delta in the same response that carries the rows.
|
|
97
|
-
function withGotQueriesAck(body, queryAware, response) {
|
|
98
|
-
if (queryAware)
|
|
99
|
-
return response;
|
|
100
|
-
const queries = body.queries;
|
|
101
|
-
if (!queries || typeof queries.version !== 'number' || !Array.isArray(queries.patch)) {
|
|
102
|
-
return response;
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
...response,
|
|
106
|
-
gotQueries: {
|
|
107
|
-
version: queries.version,
|
|
108
|
-
patch: queries.patch.map((op) => {
|
|
109
|
-
const entry = op;
|
|
110
|
-
return entry.op === 'put' ? { op: 'put', hash: entry.hash } : op;
|
|
111
|
-
}),
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
89
|
function routeAfterNamespace(pathname) {
|
|
116
90
|
const [, , ...parts] = pathname.split('/');
|
|
117
91
|
return `/${parts.join('/')}`;
|
|
@@ -269,7 +243,6 @@ export function createSyncDurableObject(config) {
|
|
|
269
243
|
validateSyncHostConfig(config);
|
|
270
244
|
const compileQuery = createQueryCompiler(config.schema);
|
|
271
245
|
const defaultRetainChanges = String(config.retainChanges ?? 4_096);
|
|
272
|
-
const caps = validatePullCaps({ ...DEFAULT_CAPS, ...config.caps });
|
|
273
246
|
const idleTeardownMs = config.idleTeardownMs ?? 5_000;
|
|
274
247
|
// A CF fan-out wakes every client into an HTTP pull. Give concurrent writer
|
|
275
248
|
// requests a real batching window so a storm burst creates one pull wave.
|
|
@@ -308,7 +281,6 @@ export function createSyncDurableObject(config) {
|
|
|
308
281
|
// during the replay wait for it instead of racing past into an empty hub.
|
|
309
282
|
#realtimeReady = null;
|
|
310
283
|
#ingestPromise = null;
|
|
311
|
-
#queryPullLocks = new Map();
|
|
312
284
|
#recordingIngestBillable = false;
|
|
313
285
|
#ingestBreaker = new IngestCircuitBreaker({
|
|
314
286
|
budgetRows: ingestBudgetRows,
|
|
@@ -384,8 +356,7 @@ export function createSyncDurableObject(config) {
|
|
|
384
356
|
this.#ingestBreaker.restore(ingestBreakerReason, Number(this.#controlGet('ingestBreakerRetryAt')), Number(this.#controlGet('ingestBreakerTrips')));
|
|
385
357
|
}
|
|
386
358
|
this.#wasm(() => engine_init_schema(this.#engineDb, config.schema));
|
|
387
|
-
|
|
388
|
-
this.#wasm(() => engine_init_query_schema(this.#engineDb));
|
|
359
|
+
this.#wasm(() => engine_init_query_schema(this.#engineDb));
|
|
389
360
|
});
|
|
390
361
|
});
|
|
391
362
|
}
|
|
@@ -402,22 +373,6 @@ export function createSyncDurableObject(config) {
|
|
|
402
373
|
this.#counters.wasmBoundaryCalls++;
|
|
403
374
|
return call();
|
|
404
375
|
}
|
|
405
|
-
async #acquireQueryPullLock(clientGroupID) {
|
|
406
|
-
const previous = this.#queryPullLocks.get(clientGroupID);
|
|
407
|
-
let release;
|
|
408
|
-
const current = new Promise((resolve) => {
|
|
409
|
-
release = resolve;
|
|
410
|
-
});
|
|
411
|
-
this.#queryPullLocks.set(clientGroupID, current);
|
|
412
|
-
if (previous)
|
|
413
|
-
await previous;
|
|
414
|
-
return () => {
|
|
415
|
-
release();
|
|
416
|
-
if (this.#queryPullLocks.get(clientGroupID) === current) {
|
|
417
|
-
this.#queryPullLocks.delete(clientGroupID);
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
376
|
#simulateIdleTeardown(now) {
|
|
422
377
|
if (this.#lastRequestAt > 0 && now - this.#lastRequestAt >= idleTeardownMs) {
|
|
423
378
|
this.#bootID = crypto.randomUUID();
|
|
@@ -438,37 +393,10 @@ export function createSyncDurableObject(config) {
|
|
|
438
393
|
}
|
|
439
394
|
this.#lastRequestAt = now;
|
|
440
395
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
rowLocal: typeof config.visibility.rowLocal === 'function'
|
|
446
|
-
? config.visibility.rowLocal(claims)
|
|
447
|
-
: config.visibility.rowLocal,
|
|
448
|
-
filters: Object.keys(config.schema.tables).flatMap((table) => {
|
|
449
|
-
const filter = config.visibility?.filter(table, claims);
|
|
450
|
-
return filter
|
|
451
|
-
? [
|
|
452
|
-
filter.kind === 'expression'
|
|
453
|
-
? { kind: 'expression', table, expression: filter.expression }
|
|
454
|
-
: {
|
|
455
|
-
kind: 'raw',
|
|
456
|
-
table,
|
|
457
|
-
sql: filter.sql,
|
|
458
|
-
params: [...(filter.params ?? [])],
|
|
459
|
-
},
|
|
460
|
-
]
|
|
461
|
-
: [];
|
|
462
|
-
}),
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
// the admin-set namespace knobs (writer, visibility, query-aware,
|
|
466
|
-
// retention) live in _zsync_host_control, NOT in instance fields: a real
|
|
467
|
-
// eviction recreates this instance, and an in-memory override silently
|
|
468
|
-
// reverting to the config default mid-run turns a query-aware namespace
|
|
469
|
-
// back into a baseline one — the client keeps re-sending its desired-query
|
|
470
|
-
// patch, the host ignores it, and every pull answers {unchanged:true}
|
|
471
|
-
// forever.
|
|
396
|
+
// the admin-set namespace knobs (writer, retention) live in
|
|
397
|
+
// _zsync_host_control, NOT in instance fields: a real eviction recreates
|
|
398
|
+
// this instance, and an in-memory override silently reverting to the
|
|
399
|
+
// config default mid-run changes namespace behavior under the client.
|
|
472
400
|
#controlGet(key) {
|
|
473
401
|
const row = this.#directSql.query('SELECT value FROM _zsync_host_control WHERE key = ?', [key])[0];
|
|
474
402
|
return row?.value ?? null;
|
|
@@ -500,14 +428,6 @@ export function createSyncDurableObject(config) {
|
|
|
500
428
|
#writerEnabled() {
|
|
501
429
|
return this.#controlGet('writerEnabled') === '1';
|
|
502
430
|
}
|
|
503
|
-
#visibilityEnabled() {
|
|
504
|
-
const value = this.#controlGet('visibilityEnabled');
|
|
505
|
-
return value === null ? (config.visibilityEnabled ?? false) : value === '1';
|
|
506
|
-
}
|
|
507
|
-
#queryAwareOverride() {
|
|
508
|
-
const value = this.#controlGet('queryAwareOverride');
|
|
509
|
-
return value === null ? null : value === '1';
|
|
510
|
-
}
|
|
511
431
|
#retainChanges() {
|
|
512
432
|
return this.#controlGet('retainChanges') ?? defaultRetainChanges;
|
|
513
433
|
}
|
|
@@ -997,83 +917,47 @@ export function createSyncDurableObject(config) {
|
|
|
997
917
|
let body;
|
|
998
918
|
try {
|
|
999
919
|
body = await requestObject(request);
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
: (config.queryAware ?? Boolean(config.resolveQueries)));
|
|
1004
|
-
const transformVersion = queryAware
|
|
1005
|
-
? typeof config.queryTransformVersion === 'function'
|
|
1006
|
-
? config.queryTransformVersion(claims)
|
|
1007
|
-
: (config.queryTransformVersion ?? 0)
|
|
1008
|
-
: 0;
|
|
920
|
+
const transformVersion = typeof config.queryTransformVersion === 'function'
|
|
921
|
+
? config.queryTransformVersion(claims)
|
|
922
|
+
: (config.queryTransformVersion ?? 0);
|
|
1009
923
|
if (!Number.isSafeInteger(transformVersion) || transformVersion < 0) {
|
|
1010
924
|
throw new TypeError('queryTransformVersion must be a non-negative safe integer');
|
|
1011
925
|
}
|
|
1012
|
-
// The group lock exists to keep two desired-query patches from being
|
|
1013
|
-
// applied out of arrival order when one of them is slow to resolve.
|
|
1014
|
-
// A pull carrying no patch applies nothing, and it reads membership
|
|
1015
|
-
// inside transactionSync, which is already atomic against whichever
|
|
1016
|
-
// patch commits around it. Holding the lock for those pulls only makes
|
|
1017
|
-
// an unchanged warm pull wait out somebody else's query-resolution round
|
|
1018
|
-
// trip to the application, which is the head-of-line blocking behind
|
|
1019
|
-
// the pull tail: a client polls far more often than it changes what it
|
|
1020
|
-
// wants, so almost every pull in flight is patch-free.
|
|
1021
|
-
const releaseQueryPull = queryAware && config.resolveQueries && body.queries
|
|
1022
|
-
? await this.#acquireQueryPullLock(typeof body.clientGroupID === 'string' ? body.clientGroupID : '')
|
|
1023
|
-
: null;
|
|
1024
926
|
let response;
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
throw requestError('query put requires a server-resolved named query');
|
|
1035
|
-
}
|
|
1036
|
-
// resolveQueries may be async and needs `env` (a consumer can
|
|
1037
|
-
// delegate the transform to its app's real synced-queries
|
|
1038
|
-
// endpoint over an app service binding — authenticate runs in the
|
|
1039
|
-
// worker isolate, but the query loop runs here in the DO, so the
|
|
1040
|
-
// binding must come from the DO's own env, not a shared global).
|
|
1041
|
-
const patch = await resolveQueryPatch(queries.patch, (requests) => config.resolveQueries(requests, claims, this.env), transformVersion, requestError);
|
|
1042
|
-
body = { ...body, queries: { ...queries, patch } };
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
if (queryAware) {
|
|
1046
|
-
body = { ...body, _serverQueryTransformVersion: transformVersion };
|
|
927
|
+
{
|
|
928
|
+
const queries = body.queries;
|
|
929
|
+
if (queries && Array.isArray(queries.patch)) {
|
|
930
|
+
// named queries resolve in-process against the app's ordinary
|
|
931
|
+
// Zero registry, synchronously: patch application order is
|
|
932
|
+
// arrival order by construction, with no cross-request await
|
|
933
|
+
// between resolution and the engine transaction.
|
|
934
|
+
const patch = resolveQueryPatch(queries.patch, config.queries, claims, transformVersion, requestError);
|
|
935
|
+
body = { ...body, queries: { ...queries, patch } };
|
|
1047
936
|
}
|
|
937
|
+
body = { ...body, _serverQueryTransformVersion: transformVersion };
|
|
1048
938
|
const clientID = typeof body.clientID === 'string' ? body.clientID : '';
|
|
1049
939
|
this.#pulling.add(clientID);
|
|
1050
940
|
try {
|
|
1051
941
|
const txStarted = performance.now();
|
|
1052
942
|
const duringFault = this.#takeFault('pull_during_tx');
|
|
1053
943
|
response = this.ctx.storage.transactionSync(() => {
|
|
1054
|
-
const result = this.#wasm(() =>
|
|
1055
|
-
? engine_handle_query_pull(this.#engineDb, config.schema, this.#retainChanges(), body, claims.userID)
|
|
1056
|
-
: engine_handle_pull(this.#engineDb, config.schema, this.#visibility(claims), caps, this.#retainChanges(), body, claims.userID));
|
|
944
|
+
const result = this.#wasm(() => engine_handle_query_pull(this.#engineDb, config.schema, this.#retainChanges(), body, claims.userID));
|
|
1057
945
|
if (duringFault)
|
|
1058
946
|
throw this.#faultError(duringFault, 'pull_during_tx');
|
|
1059
947
|
return result;
|
|
1060
948
|
});
|
|
1061
949
|
transactionMs = performance.now() - txStarted;
|
|
1062
|
-
response = withGotQueriesAck(body, queryAware, response);
|
|
1063
950
|
}
|
|
1064
951
|
finally {
|
|
1065
952
|
this.#pulling.delete(clientID);
|
|
1066
953
|
}
|
|
1067
954
|
}
|
|
1068
|
-
finally {
|
|
1069
|
-
releaseQueryPull?.();
|
|
1070
|
-
}
|
|
1071
955
|
const afterPullFault = this.#takeFault('pull_after_commit');
|
|
1072
956
|
if (afterPullFault)
|
|
1073
957
|
throw this.#faultError(afterPullFault, 'pull_after_commit');
|
|
1074
958
|
const patch = Array.isArray(response.rowsPatch) ? response.rowsPatch : [];
|
|
1075
959
|
const queriesBody = body.queries;
|
|
1076
|
-
const queryPuts =
|
|
960
|
+
const queryPuts = Array.isArray(queriesBody?.patch)
|
|
1077
961
|
? queriesBody.patch.filter((entry) => entry?.op === 'put').length
|
|
1078
962
|
: 0;
|
|
1079
963
|
this.#counters.queryRecompilations += queryPuts;
|
|
@@ -1519,20 +1403,6 @@ export function createSyncDurableObject(config) {
|
|
|
1519
1403
|
this.ctx.abort('admin requested durable object restart');
|
|
1520
1404
|
return json({ ok: true, bootID: this.#bootID });
|
|
1521
1405
|
}
|
|
1522
|
-
if (route === '/admin/visibility') {
|
|
1523
|
-
return request.json().then((body) => {
|
|
1524
|
-
const enabled = Boolean(body.enabled);
|
|
1525
|
-
this.#controlSet('visibilityEnabled', enabled ? '1' : '0');
|
|
1526
|
-
return json({ ok: true, enabled });
|
|
1527
|
-
});
|
|
1528
|
-
}
|
|
1529
|
-
if (route === '/admin/query-aware') {
|
|
1530
|
-
return request.json().then((body) => {
|
|
1531
|
-
const enabled = Boolean(body.enabled);
|
|
1532
|
-
this.#controlSet('queryAwareOverride', enabled ? '1' : '0');
|
|
1533
|
-
return json({ ok: true, enabled });
|
|
1534
|
-
});
|
|
1535
|
-
}
|
|
1536
1406
|
if (route === '/admin/retention') {
|
|
1537
1407
|
return request
|
|
1538
1408
|
.json()
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export { createSyncDurableObject, createSyncWorker } from './host.js';
|
|
2
2
|
export { createQueryCompiler } from './query-compiler.js';
|
|
3
3
|
export { resolveQueryPatch } from './query-patch.js';
|
|
4
|
-
export { visibility } from './types.js';
|
|
5
4
|
export { DEFAULT_TRANSACTION_QUERY_BUDGET, TransactionQueryBudgetError, executeTransactionQueryPlan, executeTransactionQueryPlanAsync, } from './transaction-query.js';
|
|
6
|
-
export type {
|
|
5
|
+
export type { SyncHostConfig, SyncHostEnv, ServiceBinding, SyncSql, UpstreamConfig, } from './types.js';
|
|
7
6
|
export type { SyncDurableObjectConstructor } from './host.js';
|
|
8
7
|
export type { CompiledTransactionQueryNode, CompiledTransactionQueryPlan, CompiledTransactionQueryRelationship, TransactionQueryBinding, TransactionQueryBudget, TransactionQueryColumn, TransactionQueryColumnType, TransactionQueryExecutionOptions, TransactionQueryWireValue, } from './transaction-query.js';
|
|
9
8
|
export type { TransactionQueryCompiler } from './query-compiler.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { createSyncDurableObject, createSyncWorker } from './host.js';
|
|
2
2
|
export { createQueryCompiler } from './query-compiler.js';
|
|
3
3
|
export { resolveQueryPatch } from './query-patch.js';
|
|
4
|
-
export { visibility } from './types.js';
|
|
5
4
|
export { DEFAULT_TRANSACTION_QUERY_BUDGET, TransactionQueryBudgetError, executeTransactionQueryPlan, executeTransactionQueryPlanAsync, } from './transaction-query.js';
|
package/dist/query-patch.d.ts
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export type QueryResolutionRequest = {
|
|
3
|
-
readonly name: string;
|
|
4
|
-
readonly args: readonly JsonValue[];
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* One resolved query, positionally matched to the request at the same index.
|
|
8
|
-
*
|
|
9
|
-
* An `error` fails the whole patch (the pull is refused as one unit), but the
|
|
10
|
-
* refusal names the query it came from and carries its resolver error, so a
|
|
11
|
-
* client that registers one unknown query alongside nine good ones is told
|
|
12
|
-
* which one is unknown.
|
|
13
|
-
*/
|
|
14
|
-
export type QueryResolution = {
|
|
15
|
-
readonly ast: JsonValue;
|
|
16
|
-
} | {
|
|
17
|
-
readonly error: string;
|
|
18
|
-
};
|
|
1
|
+
import type { AnyQueryRegistry } from '@rocicorp/zero';
|
|
19
2
|
/**
|
|
20
3
|
* Turn a client's desired-query patch into the resolved patch sync-core takes.
|
|
21
4
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
5
|
+
* Every named query resolves in-process against the app's ordinary Zero query
|
|
6
|
+
* registry — the same object the client was built with. `CustomQuery.fn`
|
|
7
|
+
* validates the arguments and applies the query context, so this is exactly
|
|
8
|
+
* the transform zero-cache delegates to an app endpoint, minus the network.
|
|
9
|
+
* The context is the host's authenticated identity (claims on the CF host,
|
|
10
|
+
* authData on the browser host); a query definition scopes its rows from it.
|
|
11
|
+
*
|
|
12
|
+
* Resolution is structural on purpose: the registry is walked as a plain
|
|
13
|
+
* object and the built query's `ast` is read directly, never through Zero's
|
|
14
|
+
* `asQueryInternals`, whose instance-tag check fails whenever the app's
|
|
15
|
+
* registry and this host resolve different copies of the zero module.
|
|
16
|
+
*
|
|
17
|
+
* A resolution error fails the whole patch (the pull is refused as one unit),
|
|
18
|
+
* but the refusal names the query it came from, so a client that registers
|
|
19
|
+
* one unknown query alongside nine good ones is told which one is unknown.
|
|
29
20
|
*/
|
|
30
|
-
export declare function resolveQueryPatch(patch: readonly unknown[],
|
|
21
|
+
export declare function resolveQueryPatch(patch: readonly unknown[], queries: AnyQueryRegistry, context: unknown, transformVersion: number, fail: (message: string) => Error): unknown[];
|
package/dist/query-patch.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
// Zero separates registry path segments with '.' or '|' (its getValueAtPath).
|
|
2
|
+
const NAME_SEPARATOR = /[.|]/;
|
|
1
3
|
/**
|
|
2
4
|
* Turn a client's desired-query patch into the resolved patch sync-core takes.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Every named query resolves in-process against the app's ordinary Zero query
|
|
7
|
+
* registry — the same object the client was built with. `CustomQuery.fn`
|
|
8
|
+
* validates the arguments and applies the query context, so this is exactly
|
|
9
|
+
* the transform zero-cache delegates to an app endpoint, minus the network.
|
|
10
|
+
* The context is the host's authenticated identity (claims on the CF host,
|
|
11
|
+
* authData on the browser host); a query definition scopes its rows from it.
|
|
12
|
+
*
|
|
13
|
+
* Resolution is structural on purpose: the registry is walked as a plain
|
|
14
|
+
* object and the built query's `ast` is read directly, never through Zero's
|
|
15
|
+
* `asQueryInternals`, whose instance-tag check fails whenever the app's
|
|
16
|
+
* registry and this host resolve different copies of the zero module.
|
|
17
|
+
*
|
|
18
|
+
* A resolution error fails the whole patch (the pull is refused as one unit),
|
|
19
|
+
* but the refusal names the query it came from, so a client that registers
|
|
20
|
+
* one unknown query alongside nine good ones is told which one is unknown.
|
|
11
21
|
*/
|
|
12
|
-
export
|
|
13
|
-
const
|
|
14
|
-
const requests = [];
|
|
15
|
-
const requestByKey = new Map();
|
|
22
|
+
export function resolveQueryPatch(patch, queries, context, transformVersion, fail) {
|
|
23
|
+
const result = [...patch];
|
|
16
24
|
patch.forEach((operation, index) => {
|
|
17
25
|
if (!operation || typeof operation !== 'object')
|
|
18
26
|
return;
|
|
@@ -24,34 +32,38 @@ export async function resolveQueryPatch(patch, resolve, transformVersion, fail)
|
|
|
24
32
|
}
|
|
25
33
|
if (!Array.isArray(op.args))
|
|
26
34
|
throw fail('named query args must be an array');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
let node = queries;
|
|
36
|
+
for (const segment of op.name.split(NAME_SEPARATOR)) {
|
|
37
|
+
node =
|
|
38
|
+
node && typeof node === 'object'
|
|
39
|
+
? node[segment]
|
|
40
|
+
: undefined;
|
|
31
41
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const detail = resolution && 'error' in resolution ? `: ${resolution.error}` : '';
|
|
47
|
-
throw fail(`unknown or unsupported named query: ${put.name}${detail}`);
|
|
42
|
+
const custom = node;
|
|
43
|
+
if (!custom || typeof custom.fn !== 'function') {
|
|
44
|
+
throw fail(`unknown or unsupported named query: ${op.name}`);
|
|
45
|
+
}
|
|
46
|
+
let ast;
|
|
47
|
+
try {
|
|
48
|
+
const built = custom.fn({
|
|
49
|
+
args: op.args[0],
|
|
50
|
+
ctx: context,
|
|
51
|
+
});
|
|
52
|
+
if (!built || typeof built !== 'object' || built.ast === undefined) {
|
|
53
|
+
throw new Error('query did not build to an AST-bearing Zero query');
|
|
54
|
+
}
|
|
55
|
+
ast = built.ast;
|
|
48
56
|
}
|
|
49
|
-
|
|
57
|
+
catch (error) {
|
|
58
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
59
|
+
throw fail(`unknown or unsupported named query: ${op.name}: ${detail}`);
|
|
60
|
+
}
|
|
61
|
+
result[index] = {
|
|
50
62
|
op: 'put',
|
|
51
|
-
hash:
|
|
52
|
-
ast
|
|
63
|
+
hash: op.hash,
|
|
64
|
+
ast,
|
|
53
65
|
transformVersion,
|
|
54
66
|
};
|
|
55
|
-
}
|
|
67
|
+
});
|
|
56
68
|
return result;
|
|
57
69
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import type { QueryResolution, QueryResolutionRequest } from './query-patch.js';
|
|
2
1
|
import type { TransactionQueryBudget } from './transaction-query.js';
|
|
3
|
-
import type { Schema } from '@rocicorp/zero';
|
|
4
|
-
import type { ExecResult, MutatorRegistry, NormalizedClaims, SqlStatementMetadata
|
|
2
|
+
import type { AnyQueryRegistry, Schema } from '@rocicorp/zero';
|
|
3
|
+
import type { ExecResult, MutatorRegistry, NormalizedClaims, SqlStatementMetadata } from 'orez-sync-executor';
|
|
5
4
|
import type { StreamingManifest } from 'orez-sync-executor/realtime';
|
|
6
|
-
export { visibility, type VisibilityExpression, type VisibilityFilter, type VisibilityOperand, type VisibilityValue, } from './visibility.js';
|
|
7
|
-
export type { VisibilityConfig } from 'orez-sync-executor';
|
|
8
|
-
export type { QueryResolution, QueryResolutionRequest } from './query-patch.js';
|
|
9
5
|
export interface SyncSql {
|
|
10
6
|
exec(sql: string, params?: readonly unknown[], metadata?: SqlStatementMetadata): ExecResult;
|
|
11
7
|
query<Row extends Record<string, unknown> = Record<string, unknown>>(sql: string, params?: readonly unknown[]): Row[];
|
|
12
8
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Resolve a whole desired-query patch in one call.
|
|
15
|
-
*
|
|
16
|
-
* This is a batch contract because it is nearly always a network call: a
|
|
17
|
-
* consumer delegates the transform to its application, which authenticates and
|
|
18
|
-
* answers over a service binding. Resolving one query per call made a pull's
|
|
19
|
-
* cost linear in the number of queries a screen registers, and each round trip
|
|
20
|
-
* re-authenticated. Measured against production with a captured mobile client's
|
|
21
|
-
* desired-query set: an 11-query pull took 23.3 s against 2.0 s for the same
|
|
22
|
-
* pull carrying no queries.
|
|
23
|
-
*
|
|
24
|
-
* Implementations must return exactly one entry per request, in request order.
|
|
25
|
-
*/
|
|
26
|
-
export type QueryResolver = (requests: readonly QueryResolutionRequest[], claims: NormalizedClaims, env: SyncHostEnv) => readonly QueryResolution[] | Promise<readonly QueryResolution[]>;
|
|
27
|
-
export type PullCaps = {
|
|
28
|
-
maxChangeRows: number;
|
|
29
|
-
maxChangeBytes: number;
|
|
30
|
-
};
|
|
31
9
|
export interface SyncHostEnv {
|
|
32
10
|
SYNC_DO: DurableObjectNamespace;
|
|
33
11
|
ADMIN_KEY?: string;
|
|
@@ -109,12 +87,14 @@ export type SyncHostConfig<Env extends SyncHostEnv = SyncHostEnv, S extends Sche
|
|
|
109
87
|
authorizeNotify(request: Request, env: Env): boolean | Promise<boolean>;
|
|
110
88
|
/** Resolve the first path component or another consumer-defined namespace. */
|
|
111
89
|
namespace(request: Request): string | null;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
90
|
+
/**
|
|
91
|
+
* The app's ordinary Zero query registry (the `defineQueries` result the
|
|
92
|
+
* client is built with). The host resolves every desired named query
|
|
93
|
+
* in-process against it — argument validation and context scoping run in
|
|
94
|
+
* the query definitions themselves, with the authenticated claims as the
|
|
95
|
+
* query context. There is no app endpoint to call and nothing else to wire.
|
|
96
|
+
*/
|
|
97
|
+
queries: AnyQueryRegistry;
|
|
118
98
|
/**
|
|
119
99
|
* Streaming fields for this namespace: which columns may carry a live,
|
|
120
100
|
* uncommitted value, and their publish mode and rate bounds.
|
|
@@ -138,10 +118,7 @@ export type SyncHostConfig<Env extends SyncHostEnv = SyncHostEnv, S extends Sche
|
|
|
138
118
|
authorizeProduce?: (request: Request, env: Env) => boolean | Promise<boolean>;
|
|
139
119
|
/** Server-owned invalidation epoch for permission/schema transforms. */
|
|
140
120
|
queryTransformVersion?: number | ((claims: NormalizedClaims) => number);
|
|
141
|
-
/** Enable consumer visibility from the first request. Defaults to false for harnesses. */
|
|
142
|
-
visibilityEnabled?: boolean;
|
|
143
121
|
retainChanges?: number;
|
|
144
|
-
caps?: Partial<PullCaps>;
|
|
145
122
|
idleTeardownMs?: number;
|
|
146
123
|
wakeCoalesceMs?: number;
|
|
147
124
|
/** per-query guard for recursive transaction query materialization. */
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
package/dist/wasm.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export declare const engine_begin_snapshot_generation: (db: any, schema: any, st
|
|
|
8
8
|
export declare const engine_compile_query: (schema: any, ast: any, format: any) => any;
|
|
9
9
|
export declare const engine_finalize: (db: any, client_group_id: string, client_id: string, mutation_id: string) => void;
|
|
10
10
|
export declare const engine_finalize_snapshot_generation: (db: any, schema: any, generation: string, watermark: string) => any;
|
|
11
|
-
export declare const engine_handle_pull: (db: any, schema: any, visibility: any, caps: any, retain_changes: string, body: any, user_id: string) => any;
|
|
12
11
|
export declare const engine_handle_query_pull: (db: any, schema: any, retain_changes: string, body: any, user_id: string) => any;
|
|
13
12
|
export declare const engine_init_query_schema: (db: any) => void;
|
|
14
13
|
export declare const engine_init_schema: (db: any, schema: any) => void;
|
package/dist/wasm.js
CHANGED
|
@@ -25,7 +25,6 @@ export const engine_begin_snapshot_generation = withWasm(generated.engine_begin_
|
|
|
25
25
|
export const engine_compile_query = withWasm(generated.engine_compile_query);
|
|
26
26
|
export const engine_finalize = withWasm(generated.engine_finalize);
|
|
27
27
|
export const engine_finalize_snapshot_generation = withWasm(generated.engine_finalize_snapshot_generation);
|
|
28
|
-
export const engine_handle_pull = withWasm(generated.engine_handle_pull);
|
|
29
28
|
export const engine_handle_query_pull = withWasm(generated.engine_handle_query_pull);
|
|
30
29
|
export const engine_init_query_schema = withWasm(generated.engine_init_query_schema);
|
|
31
30
|
export const engine_init_schema = withWasm(generated.engine_init_schema);
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez-sync-cf-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
|
+
"description": "Internal orez Cloudflare sync host. Do not import directly: consumers use orez-lite/cloudflare/sync.",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"exports": {
|
|
6
7
|
".": {
|
|
@@ -27,10 +28,6 @@
|
|
|
27
28
|
"types": "./dist/transaction-query.d.ts",
|
|
28
29
|
"import": "./dist/transaction-query.js"
|
|
29
30
|
},
|
|
30
|
-
"./visibility": {
|
|
31
|
-
"types": "./dist/visibility.d.ts",
|
|
32
|
-
"import": "./dist/visibility.js"
|
|
33
|
-
},
|
|
34
31
|
"./vite-wasm-loader": {
|
|
35
32
|
"types": "./dist/vite-wasm-loader.d.ts",
|
|
36
33
|
"import": "./dist/vite-wasm-loader.js"
|
|
@@ -49,7 +46,7 @@
|
|
|
49
46
|
"build:dist:platform": "bun run build:dependencies && bun run build:wasm:platform && bun run build:runtime",
|
|
50
47
|
"bundle": "bun run build:dist && wrangler deploy --dry-run --outdir .wrangler/bundle",
|
|
51
48
|
"measure": "bun run build:dist && bun measure.mjs",
|
|
52
|
-
"test": "bun run test:config && bun run test:query-compiler && bun run test:package && bun run test:platform && bun run test:integration && bun run test:ingest && bun run test:restart",
|
|
49
|
+
"test": "bun run test:config && bun run test:query-compiler && bun run test:package && bun run test:platform && bun run test:integration && bun run test:large-data && bun run test:ingest && bun run test:restart",
|
|
53
50
|
"test:config": "bun test config.test.mjs write-safeguards.test.mjs transaction-query.test.mjs",
|
|
54
51
|
"test:integration": "bun run build:dist && bun integration-test.mjs",
|
|
55
52
|
"test:ingest": "bun run build:dist && bun ingest-test.mjs",
|
|
@@ -57,10 +54,11 @@
|
|
|
57
54
|
"test:query-compiler": "bun run build:dist && bun test --preload orez-sync-cf-host/bun-wasm-loader query-compiler.test.mjs && node vite-ssr-query-compiler-test.mjs",
|
|
58
55
|
"test:restart": "bun run build:dist && bun restart-test.mjs",
|
|
59
56
|
"test:platform": "bun run build:dist:platform && bun platform-test.mjs",
|
|
60
|
-
"typecheck": "bun run build:dist:platform && tsc --noEmit"
|
|
57
|
+
"typecheck": "bun run build:dist:platform && tsc --noEmit",
|
|
58
|
+
"test:large-data": "bun run build:dist && bun large-data-test.mjs"
|
|
61
59
|
},
|
|
62
60
|
"dependencies": {
|
|
63
|
-
"orez-sync-executor": "0.
|
|
61
|
+
"orez-sync-executor": "0.12.0"
|
|
64
62
|
},
|
|
65
63
|
"devDependencies": {
|
|
66
64
|
"@cloudflare/workers-types": "4.20260617.1",
|
package/dist/visibility.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { VisibilityExpression, VisibilityFilter, VisibilityOperand, VisibilityValue } from 'orez-sync-executor';
|
|
2
|
-
export type { VisibilityExpression, VisibilityFilter, VisibilityOperand, VisibilityValue, } from 'orez-sync-executor';
|
|
3
|
-
export declare const visibility: {
|
|
4
|
-
readonly column: (table: string, column: string, qualifier?: string) => VisibilityOperand;
|
|
5
|
-
readonly value: (value: VisibilityValue) => VisibilityOperand;
|
|
6
|
-
readonly comparison: (left: VisibilityOperand, operator: "=" | "!=" | "<" | ">" | "<=" | ">=" | "IS" | "IS NOT", right: VisibilityOperand) => VisibilityExpression;
|
|
7
|
-
readonly and: (...conditions: VisibilityExpression[]) => VisibilityExpression;
|
|
8
|
-
readonly or: (...conditions: VisibilityExpression[]) => VisibilityExpression;
|
|
9
|
-
readonly exists: (table: string, where: VisibilityExpression, qualifier?: string) => VisibilityExpression;
|
|
10
|
-
readonly filter: (expression: VisibilityExpression) => VisibilityFilter;
|
|
11
|
-
readonly raw: (sql: string, params?: readonly VisibilityValue[]) => VisibilityFilter;
|
|
12
|
-
};
|
package/dist/visibility.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export const visibility = {
|
|
2
|
-
column(table, column, qualifier) {
|
|
3
|
-
return qualifier
|
|
4
|
-
? { type: 'column', table, column, qualifier }
|
|
5
|
-
: { type: 'column', table, column };
|
|
6
|
-
},
|
|
7
|
-
value(value) {
|
|
8
|
-
return { type: 'value', value };
|
|
9
|
-
},
|
|
10
|
-
comparison(left, operator, right) {
|
|
11
|
-
return { type: 'comparison', operator, left, right };
|
|
12
|
-
},
|
|
13
|
-
and(...conditions) {
|
|
14
|
-
return { type: 'and', conditions };
|
|
15
|
-
},
|
|
16
|
-
or(...conditions) {
|
|
17
|
-
return { type: 'or', conditions };
|
|
18
|
-
},
|
|
19
|
-
exists(table, where, qualifier) {
|
|
20
|
-
return qualifier
|
|
21
|
-
? { type: 'exists', table, qualifier, where }
|
|
22
|
-
: { type: 'exists', table, where };
|
|
23
|
-
},
|
|
24
|
-
filter(expression) {
|
|
25
|
-
return { kind: 'expression', expression };
|
|
26
|
-
},
|
|
27
|
-
raw(sql, params = []) {
|
|
28
|
-
return { kind: 'raw', sql, params };
|
|
29
|
-
},
|
|
30
|
-
};
|