orez-sync-cf-host 0.5.19 → 0.5.20
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 +49 -6
- package/dist/bun-wasm-loader.d.ts +1 -0
- package/dist/bun-wasm-loader.js +20 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +93 -0
- package/{src → dist}/generated/sync_wasm_bg.wasm +0 -0
- package/dist/host.d.ts +12 -0
- package/dist/host.js +1512 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +4 -0
- package/dist/mutation-error.d.ts +9 -0
- package/dist/mutation-error.js +23 -0
- package/dist/node-wasm-loader.d.ts +1 -0
- package/dist/node-wasm-loader.js +22 -0
- package/dist/post-commit.d.ts +13 -0
- package/dist/post-commit.js +31 -0
- package/dist/query-compiler.d.ts +4 -0
- package/dist/query-compiler.js +4 -0
- package/dist/sql-storage-adapter.d.ts +69 -0
- package/dist/sql-storage-adapter.js +192 -0
- package/dist/transaction-query.d.ts +72 -0
- package/dist/transaction-query.js +379 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.js +5 -0
- package/dist/vite-wasm-loader.d.ts +3 -0
- package/dist/vite-wasm-loader.js +24 -0
- package/dist/wasm.d.ts +1 -0
- package/dist/wasm.js +7 -0
- package/dist/write-safeguards.d.ts +53 -0
- package/dist/write-safeguards.js +186 -0
- package/package.json +59 -19
- package/src/bun-wasm-loader.ts +0 -16
- package/src/config.ts +0 -102
- package/src/harness-config.ts +0 -487
- package/src/harness-worker.ts +0 -33
- package/src/host.ts +0 -1958
- package/src/index.ts +0 -47
- package/src/ingest-harness-worker.ts +0 -535
- package/src/mutation-error.ts +0 -28
- package/src/platform-probe-worker.ts +0 -418
- package/src/post-commit.ts +0 -40
- package/src/query-compiler.ts +0 -16
- package/src/sql-storage-adapter.ts +0 -254
- package/src/transaction-query.ts +0 -494
- package/src/types.ts +0 -195
- package/src/wasm-platform.ts +0 -10
- package/src/wasm.ts +0 -34
- package/src/write-safeguards.ts +0 -248
- /package/{src → dist}/generated/package.json +0 -0
- /package/{src → dist}/generated/sync_wasm.d.ts +0 -0
- /package/{src → dist}/generated/sync_wasm.js +0 -0
- /package/{src → dist}/generated/sync_wasm_bg.wasm.d.ts +0 -0
package/README.md
CHANGED
|
@@ -24,12 +24,33 @@ paths.
|
|
|
24
24
|
Transaction-query `ILIKE` folding is ASCII-only on Durable Object SQLite;
|
|
25
25
|
non-ASCII case pairs can diverge from PostgreSQL.
|
|
26
26
|
|
|
27
|
-
###
|
|
27
|
+
### Query compiler runtimes
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
`
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
Every runtime uses the same `orez-sync-cf-host/wasm-module.wasm` import and the same
|
|
30
|
+
`initSync` path. Configure the loader that matches the host:
|
|
31
|
+
|
|
32
|
+
| Host | Configuration | Module value |
|
|
33
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
34
|
+
| Workerd / Wrangler | Map the package's `wasm-module.wasm` export as a compiled Wasm module; do not install the Vite plugin | Cloudflare `CompiledWasm` |
|
|
35
|
+
| Bun | Preload `orez-sync-cf-host/bun-wasm-loader` | `WebAssembly.Module` compiled by the Bun loader |
|
|
36
|
+
| Vite serve / SSR development | Add `orezSyncCfHostWasm()` from `orez-sync-cf-host/vite-wasm-loader` | `WebAssembly.Module` built from package bytes by Vite |
|
|
37
|
+
| Direct Node >= 22.15 | Preload `orez-sync-cf-host/node-wasm-loader` with `NODE_OPTIONS=--import` | `WebAssembly.Module` compiled by the Node loader |
|
|
38
|
+
| Node production bundle | Keep the same Vite plugin active for the production SSR build | `WebAssembly.Module` built from bytes embedded in the bundle |
|
|
39
|
+
|
|
40
|
+
The Vite plugin also keeps `orez-sync-cf-host` inside Vite's SSR pipeline.
|
|
41
|
+
|
|
42
|
+
Wrangler consumers map the bare address rather than a filesystem glob. The
|
|
43
|
+
`.wasm` suffix is part of the public subpath because extension-keyed bundlers
|
|
44
|
+
classify the original import address:
|
|
45
|
+
|
|
46
|
+
```toml
|
|
47
|
+
[[rules]]
|
|
48
|
+
type = "CompiledWasm"
|
|
49
|
+
globs = ["orez-sync-cf-host/wasm-module.wasm"]
|
|
50
|
+
fallthrough = true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Bun consumers register the preload in `bunfig.toml`:
|
|
33
54
|
|
|
34
55
|
```toml
|
|
35
56
|
# bunfig.toml
|
|
@@ -38,7 +59,29 @@ preload = ["orez-sync-cf-host/bun-wasm-loader"]
|
|
|
38
59
|
|
|
39
60
|
For a single command, use
|
|
40
61
|
`bun --preload orez-sync-cf-host/bun-wasm-loader <command>`. The compiler throws
|
|
41
|
-
an error
|
|
62
|
+
an error pointing to this runtime matrix if a loader is missing.
|
|
63
|
+
|
|
64
|
+
Direct Node consumers on Node 22.15 or newer preload the synchronous module
|
|
65
|
+
hook. `NODE_OPTIONS` also carries the hook into test-runner child processes:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
NODE_OPTIONS=--import=orez-sync-cf-host/node-wasm-loader node app.js
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Node-targeted Vite consumers add the plugin once and leave it active in serve
|
|
72
|
+
and build:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { orezSyncCfHostWasm } from 'orez-sync-cf-host/vite-wasm-loader'
|
|
76
|
+
|
|
77
|
+
export default {
|
|
78
|
+
plugins: [orezSyncCfHostWasm()],
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If one Vite config targets both Node and Workerd, include this plugin only for
|
|
83
|
+
the Node target. The Workerd target uses the `CompiledWasm` mapping above and
|
|
84
|
+
must not run the Vite loader.
|
|
42
85
|
|
|
43
86
|
## Wake channel and eviction
|
|
44
87
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { file, plugin } from 'bun';
|
|
4
|
+
const wasmModuleID = 'orez-sync-cf-host/wasm-module.wasm';
|
|
5
|
+
const wasmModulePath = fileURLToPath(import.meta.resolve(wasmModuleID));
|
|
6
|
+
plugin({
|
|
7
|
+
name: 'orez-sync-cf-host-wasm',
|
|
8
|
+
setup(build) {
|
|
9
|
+
build.onResolve({ filter: /^orez-sync-cf-host\/wasm-module\.wasm$/ }, () => ({
|
|
10
|
+
path: wasmModulePath,
|
|
11
|
+
}));
|
|
12
|
+
build.onLoad({ filter: /sync_wasm_bg\.wasm$/ }, async ({ path }) => {
|
|
13
|
+
const bytes = Buffer.from(await file(path).arrayBuffer()).toString('base64');
|
|
14
|
+
return {
|
|
15
|
+
contents: `export default new WebAssembly.Module(Uint8Array.fromBase64('${bytes}'))`,
|
|
16
|
+
loader: 'js',
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
});
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
export function validateSyncHostConfig(config) {
|
|
8
|
+
if (typeof config.authorizeWake !== 'function') {
|
|
9
|
+
throw new TypeError('sync host config authorizeWake is required');
|
|
10
|
+
}
|
|
11
|
+
if (typeof config.authorizeNotify !== 'function') {
|
|
12
|
+
throw new TypeError('sync host config authorizeNotify is required');
|
|
13
|
+
}
|
|
14
|
+
const hasMutators = config.mutators !== undefined;
|
|
15
|
+
const hasDelegate = config.mutateUrl !== undefined;
|
|
16
|
+
if (hasMutators === hasDelegate) {
|
|
17
|
+
throw new TypeError('sync host config requires exactly one of mutators or mutateUrl');
|
|
18
|
+
}
|
|
19
|
+
if (hasDelegate && !config.upstream) {
|
|
20
|
+
throw new TypeError('sync host config mutateUrl requires upstream');
|
|
21
|
+
}
|
|
22
|
+
if (config.mutateBinding !== undefined && !hasDelegate) {
|
|
23
|
+
throw new TypeError('sync host config mutateBinding requires mutateUrl');
|
|
24
|
+
}
|
|
25
|
+
if (config.mutateOrigin !== undefined && !hasDelegate) {
|
|
26
|
+
throw new TypeError('sync host config mutateOrigin requires mutateUrl');
|
|
27
|
+
}
|
|
28
|
+
if (config.delegatedPushRetry !== undefined && !hasDelegate) {
|
|
29
|
+
throw new TypeError('sync host config delegatedPushRetry requires mutateUrl');
|
|
30
|
+
}
|
|
31
|
+
if (config.mutateBinding !== undefined && !config.mutateBinding) {
|
|
32
|
+
throw new TypeError('sync host config mutateBinding must not be empty');
|
|
33
|
+
}
|
|
34
|
+
if (hasMutators && config.upstream) {
|
|
35
|
+
throw new TypeError('sync host config cannot combine local mutators with upstream ingest');
|
|
36
|
+
}
|
|
37
|
+
if (config.mutateUrl && !config.mutateUrl.startsWith('/')) {
|
|
38
|
+
throw new TypeError('mutateUrl must be an absolute path');
|
|
39
|
+
}
|
|
40
|
+
if (config.mutateOrigin !== undefined) {
|
|
41
|
+
let origin;
|
|
42
|
+
try {
|
|
43
|
+
origin = new URL(config.mutateOrigin);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new TypeError('mutateOrigin must be an absolute http(s) origin');
|
|
47
|
+
}
|
|
48
|
+
if ((origin.protocol !== 'http:' && origin.protocol !== 'https:') ||
|
|
49
|
+
origin.origin !== config.mutateOrigin) {
|
|
50
|
+
throw new TypeError('mutateOrigin must be an absolute http(s) origin');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (config.upstream) {
|
|
54
|
+
if (!config.upstream.binding)
|
|
55
|
+
throw new TypeError('upstream.binding is required');
|
|
56
|
+
const limit = config.upstream.changeLimit ?? 1_000;
|
|
57
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 10_000) {
|
|
58
|
+
throw new TypeError('upstream.changeLimit must be a safe integer in 1..10000');
|
|
59
|
+
}
|
|
60
|
+
const interval = config.upstream.intervalMs ?? 15_000;
|
|
61
|
+
if (!Number.isSafeInteger(interval) || interval < 1_000) {
|
|
62
|
+
throw new TypeError('upstream.intervalMs must be a safe integer >= 1000');
|
|
63
|
+
}
|
|
64
|
+
for (const [name, value] of Object.entries({
|
|
65
|
+
ingestBudgetRows: config.upstream.ingestBudgetRows ?? 150_000,
|
|
66
|
+
ingestBudgetWindowMs: config.upstream.ingestBudgetWindowMs ?? 300_000,
|
|
67
|
+
ingestBackoffMs: config.upstream.ingestBackoffMs ?? 1_000,
|
|
68
|
+
ingestMaxBackoffMs: config.upstream.ingestMaxBackoffMs ?? 60_000,
|
|
69
|
+
})) {
|
|
70
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
71
|
+
throw new TypeError(`upstream.${name} must be a positive safe integer`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (config.delegatedPushRetry) {
|
|
75
|
+
for (const [name, value] of Object.entries({
|
|
76
|
+
maxAttempts: config.delegatedPushRetry.maxAttempts ?? 3,
|
|
77
|
+
initialBackoffMs: config.delegatedPushRetry.initialBackoffMs ?? 100,
|
|
78
|
+
maxBackoffMs: config.delegatedPushRetry.maxBackoffMs ?? 1_000,
|
|
79
|
+
timeoutMs: config.delegatedPushRetry.timeoutMs ?? 5_000,
|
|
80
|
+
})) {
|
|
81
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
82
|
+
throw new TypeError(`delegatedPushRetry.${name} must be a positive safe integer`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (config.transactionQueryBudget) {
|
|
86
|
+
for (const [name, value] of Object.entries(config.transactionQueryBudget)) {
|
|
87
|
+
if (!Number.isSafeInteger(value) || Number(value) < 1) {
|
|
88
|
+
throw new TypeError(`transactionQueryBudget.${name} must be a positive safe integer`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return config;
|
|
93
|
+
}
|
|
Binary file
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DurableObject } from 'cloudflare:workers';
|
|
2
|
+
import type { SyncHostConfig, SyncHostEnv } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create the consumer-facing Worker router. Authentication happens here; the
|
|
5
|
+
* Durable Object receives only normalized claims over a binding-private header.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createSyncWorker<Env extends SyncHostEnv>(config: SyncHostConfig<Env>): ExportedHandler<Env>;
|
|
8
|
+
export interface SyncDurableObjectConstructor<Env extends SyncHostEnv> {
|
|
9
|
+
new (ctx: DurableObjectState, env: Env): DurableObject<Env>;
|
|
10
|
+
}
|
|
11
|
+
/** Create the namespace Durable Object class for one bundled consumer config. */
|
|
12
|
+
export declare function createSyncDurableObject<Env extends SyncHostEnv>(config: SyncHostConfig<Env>): SyncDurableObjectConstructor<Env>;
|