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.
Files changed (52) hide show
  1. package/README.md +49 -6
  2. package/dist/bun-wasm-loader.d.ts +1 -0
  3. package/dist/bun-wasm-loader.js +20 -0
  4. package/dist/config.d.ts +3 -0
  5. package/dist/config.js +93 -0
  6. package/{src → dist}/generated/sync_wasm_bg.wasm +0 -0
  7. package/dist/host.d.ts +12 -0
  8. package/dist/host.js +1512 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.js +4 -0
  11. package/dist/mutation-error.d.ts +9 -0
  12. package/dist/mutation-error.js +23 -0
  13. package/dist/node-wasm-loader.d.ts +1 -0
  14. package/dist/node-wasm-loader.js +22 -0
  15. package/dist/post-commit.d.ts +13 -0
  16. package/dist/post-commit.js +31 -0
  17. package/dist/query-compiler.d.ts +4 -0
  18. package/dist/query-compiler.js +4 -0
  19. package/dist/sql-storage-adapter.d.ts +69 -0
  20. package/dist/sql-storage-adapter.js +192 -0
  21. package/dist/transaction-query.d.ts +72 -0
  22. package/dist/transaction-query.js +379 -0
  23. package/dist/types.d.ts +144 -0
  24. package/dist/types.js +5 -0
  25. package/dist/vite-wasm-loader.d.ts +3 -0
  26. package/dist/vite-wasm-loader.js +24 -0
  27. package/dist/wasm.d.ts +1 -0
  28. package/dist/wasm.js +7 -0
  29. package/dist/write-safeguards.d.ts +53 -0
  30. package/dist/write-safeguards.js +186 -0
  31. package/package.json +59 -19
  32. package/src/bun-wasm-loader.ts +0 -16
  33. package/src/config.ts +0 -102
  34. package/src/harness-config.ts +0 -487
  35. package/src/harness-worker.ts +0 -33
  36. package/src/host.ts +0 -1958
  37. package/src/index.ts +0 -47
  38. package/src/ingest-harness-worker.ts +0 -535
  39. package/src/mutation-error.ts +0 -28
  40. package/src/platform-probe-worker.ts +0 -418
  41. package/src/post-commit.ts +0 -40
  42. package/src/query-compiler.ts +0 -16
  43. package/src/sql-storage-adapter.ts +0 -254
  44. package/src/transaction-query.ts +0 -494
  45. package/src/types.ts +0 -195
  46. package/src/wasm-platform.ts +0 -10
  47. package/src/wasm.ts +0 -34
  48. package/src/write-safeguards.ts +0 -248
  49. /package/{src → dist}/generated/package.json +0 -0
  50. /package/{src → dist}/generated/sync_wasm.d.ts +0 -0
  51. /package/{src → dist}/generated/sync_wasm.js +0 -0
  52. /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
- ### Bun query compiler
27
+ ### Query compiler runtimes
28
28
 
29
- Wrangler loads the engine's `.wasm` import as a precompiled
30
- `WebAssembly.Module`. Bun consumers of `createQueryCompiler` must register the
31
- package loader so the same import has the same value without changing the
32
- compiler code:
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 naming this preload when Bun resolves the `.wasm` import as a pathname.
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
+ });
@@ -0,0 +1,3 @@
1
+ import type { PullCaps, SyncHostConfig, SyncHostEnv } from './types.js';
2
+ export declare function validatePullCaps(caps: PullCaps): PullCaps;
3
+ export declare function validateSyncHostConfig<Env extends SyncHostEnv>(config: SyncHostConfig<Env>): SyncHostConfig<Env>;
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
+ }
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>;