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.
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 +194 -0
  21. package/dist/transaction-query.d.ts +72 -0
  22. package/dist/transaction-query.js +379 -0
  23. package/dist/types.d.ts +159 -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
@@ -1,494 +0,0 @@
1
- export type TransactionQueryWireValue =
2
- | { kind: 'null' }
3
- | { kind: 'integer'; value: string }
4
- | { kind: 'real'; value: number }
5
- | { kind: 'text'; value: string }
6
- | { kind: 'blob'; value: number[] }
7
-
8
- export type TransactionQueryBinding =
9
- | { kind: 'literal'; value: TransactionQueryWireValue }
10
- | { kind: 'parent_field'; field: string }
11
-
12
- export type TransactionQueryColumnType = 'string' | 'number' | 'boolean' | 'json' | 'null'
13
-
14
- export type TransactionQueryColumn = {
15
- name: string
16
- columnType: TransactionQueryColumnType
17
- }
18
-
19
- export type CompiledTransactionQueryRelationship = {
20
- name: string
21
- node: CompiledTransactionQueryNode
22
- }
23
-
24
- export type CompiledTransactionQueryNode = {
25
- table: string
26
- singular: boolean
27
- sql: string
28
- bindings: TransactionQueryBinding[]
29
- columns: TransactionQueryColumn[]
30
- relationships: CompiledTransactionQueryRelationship[]
31
- }
32
-
33
- export type CompiledTransactionQueryPlan = {
34
- rootTable: string
35
- planHash: string
36
- root: CompiledTransactionQueryNode
37
- }
38
-
39
- export type TransactionQueryBudget = {
40
- maxSelects: number
41
- maxRows: number
42
- }
43
-
44
- export type TransactionQueryExecutionOptions = {
45
- queryName?: string
46
- budget?: Partial<TransactionQueryBudget>
47
- }
48
-
49
- export type TransactionQueryFormat = {
50
- singular: boolean
51
- relationships: Readonly<Record<string, TransactionQueryFormat>>
52
- }
53
-
54
- export const DEFAULT_TRANSACTION_QUERY_BUDGET: TransactionQueryBudget = {
55
- maxSelects: 256,
56
- maxRows: 10_000,
57
- }
58
-
59
- export class TransactionQueryBudgetError extends Error {
60
- readonly code = 'transaction_query_budget_exceeded'
61
-
62
- constructor(
63
- readonly query: string,
64
- readonly selects: number,
65
- readonly rows: number,
66
- readonly maxSelects: number,
67
- readonly maxRows: number
68
- ) {
69
- super(
70
- `transaction_query_budget_exceeded: query=${query} selects=${selects} rows=${rows} maxSelects=${maxSelects} maxRows=${maxRows}`
71
- )
72
- this.name = 'TransactionQueryBudgetError'
73
- }
74
- }
75
-
76
- type MaterializedRow = Record<string, unknown>
77
- type ExecuteSelect = (
78
- sql: string,
79
- params: readonly unknown[]
80
- ) => readonly MaterializedRow[]
81
- type ExecuteSelectAsync = (
82
- sql: string,
83
- params: readonly unknown[]
84
- ) => readonly MaterializedRow[] | Promise<readonly MaterializedRow[]>
85
-
86
- const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER)
87
- const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER)
88
- const PLAN_HASH = /^[0-9a-f]{16}$/
89
- const COLUMN_TYPES = new Set<TransactionQueryColumnType>([
90
- 'string',
91
- 'number',
92
- 'boolean',
93
- 'json',
94
- 'null',
95
- ])
96
-
97
- function isObject(value: unknown): value is Record<string, unknown> {
98
- return typeof value === 'object' && value !== null && !Array.isArray(value)
99
- }
100
-
101
- function malformed(detail: string): TypeError {
102
- return new TypeError(`compiled transaction query plan ${detail}`)
103
- }
104
-
105
- function assertWireValue(value: unknown, path: string): TransactionQueryWireValue {
106
- if (!isObject(value) || typeof value.kind !== 'string') {
107
- throw malformed(`${path} has an invalid literal`)
108
- }
109
- switch (value.kind) {
110
- case 'null':
111
- return value as TransactionQueryWireValue
112
- case 'integer':
113
- if (typeof value.value !== 'string' || !/^-?[0-9]+$/.test(value.value)) {
114
- throw malformed(`${path} has an invalid integer literal`)
115
- }
116
- return value as TransactionQueryWireValue
117
- case 'real':
118
- if (typeof value.value !== 'number' || !Number.isFinite(value.value)) {
119
- throw malformed(`${path} has an invalid real literal`)
120
- }
121
- return value as TransactionQueryWireValue
122
- case 'text':
123
- if (typeof value.value !== 'string') {
124
- throw malformed(`${path} has an invalid text literal`)
125
- }
126
- return value as TransactionQueryWireValue
127
- case 'blob':
128
- if (
129
- !Array.isArray(value.value) ||
130
- value.value.some(
131
- (byte) => !Number.isInteger(byte) || Number(byte) < 0 || Number(byte) > 255
132
- )
133
- ) {
134
- throw malformed(`${path} has an invalid blob literal`)
135
- }
136
- return value as TransactionQueryWireValue
137
- default:
138
- throw malformed(`${path} has an unknown literal kind`)
139
- }
140
- }
141
-
142
- function assertNode(
143
- value: unknown,
144
- path: string,
145
- depth: number
146
- ): asserts value is CompiledTransactionQueryNode {
147
- if (depth > 64) throw malformed(`${path} exceeds the maximum relationship depth`)
148
- if (!isObject(value)) throw malformed(`${path} must be an object`)
149
- if (typeof value.table !== 'string' || value.table.length === 0) {
150
- throw malformed(`${path}.table must be a non-empty string`)
151
- }
152
- if (typeof value.singular !== 'boolean') {
153
- throw malformed(`${path}.singular must be a boolean`)
154
- }
155
- if (typeof value.sql !== 'string' || value.sql.length === 0) {
156
- throw malformed(`${path}.sql must be a non-empty string`)
157
- }
158
- if (!Array.isArray(value.bindings)) {
159
- throw malformed(`${path}.bindings must be an array`)
160
- }
161
- for (const [index, binding] of value.bindings.entries()) {
162
- if (!isObject(binding)) throw malformed(`${path}.bindings[${index}] is invalid`)
163
- if (binding.kind === 'literal') {
164
- assertWireValue(binding.value, `${path}.bindings[${index}]`)
165
- } else if (
166
- binding.kind !== 'parent_field' ||
167
- typeof binding.field !== 'string' ||
168
- binding.field.length === 0
169
- ) {
170
- throw malformed(`${path}.bindings[${index}] is invalid`)
171
- }
172
- }
173
- if (!Array.isArray(value.columns) || value.columns.length === 0) {
174
- throw malformed(`${path}.columns must be a non-empty array`)
175
- }
176
- const names = new Set<string>()
177
- for (const [index, column] of value.columns.entries()) {
178
- if (
179
- !isObject(column) ||
180
- typeof column.name !== 'string' ||
181
- column.name.length === 0 ||
182
- typeof column.columnType !== 'string' ||
183
- !COLUMN_TYPES.has(column.columnType as TransactionQueryColumnType)
184
- ) {
185
- throw malformed(`${path}.columns[${index}] is invalid`)
186
- }
187
- if (names.has(column.name)) {
188
- throw malformed(`${path}.columns contains duplicate '${column.name}'`)
189
- }
190
- names.add(column.name)
191
- }
192
- if (!Array.isArray(value.relationships)) {
193
- throw malformed(`${path}.relationships must be an array`)
194
- }
195
- const relationships = new Set<string>()
196
- for (const [index, relationship] of value.relationships.entries()) {
197
- if (
198
- !isObject(relationship) ||
199
- typeof relationship.name !== 'string' ||
200
- relationship.name.length === 0
201
- ) {
202
- throw malformed(`${path}.relationships[${index}] is invalid`)
203
- }
204
- if (relationships.has(relationship.name) || names.has(relationship.name)) {
205
- throw malformed(`${path}.relationships contains conflicting '${relationship.name}'`)
206
- }
207
- relationships.add(relationship.name)
208
- assertNode(relationship.node, `${path}.relationships[${index}].node`, depth + 1)
209
- }
210
- }
211
-
212
- function assertPlan(value: unknown): asserts value is CompiledTransactionQueryPlan {
213
- if (!isObject(value)) throw malformed('must be an object')
214
- if (typeof value.rootTable !== 'string' || value.rootTable.length === 0) {
215
- throw malformed('rootTable must be a non-empty string')
216
- }
217
- if (typeof value.planHash !== 'string' || !PLAN_HASH.test(value.planHash)) {
218
- throw malformed('planHash must be 16 lowercase hexadecimal characters')
219
- }
220
- assertNode(value.root, 'root', 0)
221
- if (value.root.table !== value.rootTable) {
222
- throw malformed('root.table must match rootTable')
223
- }
224
- }
225
-
226
- function resolveBudget(
227
- budget: Partial<TransactionQueryBudget> | undefined
228
- ): TransactionQueryBudget {
229
- const resolved = { ...DEFAULT_TRANSACTION_QUERY_BUDGET, ...budget }
230
- for (const [name, value] of Object.entries(resolved)) {
231
- if (!Number.isSafeInteger(value) || value < 1) {
232
- throw new TypeError(
233
- `transaction query budget ${name} must be a positive safe integer`
234
- )
235
- }
236
- }
237
- return resolved
238
- }
239
-
240
- function decodeWireValue(value: TransactionQueryWireValue): unknown {
241
- switch (value.kind) {
242
- case 'null':
243
- return null
244
- case 'integer': {
245
- const exact = BigInt(value.value)
246
- return exact >= MIN_SAFE && exact <= MAX_SAFE ? Number(exact) : value.value
247
- }
248
- case 'real':
249
- case 'text':
250
- return value.value
251
- case 'blob':
252
- return Uint8Array.from(value.value)
253
- }
254
- }
255
-
256
- function bytes(value: unknown): Uint8Array | undefined {
257
- if (value instanceof ArrayBuffer) return new Uint8Array(value)
258
- if (ArrayBuffer.isView(value)) {
259
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength)
260
- }
261
- return undefined
262
- }
263
-
264
- function decodeColumn(
265
- value: unknown,
266
- columnType: TransactionQueryColumnType,
267
- path: string
268
- ): unknown {
269
- if (value === null) return null
270
- switch (columnType) {
271
- case 'string':
272
- if (typeof value === 'string') return value
273
- break
274
- case 'number':
275
- if (typeof value === 'number' && Number.isFinite(value)) return value
276
- if (typeof value === 'bigint' && value >= MIN_SAFE && value <= MAX_SAFE) {
277
- return Number(value)
278
- }
279
- break
280
- case 'boolean':
281
- if (typeof value === 'boolean') return value
282
- if (value === 0 || value === 0n || value === '0') return false
283
- if (value === 1 || value === 1n || value === '1') return true
284
- break
285
- case 'json': {
286
- if (typeof value === 'string') {
287
- try {
288
- return JSON.parse(value)
289
- } catch {
290
- throw new TypeError(`transaction query row ${path} contains invalid JSON`)
291
- }
292
- }
293
- const encoded = bytes(value)
294
- if (encoded) {
295
- try {
296
- return JSON.parse(new TextDecoder().decode(encoded))
297
- } catch {
298
- throw new TypeError(`transaction query row ${path} contains invalid JSON`)
299
- }
300
- }
301
- if (typeof value === 'object') return value
302
- break
303
- }
304
- case 'null':
305
- break
306
- }
307
- throw new TypeError(
308
- `transaction query row ${path} does not match schema type ${columnType}`
309
- )
310
- }
311
-
312
- function decodeRow(
313
- raw: MaterializedRow,
314
- node: CompiledTransactionQueryNode
315
- ): MaterializedRow {
316
- const decoded: MaterializedRow = {}
317
- for (const column of node.columns) {
318
- if (!Object.hasOwn(raw, column.name)) {
319
- throw new TypeError(
320
- `transaction query row for ${node.table} is missing column ${column.name}`
321
- )
322
- }
323
- decoded[column.name] = decodeColumn(
324
- raw[column.name],
325
- column.columnType,
326
- `${node.table}.${column.name}`
327
- )
328
- }
329
- return decoded
330
- }
331
-
332
- type TransactionQueryExecutionState = {
333
- budget: TransactionQueryBudget
334
- query: string
335
- rows: number
336
- selects: number
337
- }
338
-
339
- type PreparedSelect = {
340
- params: unknown[]
341
- state: TransactionQueryExecutionState
342
- }
343
-
344
- type DecodedSelect = {
345
- rows: Array<{ decoded: MaterializedRow; raw: MaterializedRow }>
346
- state: TransactionQueryExecutionState
347
- }
348
-
349
- type NodeExecution = {
350
- state: TransactionQueryExecutionState
351
- value: MaterializedRow[] | MaterializedRow | null | undefined
352
- }
353
-
354
- function createExecutionState(
355
- plan: CompiledTransactionQueryPlan,
356
- options: TransactionQueryExecutionOptions
357
- ): TransactionQueryExecutionState {
358
- assertPlan(plan)
359
- return {
360
- budget: resolveBudget(options.budget),
361
- query: options.queryName?.trim() || `${plan.rootTable}:${plan.planHash}`,
362
- rows: 0,
363
- selects: 0,
364
- }
365
- }
366
-
367
- function assertWithinBudget(state: TransactionQueryExecutionState): void {
368
- if (state.selects <= state.budget.maxSelects && state.rows <= state.budget.maxRows) {
369
- return
370
- }
371
- throw new TransactionQueryBudgetError(
372
- state.query,
373
- state.selects,
374
- state.rows,
375
- state.budget.maxSelects,
376
- state.budget.maxRows
377
- )
378
- }
379
-
380
- function prepareSelect(
381
- state: TransactionQueryExecutionState,
382
- node: CompiledTransactionQueryNode,
383
- parent?: MaterializedRow
384
- ): PreparedSelect {
385
- const params = node.bindings.map((binding) => {
386
- if (binding.kind === 'literal') return decodeWireValue(binding.value)
387
- if (!parent || !Object.hasOwn(parent, binding.field)) {
388
- throw new TypeError(
389
- `transaction query parent row for ${node.table} is missing field ${binding.field}`
390
- )
391
- }
392
- return parent[binding.field]
393
- })
394
- const next = { ...state, selects: state.selects + 1 }
395
- assertWithinBudget(next)
396
- return { params, state: next }
397
- }
398
-
399
- function decodeSelect(
400
- state: TransactionQueryExecutionState,
401
- node: CompiledTransactionQueryNode,
402
- materialized: readonly MaterializedRow[]
403
- ): DecodedSelect {
404
- if (!Array.isArray(materialized)) {
405
- throw new TypeError('transaction query select must return a materialized row array')
406
- }
407
- const next = { ...state, rows: state.rows + materialized.length }
408
- assertWithinBudget(next)
409
- const rows = materialized.map((raw) => {
410
- if (!isObject(raw)) {
411
- throw new TypeError(
412
- `transaction query select for ${node.table} returned a non-object row`
413
- )
414
- }
415
- return { decoded: decodeRow(raw, node), raw }
416
- })
417
- return { rows, state: next }
418
- }
419
-
420
- function shapeRows(
421
- node: CompiledTransactionQueryNode,
422
- rows: MaterializedRow[]
423
- ): NodeExecution['value'] {
424
- return node.singular ? rows[0] : rows
425
- }
426
-
427
- export function executeTransactionQueryPlan<Result = unknown>(
428
- plan: CompiledTransactionQueryPlan,
429
- execute: ExecuteSelect,
430
- options: TransactionQueryExecutionOptions = {}
431
- ): Result {
432
- const run = (
433
- node: CompiledTransactionQueryNode,
434
- state: TransactionQueryExecutionState,
435
- parent?: MaterializedRow
436
- ): NodeExecution => {
437
- const prepared = prepareSelect(state, node, parent)
438
- const selected = decodeSelect(
439
- prepared.state,
440
- node,
441
- execute(node.sql, prepared.params)
442
- )
443
- const hydrated: MaterializedRow[] = []
444
- let current = selected.state
445
- for (const { decoded, raw } of selected.rows) {
446
- for (const relationship of node.relationships) {
447
- const related = run(relationship.node, current, raw)
448
- current = related.state
449
- decoded[relationship.name] =
450
- relationship.node.singular && related.value === undefined ? null : related.value
451
- }
452
- hydrated.push(decoded)
453
- }
454
- return { state: current, value: shapeRows(node, hydrated) }
455
- }
456
-
457
- const state = createExecutionState(plan, options)
458
- return run(plan.root, state).value as Result
459
- }
460
-
461
- export async function executeTransactionQueryPlanAsync<Result = unknown>(
462
- plan: CompiledTransactionQueryPlan,
463
- execute: ExecuteSelectAsync,
464
- options: TransactionQueryExecutionOptions = {}
465
- ): Promise<Result> {
466
- const run = async (
467
- node: CompiledTransactionQueryNode,
468
- state: TransactionQueryExecutionState,
469
- parent?: MaterializedRow
470
- ): Promise<NodeExecution> => {
471
- const prepared = prepareSelect(state, node, parent)
472
- const selected = decodeSelect(
473
- prepared.state,
474
- node,
475
- await execute(node.sql, prepared.params)
476
- )
477
- const hydrated: MaterializedRow[] = []
478
- let current = selected.state
479
- for (const { decoded, raw } of selected.rows) {
480
- for (const relationship of node.relationships) {
481
- const related = await run(relationship.node, current, raw)
482
- current = related.state
483
- decoded[relationship.name] =
484
- relationship.node.singular && related.value === undefined ? null : related.value
485
- }
486
- hydrated.push(decoded)
487
- }
488
- return { state: current, value: shapeRows(node, hydrated) }
489
- }
490
-
491
- const state = createExecutionState(plan, options)
492
- const { value } = await run(plan.root, state)
493
- return value as Result
494
- }
package/src/types.ts DELETED
@@ -1,195 +0,0 @@
1
- import type { DeferredEffect } from './post-commit.js'
2
- import type {
3
- TransactionQueryBudget,
4
- TransactionQueryFormat,
5
- } from './transaction-query.js'
6
-
7
- export type { DeferredEffect } from './post-commit.js'
8
-
9
- export type JsonPrimitive = string | number | boolean | null
10
- export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue }
11
-
12
- export type NormalizedClaims = {
13
- /** Stable consumer user id used for client-group ownership. */
14
- userID: string
15
- [claim: string]: JsonValue
16
- }
17
-
18
- export type ZeroSchemaConfig = {
19
- readonly tables: Readonly<
20
- Record<
21
- string,
22
- {
23
- readonly name?: string
24
- readonly serverName?: string
25
- readonly columns: Readonly<
26
- Record<string, { readonly type: string; readonly serverName?: string }>
27
- >
28
- readonly primaryKey: readonly string[]
29
- }
30
- >
31
- >
32
- }
33
-
34
- export interface SyncSql {
35
- exec(sql: string, params?: readonly unknown[]): void
36
- query<Row extends Record<string, unknown> = Record<string, unknown>>(
37
- sql: string,
38
- params?: readonly unknown[]
39
- ): Row[]
40
- }
41
-
42
- export interface MutatorSql {
43
- exec(sql: string, params?: readonly unknown[]): Promise<void>
44
- query<Row extends Record<string, unknown> = Record<string, unknown>>(
45
- sql: string,
46
- params?: readonly unknown[]
47
- ): Promise<Row[]>
48
- /** Execute a validated Zero AST inside the current application transaction. */
49
- queryAst<Result = unknown>(
50
- ast: JsonValue,
51
- format: TransactionQueryFormat,
52
- queryName?: string
53
- ): Promise<Result>
54
- }
55
-
56
- export type MutatorContext = {
57
- claims: NormalizedClaims
58
- /** Current Zero mutation identity, when invoked by the production host. */
59
- clientID?: string
60
- mutationID?: string | number
61
- defer(effect: DeferredEffect): void
62
- }
63
-
64
- export type RegisteredMutator = (
65
- tx: MutatorSql,
66
- args: JsonValue,
67
- context: MutatorContext
68
- ) => void | Promise<void>
69
-
70
- export type MutatorRegistry = Readonly<Record<string, RegisteredMutator>>
71
-
72
- /** Preserve mutator names while making the host-facing registry immutable. */
73
- export function registerMutators<
74
- const Registry extends Record<string, RegisteredMutator>,
75
- >(registry: Registry): Readonly<Registry> {
76
- return Object.freeze({ ...registry })
77
- }
78
-
79
- export { MutationApplicationError, isMutationApplicationError } from './mutation-error.js'
80
-
81
- export type VisibilityFilter = {
82
- /** SQL WHERE fragment only, without the WHERE keyword. */
83
- sql: string
84
- params?: readonly JsonPrimitive[]
85
- }
86
-
87
- export type VisibilityConfig = {
88
- /** True only when every predicate depends on the selected row alone. */
89
- rowLocal: boolean | ((claims: NormalizedClaims) => boolean)
90
- filter(table: string, claims: NormalizedClaims): VisibilityFilter | undefined
91
- }
92
-
93
- export type QueryResolver = (
94
- name: string,
95
- args: readonly JsonValue[],
96
- claims: NormalizedClaims,
97
- env: SyncHostEnv
98
- ) => JsonValue | Promise<JsonValue>
99
-
100
- export type PullCaps = {
101
- maxChangeRows: number
102
- maxChangeBytes: number
103
- }
104
-
105
- export interface SyncHostEnv {
106
- SYNC_DO: DurableObjectNamespace
107
- ADMIN_KEY?: string
108
- }
109
-
110
- export type ServiceBinding = {
111
- fetch(input: string | Request, init?: RequestInit): Promise<Response>
112
- }
113
-
114
- export type UpstreamConfig = {
115
- /** Env key for the service binding that owns the app write endpoint and feed. */
116
- binding: string
117
- /** Path to this namespace on the bound service (for example `/data/<id>`). */
118
- namespacePath: string | ((namespace: string) => string)
119
- /** Feed page size; the cursor loop continues until the reported head is reached. */
120
- changeLimit?: number
121
- /** Active wake-socket alarm safety net. Defaults to 15 seconds. */
122
- intervalMs?: number
123
- /** Billable SQLite rows written by ingest per rolling window. Defaults to 150,000. */
124
- ingestBudgetRows?: number
125
- /** Rolling ingest budget window. Defaults to five minutes. */
126
- ingestBudgetWindowMs?: number
127
- /** Initial breaker cooldown. Defaults to one second. */
128
- ingestBackoffMs?: number
129
- /** Maximum breaker cooldown. Defaults to one minute. */
130
- ingestMaxBackoffMs?: number
131
- }
132
-
133
- export type DelegatedPushRetryConfig = {
134
- /** Total attempts including the first request. Defaults to 3. */
135
- maxAttempts?: number
136
- /** Initial exponential delay. Defaults to 100ms. */
137
- initialBackoffMs?: number
138
- /** Delay cap. Defaults to 1,000ms. */
139
- maxBackoffMs?: number
140
- /** Per-attempt service-binding timeout. Defaults to 5,000ms. */
141
- timeoutMs?: number
142
- }
143
-
144
- export type SyncHostConfig<Env extends SyncHostEnv = SyncHostEnv> = {
145
- hostVersion: string
146
- schema: ZeroSchemaConfig
147
- mutators?: MutatorRegistry
148
- /**
149
- * absolute app push path on the delegated mutation service. a successful
150
- * response must be causally visible through the configured upstream data
151
- * feed before the app returns, because the host ingests effects before it
152
- * records the mutation's lmid.
153
- */
154
- mutateUrl?: string
155
- /** Absolute origin used for delegated push requests through the service binding. */
156
- mutateOrigin?: string
157
- /**
158
- * env binding for delegated pushes; defaults to upstream.binding. the bound
159
- * service and upstream feed must satisfy the mutateUrl causality contract.
160
- */
161
- mutateBinding?: string
162
- delegatedPushRetry?: DelegatedPushRetryConfig
163
- /** Required for delegated push; forbidden with local mutators (no dual apply). */
164
- upstream?: UpstreamConfig
165
- /** Application DDL and optional seed, called before sync-core schema init. */
166
- initialize(sql: SyncSql): void
167
- authenticate(
168
- request: Request,
169
- env: Env
170
- ): NormalizedClaims | null | Promise<NormalizedClaims | null>
171
- /** Authorize the advisory wake socket before selecting a namespace DO.
172
- * Browser clients should present a short-lived, namespace-scoped capability
173
- * in the query string because WebSocket cannot set request headers. */
174
- authorizeWake(request: Request, env: Env): boolean | Promise<boolean>
175
- /** Authorize upstream service notifications before selecting a namespace DO. */
176
- authorizeNotify(request: Request, env: Env): boolean | Promise<boolean>
177
- /** Resolve the first path component or another consumer-defined namespace. */
178
- namespace(request: Request): string | null
179
- visibility?: VisibilityConfig
180
- /** Enable desired-query pulls for this namespace and resolve named queries
181
- * into validated Zero ASTs before they reach sync-core. */
182
- queryAware?: boolean | ((claims: NormalizedClaims) => boolean)
183
- resolveQuery?: QueryResolver
184
- /** Server-owned invalidation epoch for permission/schema transforms. */
185
- queryTransformVersion?: number | ((claims: NormalizedClaims) => number)
186
- /** Enable consumer visibility from the first request. Defaults to false for harnesses. */
187
- visibilityEnabled?: boolean
188
- retainChanges?: number
189
- caps?: Partial<PullCaps>
190
- idleTeardownMs?: number
191
- wakeCoalesceMs?: number
192
- /** per-query guard for recursive transaction query materialization. */
193
- transactionQueryBudget?: Partial<TransactionQueryBudget>
194
- authorizeAdmin?: (request: Request, env: Env) => boolean | Promise<boolean>
195
- }
@@ -1,10 +0,0 @@
1
- import './wasm.js'
2
-
3
- export {
4
- init_probe_schema,
5
- pull_snapshot,
6
- push_finalize,
7
- push_preflight,
8
- rust_panic_after_writes,
9
- value_round_trip,
10
- } from './generated/sync_wasm.js'