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,418 +0,0 @@
1
- import { ZeroDO } from '../../../src/cf-do/worker.js'
2
- import { createQueryCompiler } from './query-compiler.js'
3
- import {
4
- SqlStorageDirect,
5
- SqlStorageMutatorTransaction,
6
- SqlStorageSyncDb,
7
- } from './sql-storage-adapter.js'
8
- import {
9
- init_probe_schema,
10
- pull_snapshot,
11
- push_finalize,
12
- push_preflight,
13
- rust_panic_after_writes,
14
- value_round_trip,
15
- } from './wasm-platform.js'
16
-
17
- import type { TransactionQueryFormat } from './transaction-query.js'
18
- import type { ZeroSchemaConfig } from './types.js'
19
-
20
- interface Env {
21
- PROBE_DO: DurableObjectNamespace<ProbeDurableObject>
22
- }
23
-
24
- type DeferredEffect = { mutationID: string; kind: string }
25
- type MutatorName = 'read-then-write' | 'multi-table' | 'application-error'
26
-
27
- const transactionQuerySchema = {
28
- tables: {
29
- account: {
30
- name: 'account',
31
- serverName: 'accounts',
32
- columns: {
33
- id: { type: 'string' },
34
- balance: { type: 'number' },
35
- },
36
- primaryKey: ['id'],
37
- },
38
- entry: {
39
- name: 'entry',
40
- serverName: 'ledger',
41
- columns: {
42
- id: { type: 'number' },
43
- accountId: { type: 'string', serverName: 'account_id' },
44
- amount: { type: 'number' },
45
- note: { type: 'string' },
46
- },
47
- primaryKey: ['id'],
48
- },
49
- },
50
- } as const satisfies ZeroSchemaConfig
51
-
52
- const transactionQueryAst = {
53
- table: 'account',
54
- where: {
55
- type: 'simple',
56
- op: '=',
57
- left: { type: 'column', name: 'id' },
58
- right: { type: 'literal', value: 'primary' },
59
- },
60
- related: [
61
- {
62
- correlation: { parentField: ['id'], childField: ['accountId'] },
63
- subquery: { table: 'entry', alias: 'entries', orderBy: [['id', 'asc']] },
64
- },
65
- ],
66
- }
67
-
68
- const transactionQueryFormat = {
69
- singular: true,
70
- relationships: { entries: { singular: false, relationships: {} } },
71
- } as const satisfies TransactionQueryFormat
72
-
73
- const compileTransactionQuery = createQueryCompiler(transactionQuerySchema)
74
-
75
- // Deterministic local analogue of normal DO idle eviction, matching the
76
- // harness/cf probe: discard all in-memory state after an idle gap while SQL
77
- // storage remains intact. This keeps the test sub-second instead of waiting
78
- // for workerd/platform eviction heuristics.
79
- const IDLE_TEARDOWN_MS = 250
80
-
81
- const json = (value: unknown, status = 200) =>
82
- Response.json(value, {
83
- status,
84
- headers: { 'cache-control': 'no-store' },
85
- })
86
-
87
- export class ProbeDurableObject extends ZeroDO {
88
- readonly #db: SqlStorageSyncDb
89
- #bootID = crypto.randomUUID()
90
- #lastRequestAt = 0
91
- #reinstantiations = 0
92
- #effects: Array<DeferredEffect & { observedCommitted: boolean }> = []
93
-
94
- constructor(ctx: DurableObjectState, env: Env) {
95
- super(ctx, env as never)
96
- this.#db = new SqlStorageSyncDb(ctx.storage.sql)
97
- ctx.storage.transactionSync(() => init_probe_schema(this.#db))
98
- }
99
-
100
- #maybeReinstantiate(now: number): void {
101
- if (this.#lastRequestAt > 0 && now - this.#lastRequestAt >= IDLE_TEARDOWN_MS) {
102
- this.#bootID = crypto.randomUUID()
103
- this.#effects = []
104
- this.#reinstantiations++
105
- }
106
- this.#lastRequestAt = now
107
- }
108
-
109
- #state(): Record<string, unknown> {
110
- const row = this.ctx.storage.sql
111
- .exec(
112
- `SELECT
113
- CAST((SELECT lmid FROM probe_state WHERE singleton = 1) AS TEXT) AS lmid,
114
- (SELECT balance FROM accounts WHERE id = 'primary') AS balance,
115
- (SELECT COUNT(*) FROM ledger) AS ledgerCount,
116
- (SELECT COUNT(*) FROM outbox) AS outboxCount,
117
- (SELECT COUNT(*) FROM mutation_log) AS mutationCount,
118
- (SELECT COUNT(*) FROM value_probe) AS valueCount`
119
- )
120
- .one()
121
- return {
122
- ...row,
123
- sideEffectCount: this.#effects.length,
124
- sideEffects: this.#effects,
125
- }
126
- }
127
-
128
- async #mutate(name: MutatorName, deferred: DeferredEffect[]): Promise<void> {
129
- if (name === 'read-then-write') {
130
- const { balance } = this.ctx.storage.sql
131
- .exec("SELECT balance FROM accounts WHERE id = 'primary'")
132
- .one() as { balance: number }
133
- await Promise.resolve()
134
- this.ctx.storage.sql.exec(
135
- "UPDATE accounts SET balance = ? WHERE id = 'primary'",
136
- balance + 10
137
- )
138
- this.ctx.storage.sql.exec(
139
- 'INSERT INTO ledger (account_id, amount, note) VALUES (?, ?, ?)',
140
- 'primary',
141
- 10,
142
- 'read-then-write'
143
- )
144
- deferred.push({ mutationID: '', kind: 'balance-notification' })
145
- return
146
- }
147
-
148
- if (name === 'multi-table') {
149
- this.ctx.storage.sql.exec(
150
- "UPDATE accounts SET balance = balance - 5 WHERE id = 'primary'"
151
- )
152
- await Promise.resolve()
153
- this.ctx.storage.sql.exec(
154
- 'INSERT INTO ledger (account_id, amount, note) VALUES (?, ?, ?)',
155
- 'primary',
156
- -5,
157
- 'multi-table'
158
- )
159
- this.ctx.storage.sql.exec(
160
- 'INSERT INTO outbox (topic, payload) VALUES (?, ?)',
161
- 'account.changed',
162
- JSON.stringify({ id: 'primary', delta: -5 })
163
- )
164
- deferred.push({ mutationID: '', kind: 'outbox-notification' })
165
- return
166
- }
167
-
168
- const { balance } = this.ctx.storage.sql
169
- .exec("SELECT balance FROM accounts WHERE id = 'primary'")
170
- .one() as { balance: number }
171
- this.ctx.storage.sql.exec(
172
- "UPDATE accounts SET balance = ? WHERE id = 'primary'",
173
- balance + 777
174
- )
175
- await Promise.resolve()
176
- this.ctx.storage.sql.exec(
177
- 'INSERT INTO ledger (account_id, amount, note) VALUES (?, ?, ?)',
178
- 'primary',
179
- 777,
180
- 'application-error'
181
- )
182
- deferred.push({ mutationID: '', kind: 'must-not-run' })
183
- throw new Error('intentional application mutator error')
184
- }
185
-
186
- async #push(name: MutatorName, mutationID: string): Promise<Response> {
187
- this.#db.resetStats()
188
- const deferred: DeferredEffect[] = []
189
- const started = performance.now()
190
- let lmid: string | undefined
191
- let wasmMs = 0
192
- try {
193
- await this.ctx.storage.transaction(async () => {
194
- let wasmStarted = performance.now()
195
- const expected = push_preflight(this.#db, mutationID)
196
- wasmMs += performance.now() - wasmStarted
197
-
198
- // MutatorSql methods resolve through the microtask queue. Cross that
199
- // same boundary here without admitting timers or other external work
200
- // into the storage transaction.
201
- await Promise.resolve()
202
- await this.#mutate(name, deferred)
203
-
204
- wasmStarted = performance.now()
205
- lmid = push_finalize(this.#db, mutationID, expected)
206
- wasmMs += performance.now() - wasmStarted
207
- })
208
- } catch (error) {
209
- return json(
210
- {
211
- ok: false,
212
- error: String(error),
213
- awaitedInsideTransaction: true,
214
- state: this.#state(),
215
- effectsDeferredButNotRun: deferred.length,
216
- },
217
- 409
218
- )
219
- }
220
-
221
- // External effects are not even inspected until the transaction promise
222
- // resolves. Each effect independently verifies its mutation is durable.
223
- for (const effect of deferred) {
224
- effect.mutationID = mutationID
225
- const committed =
226
- this.ctx.storage.sql
227
- .exec(
228
- 'SELECT 1 AS committed FROM mutation_log WHERE mutation_id = ?',
229
- mutationID
230
- )
231
- .toArray().length === 1
232
- this.#effects.push({ ...effect, observedCommitted: committed })
233
- }
234
-
235
- return json({
236
- ok: true,
237
- lmid,
238
- awaitedInsideTransaction: true,
239
- state: this.#state(),
240
- timing: {
241
- elapsedMs: performance.now() - started,
242
- wasmMs,
243
- ...this.#db.stats,
244
- },
245
- })
246
- }
247
-
248
- async fetch(request: Request): Promise<Response> {
249
- this.#maybeReinstantiate(Date.now())
250
- const url = new URL(request.url)
251
- const [, , ...routeParts] = url.pathname.split('/')
252
- const route = `/${routeParts.join('/')}`
253
-
254
- if (route === '/status') {
255
- return json({
256
- bootID: this.#bootID,
257
- idleTeardownMs: IDLE_TEARDOWN_MS,
258
- reinstantiations: this.#reinstantiations,
259
- state: this.#state(),
260
- })
261
- }
262
-
263
- if (route === '/pull') {
264
- this.#db.resetStats()
265
- const started = performance.now()
266
- const snapshot = this.ctx.storage.transactionSync(() => pull_snapshot(this.#db))
267
- return json({
268
- snapshot,
269
- transaction: 'transactionSync',
270
- timing: { elapsedMs: performance.now() - started, ...this.#db.stats },
271
- })
272
- }
273
-
274
- if (route.startsWith('/push/')) {
275
- const name = route.slice('/push/'.length) as MutatorName
276
- if (!['read-then-write', 'multi-table', 'application-error'].includes(name)) {
277
- return json({ error: 'unknown mutator' }, 404)
278
- }
279
- const body = (await request.json()) as { mutationID: string }
280
- return this.#push(name, body.mutationID)
281
- }
282
-
283
- if (route === '/js-exception') {
284
- const before = this.#state()
285
- try {
286
- await this.ctx.storage.transaction(async () => {
287
- const expected = push_preflight(this.#db, 'js-exception')
288
- this.ctx.storage.sql.exec(
289
- "UPDATE accounts SET balance = balance + 123 WHERE id = 'primary'"
290
- )
291
- await Promise.resolve()
292
- this.ctx.storage.sql.exec(
293
- 'INSERT INTO outbox (topic, payload) VALUES (?, ?)',
294
- 'must.rollback',
295
- '{}'
296
- )
297
- push_finalize(this.#db, 'js-exception', expected)
298
- throw new Error('intentional JS exception after finalization')
299
- })
300
- } catch (error) {
301
- return json(
302
- { ok: false, error: String(error), before, after: this.#state() },
303
- 409
304
- )
305
- }
306
- return json({ error: 'JS exception did not escape transaction' }, 500)
307
- }
308
-
309
- if (route === '/rust-panic') {
310
- const before = this.#state()
311
- try {
312
- this.ctx.storage.transactionSync(() => rust_panic_after_writes(this.#db))
313
- } catch (error) {
314
- return json(
315
- { ok: false, error: String(error), before, after: this.#state() },
316
- 409
317
- )
318
- }
319
- return json({ error: 'Rust panic did not escape transaction' }, 500)
320
- }
321
-
322
- if (route === '/values') {
323
- const input = await request.json()
324
- const output = this.ctx.storage.transactionSync(() =>
325
- value_round_trip(this.#db, input)
326
- )
327
- return json(output)
328
- }
329
-
330
- if (route === '/adapter-guard') {
331
- const errors: string[] = []
332
- for (const [sql, params] of [
333
- ['BEGIN TRANSACTION', []],
334
- ['SELECT ?1', [{ kind: 'integer', value: '1' }]],
335
- ] as const) {
336
- try {
337
- this.#db.exec(sql, [...params] as never)
338
- } catch (error) {
339
- errors.push(String(error))
340
- }
341
- }
342
- return json({ errors })
343
- }
344
-
345
- if (route === '/transaction-query') {
346
- let plan: ReturnType<typeof compileTransactionQuery> | undefined
347
- const tx = new SqlStorageMutatorTransaction(
348
- new SqlStorageDirect(this.ctx.storage.sql),
349
- (queryAst, queryFormat) => {
350
- plan = compileTransactionQuery(queryAst, queryFormat)
351
- return plan
352
- }
353
- )
354
- const result = await tx.queryAst(
355
- transactionQueryAst,
356
- transactionQueryFormat,
357
- 'platformTransactionQuery'
358
- )
359
- let malformedFormatStatus: unknown
360
- try {
361
- await tx.queryAst(transactionQueryAst, undefined as never)
362
- } catch (error) {
363
- malformedFormatStatus = (error as { status?: unknown }).status
364
- }
365
- return json({ result, malformedFormatStatus, plan })
366
- }
367
-
368
- if (route === '/application-transaction-query') {
369
- const result = await this.runApplicationTransaction(
370
- compileTransactionQuery,
371
- (tx) =>
372
- tx.queryAst(
373
- transactionQueryAst,
374
- transactionQueryFormat,
375
- 'applicationTransactionQuery'
376
- ),
377
- { maxSelects: 8, maxRows: 20 }
378
- )
379
- return json({ result })
380
- }
381
-
382
- if (route === '/application-transaction-query-budget') {
383
- try {
384
- await this.runApplicationTransaction(
385
- compileTransactionQuery,
386
- (tx) =>
387
- tx.queryAst(
388
- transactionQueryAst,
389
- transactionQueryFormat,
390
- 'budgetedApplicationTransactionQuery'
391
- ),
392
- { maxSelects: 1 }
393
- )
394
- } catch (error) {
395
- return json(
396
- {
397
- code: Reflect.get(error as object, 'code'),
398
- query: Reflect.get(error as object, 'query'),
399
- selects: Reflect.get(error as object, 'selects'),
400
- },
401
- 409
402
- )
403
- }
404
- return json({ error: 'query budget did not abort the transaction' }, 500)
405
- }
406
-
407
- return json({ error: 'not found', route }, 404)
408
- }
409
- }
410
-
411
- export default {
412
- fetch(request: Request, env: Env): Promise<Response> {
413
- const url = new URL(request.url)
414
- const [, namespace] = url.pathname.split('/')
415
- if (!namespace) return Promise.resolve(new Response('orez rust sync M0 probe'))
416
- return env.PROBE_DO.get(env.PROBE_DO.idFromName(namespace)).fetch(request)
417
- },
418
- } satisfies ExportedHandler<Env>
@@ -1,40 +0,0 @@
1
- export type DeferredEffect = () => void | Promise<void>
2
-
3
- export type PostCommitEffects = {
4
- /** discard effects from a storage-transaction attempt before starting another */
5
- beginAttempt(): void
6
- defer(effect: DeferredEffect): void
7
- /** run the committed attempt's effects in order without rejecting */
8
- runAfterCommit(onError: (error: unknown) => void): Promise<void>
9
- }
10
-
11
- /**
12
- * collect external work during a retryable transaction and run only the effects
13
- * belonging to the attempt that commits.
14
- */
15
- export function createPostCommitEffects(): PostCommitEffects {
16
- const effects: DeferredEffect[] = []
17
-
18
- return {
19
- beginAttempt() {
20
- effects.length = 0
21
- },
22
- defer(effect) {
23
- effects.push(effect)
24
- },
25
- async runAfterCommit(onError) {
26
- const committed = effects.splice(0)
27
- for (const effect of committed) {
28
- try {
29
- await effect()
30
- } catch (error) {
31
- try {
32
- onError(error)
33
- } catch {
34
- // reporting must not turn a committed effect failure into a rejection
35
- }
36
- }
37
- }
38
- },
39
- }
40
- }
@@ -1,16 +0,0 @@
1
- import { engine_compile_query } from './wasm.js'
2
-
3
- import type {
4
- CompiledTransactionQueryPlan,
5
- TransactionQueryFormat,
6
- } from './transaction-query.js'
7
- import type { ZeroSchemaConfig } from './types.js'
8
-
9
- export type TransactionQueryCompiler = (
10
- ast: unknown,
11
- format: TransactionQueryFormat
12
- ) => CompiledTransactionQueryPlan
13
-
14
- export function createQueryCompiler(schema: ZeroSchemaConfig): TransactionQueryCompiler {
15
- return (ast, format) => engine_compile_query(schema, ast, format)
16
- }