orez-sync-cf-host 0.5.19 → 0.5.21
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 +194 -0
- package/dist/transaction-query.d.ts +72 -0
- package/dist/transaction-query.js +379 -0
- package/dist/types.d.ts +159 -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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
export class IngestBreakerError extends Error {
|
|
2
|
+
error;
|
|
3
|
+
windowRows;
|
|
4
|
+
budget;
|
|
5
|
+
retryAfterMs;
|
|
6
|
+
status = 429;
|
|
7
|
+
constructor(error, windowRows, budget, retryAfterMs) {
|
|
8
|
+
super(`${error}: ${windowRows}/${budget} rows; retry after ${retryAfterMs}ms`);
|
|
9
|
+
this.error = error;
|
|
10
|
+
this.windowRows = windowRows;
|
|
11
|
+
this.budget = budget;
|
|
12
|
+
this.retryAfterMs = retryAfterMs;
|
|
13
|
+
this.name = 'IngestBreakerError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class IngestCircuitBreaker {
|
|
17
|
+
#options;
|
|
18
|
+
#samples = [];
|
|
19
|
+
#billableRows = 0;
|
|
20
|
+
#logicalRows = 0;
|
|
21
|
+
#reason = null;
|
|
22
|
+
#retryAt = null;
|
|
23
|
+
#consecutiveTrips = 0;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
for (const [name, value] of Object.entries(options)) {
|
|
26
|
+
if (name === 'now')
|
|
27
|
+
continue;
|
|
28
|
+
if (!Number.isSafeInteger(value) || Number(value) < 1)
|
|
29
|
+
throw new TypeError(`${name} must be a positive safe integer`);
|
|
30
|
+
}
|
|
31
|
+
this.#options = options;
|
|
32
|
+
}
|
|
33
|
+
#prune(now) {
|
|
34
|
+
const cutoff = now - this.#options.windowMs;
|
|
35
|
+
let remove = 0;
|
|
36
|
+
while (remove < this.#samples.length && this.#samples[remove].at <= cutoff) {
|
|
37
|
+
this.#billableRows -= this.#samples[remove].billableRows;
|
|
38
|
+
this.#logicalRows -= this.#samples[remove].logicalRows;
|
|
39
|
+
remove++;
|
|
40
|
+
}
|
|
41
|
+
if (remove > 0)
|
|
42
|
+
this.#samples.splice(0, remove);
|
|
43
|
+
}
|
|
44
|
+
status() {
|
|
45
|
+
const now = this.#options.now();
|
|
46
|
+
this.#prune(now);
|
|
47
|
+
const retryAfterMs = Math.max(0, (this.#retryAt ?? now) - now);
|
|
48
|
+
return {
|
|
49
|
+
windowRows: this.#billableRows,
|
|
50
|
+
billableRows: this.#billableRows,
|
|
51
|
+
logicalRows: this.#logicalRows,
|
|
52
|
+
budget: this.#options.budgetRows,
|
|
53
|
+
windowMs: this.#options.windowMs,
|
|
54
|
+
tripped: this.#reason !== null && retryAfterMs > 0,
|
|
55
|
+
reason: this.#reason,
|
|
56
|
+
retryAt: this.#retryAt,
|
|
57
|
+
retryAfterMs,
|
|
58
|
+
consecutiveTrips: this.#consecutiveTrips,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
assertReady() {
|
|
62
|
+
const status = this.status();
|
|
63
|
+
if (status.tripped && status.reason) {
|
|
64
|
+
throw new IngestBreakerError(status.reason, status.windowRows, status.budget, status.retryAfterMs);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
record(rows) {
|
|
68
|
+
this.recordLogical(rows);
|
|
69
|
+
this.recordBillable(rows);
|
|
70
|
+
}
|
|
71
|
+
#sample(now) {
|
|
72
|
+
this.#prune(now);
|
|
73
|
+
const last = this.#samples[this.#samples.length - 1];
|
|
74
|
+
if (last?.at === now)
|
|
75
|
+
return last;
|
|
76
|
+
const sample = { at: now, billableRows: 0, logicalRows: 0 };
|
|
77
|
+
this.#samples.push(sample);
|
|
78
|
+
return sample;
|
|
79
|
+
}
|
|
80
|
+
recordLogical(rows) {
|
|
81
|
+
if (!Number.isSafeInteger(rows) || rows <= 0)
|
|
82
|
+
return;
|
|
83
|
+
const sample = this.#sample(this.#options.now());
|
|
84
|
+
sample.logicalRows += rows;
|
|
85
|
+
this.#logicalRows += rows;
|
|
86
|
+
}
|
|
87
|
+
recordBillable(rows) {
|
|
88
|
+
this.assertReady();
|
|
89
|
+
if (!Number.isSafeInteger(rows) || rows <= 0)
|
|
90
|
+
return;
|
|
91
|
+
const now = this.#options.now();
|
|
92
|
+
const sample = this.#sample(now);
|
|
93
|
+
sample.billableRows += rows;
|
|
94
|
+
this.#billableRows += rows;
|
|
95
|
+
if (this.#billableRows > this.#options.budgetRows)
|
|
96
|
+
this.trip('ingestBudgetExceeded');
|
|
97
|
+
}
|
|
98
|
+
trip(reason) {
|
|
99
|
+
const now = this.#options.now();
|
|
100
|
+
this.#consecutiveTrips++;
|
|
101
|
+
const delay = Math.min(this.#options.maxBackoffMs, this.#options.initialBackoffMs * 2 ** (this.#consecutiveTrips - 1));
|
|
102
|
+
this.#reason = reason;
|
|
103
|
+
this.#retryAt = now + delay;
|
|
104
|
+
throw new IngestBreakerError(reason, this.#billableRows, this.#options.budgetRows, delay);
|
|
105
|
+
}
|
|
106
|
+
recovered() {
|
|
107
|
+
this.#reason = null;
|
|
108
|
+
this.#retryAt = null;
|
|
109
|
+
this.#consecutiveTrips = 0;
|
|
110
|
+
}
|
|
111
|
+
restore(reason, retryAt, consecutiveTrips) {
|
|
112
|
+
if (!Number.isFinite(retryAt) || retryAt <= 0)
|
|
113
|
+
return;
|
|
114
|
+
this.#reason = reason;
|
|
115
|
+
this.#retryAt = retryAt;
|
|
116
|
+
this.#consecutiveTrips = Math.max(1, Math.floor(consecutiveTrips) || 1);
|
|
117
|
+
}
|
|
118
|
+
reopen() {
|
|
119
|
+
this.#samples = [];
|
|
120
|
+
this.#billableRows = 0;
|
|
121
|
+
this.#logicalRows = 0;
|
|
122
|
+
this.recovered();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export function trackBillableCursorRows(cursor, record) {
|
|
126
|
+
if (!cursor || typeof cursor !== 'object')
|
|
127
|
+
return cursor;
|
|
128
|
+
let accountedRows = 0;
|
|
129
|
+
const account = () => {
|
|
130
|
+
const current = Number(cursor.rowsWritten ?? 0);
|
|
131
|
+
if (!Number.isSafeInteger(current) || current <= accountedRows)
|
|
132
|
+
return;
|
|
133
|
+
const delta = current - accountedRows;
|
|
134
|
+
accountedRows = current;
|
|
135
|
+
record(delta);
|
|
136
|
+
};
|
|
137
|
+
const wrapIterator = (iterator) => {
|
|
138
|
+
const next = iterator.next.bind(iterator);
|
|
139
|
+
return {
|
|
140
|
+
next(...args) {
|
|
141
|
+
try {
|
|
142
|
+
return next(...args);
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
account();
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
[Symbol.iterator]() {
|
|
149
|
+
return this;
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
let proxy;
|
|
154
|
+
proxy = new Proxy(cursor, {
|
|
155
|
+
get(target, property) {
|
|
156
|
+
const value = Reflect.get(target, property, target);
|
|
157
|
+
if (property === Symbol.iterator)
|
|
158
|
+
return () => proxy;
|
|
159
|
+
if ((property === 'next' || property === 'one' || property === 'toArray') &&
|
|
160
|
+
typeof value === 'function') {
|
|
161
|
+
return (...args) => {
|
|
162
|
+
try {
|
|
163
|
+
return Reflect.apply(value, target, args);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
account();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (property === 'raw' && typeof value === 'function') {
|
|
171
|
+
return (...args) => wrapIterator(Reflect.apply(value, target, args));
|
|
172
|
+
}
|
|
173
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
account();
|
|
177
|
+
return proxy;
|
|
178
|
+
}
|
|
179
|
+
export function retryDelayMs(attempt, initialBackoffMs, maxBackoffMs) {
|
|
180
|
+
return Math.min(maxBackoffMs, initialBackoffMs * 2 ** Math.max(0, attempt - 1));
|
|
181
|
+
}
|
|
182
|
+
export function shouldRetryDelegatedPush(responseStatus, attempt, maxAttempts) {
|
|
183
|
+
if (attempt >= maxAttempts)
|
|
184
|
+
return false;
|
|
185
|
+
return responseStatus === null || responseStatus === 429 || responseStatus >= 500;
|
|
186
|
+
}
|
package/package.json
CHANGED
|
@@ -1,31 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orez-sync-cf-host",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
|
-
".":
|
|
7
|
-
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"./post-commit": {
|
|
11
|
+
"types": "./dist/post-commit.d.ts",
|
|
12
|
+
"import": "./dist/post-commit.js"
|
|
13
|
+
},
|
|
8
14
|
"./mutation-error": {
|
|
9
|
-
"types": "./
|
|
10
|
-
"import": "./
|
|
15
|
+
"types": "./dist/mutation-error.d.ts",
|
|
16
|
+
"import": "./dist/mutation-error.js"
|
|
17
|
+
},
|
|
18
|
+
"./node-wasm-loader": {
|
|
19
|
+
"types": "./dist/node-wasm-loader.d.ts",
|
|
20
|
+
"import": "./dist/node-wasm-loader.js"
|
|
21
|
+
},
|
|
22
|
+
"./bun-wasm-loader": {
|
|
23
|
+
"types": "./dist/bun-wasm-loader.d.ts",
|
|
24
|
+
"import": "./dist/bun-wasm-loader.js"
|
|
25
|
+
},
|
|
26
|
+
"./query-compiler": {
|
|
27
|
+
"types": "./dist/query-compiler.d.ts",
|
|
28
|
+
"import": "./dist/query-compiler.js"
|
|
11
29
|
},
|
|
12
|
-
"./
|
|
13
|
-
|
|
14
|
-
|
|
30
|
+
"./transaction-query": {
|
|
31
|
+
"types": "./dist/transaction-query.d.ts",
|
|
32
|
+
"import": "./dist/transaction-query.js"
|
|
33
|
+
},
|
|
34
|
+
"./vite-wasm-loader": {
|
|
35
|
+
"types": "./dist/vite-wasm-loader.d.ts",
|
|
36
|
+
"import": "./dist/vite-wasm-loader.js"
|
|
37
|
+
},
|
|
38
|
+
"./wasm-module.wasm": {
|
|
39
|
+
"types": "./dist/generated/sync_wasm_bg.wasm.d.ts",
|
|
40
|
+
"import": "./dist/generated/sync_wasm_bg.wasm"
|
|
41
|
+
}
|
|
15
42
|
},
|
|
16
43
|
"scripts": {
|
|
17
44
|
"build:wasm": "wasm-pack build ../../crates/sync-wasm --target web --release --out-dir ../../packages/sync-cf-host/src/generated --out-name sync_wasm && rm -f src/generated/.gitignore && bun scripts/write-wasm-module-type.ts",
|
|
18
45
|
"build:wasm:platform": "wasm-pack build ../../crates/sync-wasm --target web --release --out-dir ../../packages/sync-cf-host/src/generated --out-name sync_wasm -- --features platform-probes && rm -f src/generated/.gitignore && bun scripts/write-wasm-module-type.ts",
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"test
|
|
25
|
-
"test:
|
|
26
|
-
"test:
|
|
27
|
-
"test:
|
|
28
|
-
"
|
|
46
|
+
"build:runtime": "rm -rf dist && mkdir -p dist && cp -R src/generated dist/generated && tsc -p tsconfig.build.json",
|
|
47
|
+
"build:dist": "bun run build:wasm && bun run build:runtime",
|
|
48
|
+
"build:dist:platform": "bun run build:wasm:platform && bun run build:runtime",
|
|
49
|
+
"bundle": "bun run build:dist && wrangler deploy --dry-run --outdir .wrangler/bundle",
|
|
50
|
+
"measure": "bun run build:dist && bun measure.mjs",
|
|
51
|
+
"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",
|
|
52
|
+
"test:config": "bun test config.test.mjs write-safeguards.test.mjs mutation-error.test.mjs post-commit.test.mjs transaction-query.test.mjs",
|
|
53
|
+
"test:integration": "bun run build:dist && bun integration-test.mjs",
|
|
54
|
+
"test:ingest": "bun run build:dist && bun ingest-test.mjs",
|
|
55
|
+
"test:package": "bun run build:dist && node package-runtime-test.mjs",
|
|
56
|
+
"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",
|
|
57
|
+
"test:restart": "bun run build:dist && bun restart-test.mjs",
|
|
58
|
+
"test:platform": "bun run build:dist:platform && bun platform-test.mjs",
|
|
59
|
+
"typecheck": "bun run build:dist:platform && tsc --noEmit"
|
|
29
60
|
},
|
|
30
61
|
"devDependencies": {
|
|
31
62
|
"@cloudflare/workers-types": "4.20260617.1",
|
|
@@ -35,7 +66,16 @@
|
|
|
35
66
|
"workerd": "1.20260617.1",
|
|
36
67
|
"wrangler": "4.103.0"
|
|
37
68
|
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"vite": ">=5"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"vite": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
38
77
|
"files": [
|
|
39
|
-
"
|
|
78
|
+
"dist",
|
|
79
|
+
"README.md"
|
|
40
80
|
]
|
|
41
81
|
}
|
package/src/bun-wasm-loader.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Buffer } from 'node:buffer'
|
|
2
|
-
|
|
3
|
-
import { file, plugin } from 'bun'
|
|
4
|
-
|
|
5
|
-
plugin({
|
|
6
|
-
name: 'orez-sync-cf-host-wasm',
|
|
7
|
-
setup(build) {
|
|
8
|
-
build.onLoad({ filter: /sync_wasm_bg\.wasm$/ }, async ({ path }) => {
|
|
9
|
-
const bytes = Buffer.from(await file(path).arrayBuffer()).toString('base64')
|
|
10
|
-
return {
|
|
11
|
-
contents: `export default new WebAssembly.Module(Uint8Array.fromBase64('${bytes}'))`,
|
|
12
|
-
loader: 'js',
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
},
|
|
16
|
-
})
|
package/src/config.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { PullCaps, SyncHostConfig, SyncHostEnv } from './types.js'
|
|
2
|
-
|
|
3
|
-
export function validatePullCaps(caps: PullCaps): PullCaps {
|
|
4
|
-
if (!Number.isSafeInteger(caps.maxChangeRows) || caps.maxChangeRows < 1) {
|
|
5
|
-
throw new TypeError('caps.maxChangeRows must be a positive safe integer')
|
|
6
|
-
}
|
|
7
|
-
return caps
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function validateSyncHostConfig<Env extends SyncHostEnv>(
|
|
11
|
-
config: SyncHostConfig<Env>
|
|
12
|
-
): SyncHostConfig<Env> {
|
|
13
|
-
if (typeof config.authorizeWake !== 'function') {
|
|
14
|
-
throw new TypeError('sync host config authorizeWake is required')
|
|
15
|
-
}
|
|
16
|
-
if (typeof config.authorizeNotify !== 'function') {
|
|
17
|
-
throw new TypeError('sync host config authorizeNotify is required')
|
|
18
|
-
}
|
|
19
|
-
const hasMutators = config.mutators !== undefined
|
|
20
|
-
const hasDelegate = config.mutateUrl !== undefined
|
|
21
|
-
if (hasMutators === hasDelegate) {
|
|
22
|
-
throw new TypeError('sync host config requires exactly one of mutators or mutateUrl')
|
|
23
|
-
}
|
|
24
|
-
if (hasDelegate && !config.upstream) {
|
|
25
|
-
throw new TypeError('sync host config mutateUrl requires upstream')
|
|
26
|
-
}
|
|
27
|
-
if (config.mutateBinding !== undefined && !hasDelegate) {
|
|
28
|
-
throw new TypeError('sync host config mutateBinding requires mutateUrl')
|
|
29
|
-
}
|
|
30
|
-
if (config.mutateOrigin !== undefined && !hasDelegate) {
|
|
31
|
-
throw new TypeError('sync host config mutateOrigin requires mutateUrl')
|
|
32
|
-
}
|
|
33
|
-
if (config.delegatedPushRetry !== undefined && !hasDelegate) {
|
|
34
|
-
throw new TypeError('sync host config delegatedPushRetry requires mutateUrl')
|
|
35
|
-
}
|
|
36
|
-
if (config.mutateBinding !== undefined && !config.mutateBinding) {
|
|
37
|
-
throw new TypeError('sync host config mutateBinding must not be empty')
|
|
38
|
-
}
|
|
39
|
-
if (hasMutators && config.upstream) {
|
|
40
|
-
throw new TypeError(
|
|
41
|
-
'sync host config cannot combine local mutators with upstream ingest'
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
if (config.mutateUrl && !config.mutateUrl.startsWith('/')) {
|
|
45
|
-
throw new TypeError('mutateUrl must be an absolute path')
|
|
46
|
-
}
|
|
47
|
-
if (config.mutateOrigin !== undefined) {
|
|
48
|
-
let origin: URL
|
|
49
|
-
try {
|
|
50
|
-
origin = new URL(config.mutateOrigin)
|
|
51
|
-
} catch {
|
|
52
|
-
throw new TypeError('mutateOrigin must be an absolute http(s) origin')
|
|
53
|
-
}
|
|
54
|
-
if (
|
|
55
|
-
(origin.protocol !== 'http:' && origin.protocol !== 'https:') ||
|
|
56
|
-
origin.origin !== config.mutateOrigin
|
|
57
|
-
) {
|
|
58
|
-
throw new TypeError('mutateOrigin must be an absolute http(s) origin')
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (config.upstream) {
|
|
62
|
-
if (!config.upstream.binding) throw new TypeError('upstream.binding is required')
|
|
63
|
-
const limit = config.upstream.changeLimit ?? 1_000
|
|
64
|
-
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 10_000) {
|
|
65
|
-
throw new TypeError('upstream.changeLimit must be a safe integer in 1..10000')
|
|
66
|
-
}
|
|
67
|
-
const interval = config.upstream.intervalMs ?? 15_000
|
|
68
|
-
if (!Number.isSafeInteger(interval) || interval < 1_000) {
|
|
69
|
-
throw new TypeError('upstream.intervalMs must be a safe integer >= 1000')
|
|
70
|
-
}
|
|
71
|
-
for (const [name, value] of Object.entries({
|
|
72
|
-
ingestBudgetRows: config.upstream.ingestBudgetRows ?? 150_000,
|
|
73
|
-
ingestBudgetWindowMs: config.upstream.ingestBudgetWindowMs ?? 300_000,
|
|
74
|
-
ingestBackoffMs: config.upstream.ingestBackoffMs ?? 1_000,
|
|
75
|
-
ingestMaxBackoffMs: config.upstream.ingestMaxBackoffMs ?? 60_000,
|
|
76
|
-
})) {
|
|
77
|
-
if (!Number.isSafeInteger(value) || value < 1)
|
|
78
|
-
throw new TypeError(`upstream.${name} must be a positive safe integer`)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (config.delegatedPushRetry) {
|
|
82
|
-
for (const [name, value] of Object.entries({
|
|
83
|
-
maxAttempts: config.delegatedPushRetry.maxAttempts ?? 3,
|
|
84
|
-
initialBackoffMs: config.delegatedPushRetry.initialBackoffMs ?? 100,
|
|
85
|
-
maxBackoffMs: config.delegatedPushRetry.maxBackoffMs ?? 1_000,
|
|
86
|
-
timeoutMs: config.delegatedPushRetry.timeoutMs ?? 5_000,
|
|
87
|
-
})) {
|
|
88
|
-
if (!Number.isSafeInteger(value) || value < 1)
|
|
89
|
-
throw new TypeError(`delegatedPushRetry.${name} must be a positive safe integer`)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (config.transactionQueryBudget) {
|
|
93
|
-
for (const [name, value] of Object.entries(config.transactionQueryBudget)) {
|
|
94
|
-
if (!Number.isSafeInteger(value) || Number(value) < 1) {
|
|
95
|
-
throw new TypeError(
|
|
96
|
-
`transactionQueryBudget.${name} must be a positive safe integer`
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return config
|
|
102
|
-
}
|