rivetkit 2.3.4 → 2.3.5

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 (161) hide show
  1. package/dist/browser/client.d.ts +157 -9
  2. package/dist/browser/client.js +518 -542
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.d.ts +101 -2
  5. package/dist/browser/inspector/client.js +1245 -227
  6. package/dist/browser/inspector/client.js.map +1 -1
  7. package/dist/tsup/agent-os/index.cjs +103 -36
  8. package/dist/tsup/agent-os/index.cjs.map +1 -1
  9. package/dist/tsup/agent-os/index.d.cts +159 -11
  10. package/dist/tsup/agent-os/index.d.ts +159 -11
  11. package/dist/tsup/agent-os/index.js +103 -36
  12. package/dist/tsup/agent-os/index.js.map +1 -1
  13. package/dist/tsup/{chunk-7X4M7ZRA.cjs → chunk-2GZPBFZV.cjs} +146 -177
  14. package/dist/tsup/chunk-2GZPBFZV.cjs.map +1 -0
  15. package/dist/tsup/{chunk-JZ7TWV65.js → chunk-7AFZMFPQ.js} +15 -1
  16. package/dist/tsup/chunk-7AFZMFPQ.js.map +1 -0
  17. package/dist/tsup/{chunk-UPOCJPEQ.cjs → chunk-7HDG7FY3.cjs} +4 -4
  18. package/dist/tsup/{chunk-UPOCJPEQ.cjs.map → chunk-7HDG7FY3.cjs.map} +1 -1
  19. package/dist/tsup/{chunk-T4MURX4P.cjs → chunk-7IIAJQTF.cjs} +7 -7
  20. package/dist/tsup/{chunk-T4MURX4P.cjs.map → chunk-7IIAJQTF.cjs.map} +1 -1
  21. package/dist/tsup/{chunk-EOSGY3DC.js → chunk-ENPXVDGZ.js} +2 -2
  22. package/dist/tsup/{chunk-GXW3VFJQ.js → chunk-FJQ36P7Q.js} +2 -2
  23. package/dist/tsup/{chunk-3MHDOUD7.cjs → chunk-G3CCRCTK.cjs} +14 -9
  24. package/dist/tsup/chunk-G3CCRCTK.cjs.map +1 -0
  25. package/dist/tsup/{chunk-Q7NBMTF3.cjs → chunk-ID7DMKXR.cjs} +384 -4
  26. package/dist/tsup/chunk-ID7DMKXR.cjs.map +1 -0
  27. package/dist/tsup/{chunk-HXUEHHJF.js → chunk-JI6GZ2C2.js} +2 -11
  28. package/dist/tsup/{chunk-HXUEHHJF.js.map → chunk-JI6GZ2C2.js.map} +1 -1
  29. package/dist/tsup/{chunk-VE2X4KMG.cjs → chunk-MYWZCDML.cjs} +2 -11
  30. package/dist/tsup/chunk-MYWZCDML.cjs.map +1 -0
  31. package/dist/tsup/{chunk-M5TAM4JV.js → chunk-NYEGEML6.js} +3 -3
  32. package/dist/tsup/{chunk-SRT2DPN3.cjs → chunk-PG5P2IDC.cjs} +9 -11
  33. package/dist/tsup/chunk-PG5P2IDC.cjs.map +1 -0
  34. package/dist/tsup/{chunk-CMPFZ724.js → chunk-PMIEL4PM.js} +16 -47
  35. package/dist/tsup/{chunk-CMPFZ724.js.map → chunk-PMIEL4PM.js.map} +1 -1
  36. package/dist/tsup/{chunk-XIX5DOZN.js → chunk-SHBE77IT.js} +14 -9
  37. package/dist/tsup/chunk-SHBE77IT.js.map +1 -0
  38. package/dist/tsup/{chunk-3SNFA373.cjs → chunk-TKPQNJQ2.cjs} +3 -3
  39. package/dist/tsup/{chunk-3SNFA373.cjs.map → chunk-TKPQNJQ2.cjs.map} +1 -1
  40. package/dist/tsup/{chunk-ZA7FLHKH.cjs → chunk-UEULFXUI.cjs} +16 -2
  41. package/dist/tsup/chunk-UEULFXUI.cjs.map +1 -0
  42. package/dist/tsup/{chunk-H4XOUVGP.js → chunk-WGRSAJSX.js} +5 -7
  43. package/dist/tsup/{chunk-H4XOUVGP.js.map → chunk-WGRSAJSX.js.map} +1 -1
  44. package/dist/tsup/{chunk-M7DCRMOZ.js → chunk-YOP6FRYG.js} +382 -2
  45. package/dist/tsup/chunk-YOP6FRYG.js.map +1 -0
  46. package/dist/tsup/client/mod.cjs +7 -8
  47. package/dist/tsup/client/mod.cjs.map +1 -1
  48. package/dist/tsup/client/mod.d.cts +5 -4
  49. package/dist/tsup/client/mod.d.ts +5 -4
  50. package/dist/tsup/client/mod.js +6 -7
  51. package/dist/tsup/common/log.cjs +2 -2
  52. package/dist/tsup/common/log.js +1 -1
  53. package/dist/tsup/common/websocket.cjs +3 -3
  54. package/dist/tsup/common/websocket.js +2 -2
  55. package/dist/tsup/{config-D49x8NpL.d.cts → config-BxOimoGX.d.cts} +16 -0
  56. package/dist/tsup/{config-D49x8NpL.d.ts → config-BxOimoGX.d.ts} +16 -0
  57. package/dist/tsup/{config-ZkIsRFsN.d.ts → config-BzdqbLAP.d.ts} +164 -27
  58. package/dist/tsup/{config-BfBhjEbk.d.cts → config-CPPEwn_e.d.cts} +164 -27
  59. package/dist/tsup/{context-sZ9GlaBK.d.cts → context-DSA9gFXB.d.cts} +2 -2
  60. package/dist/tsup/{context-B7-84u9K.d.ts → context-wWv-K2K_.d.ts} +2 -2
  61. package/dist/tsup/db/drizzle.cjs +120 -68
  62. package/dist/tsup/db/drizzle.cjs.map +1 -1
  63. package/dist/tsup/db/drizzle.d.cts +8 -3
  64. package/dist/tsup/db/drizzle.d.ts +8 -3
  65. package/dist/tsup/db/drizzle.js +120 -68
  66. package/dist/tsup/db/drizzle.js.map +1 -1
  67. package/dist/tsup/db/mod.cjs +74 -22
  68. package/dist/tsup/db/mod.cjs.map +1 -1
  69. package/dist/tsup/db/mod.d.cts +4 -3
  70. package/dist/tsup/db/mod.d.ts +4 -3
  71. package/dist/tsup/db/mod.js +74 -22
  72. package/dist/tsup/db/mod.js.map +1 -1
  73. package/dist/tsup/dynamic/mod.cjs +2 -2
  74. package/dist/tsup/dynamic/mod.d.cts +3 -3
  75. package/dist/tsup/dynamic/mod.d.ts +3 -3
  76. package/dist/tsup/dynamic/mod.js +1 -1
  77. package/dist/tsup/inspector/mod.cjs +4 -5
  78. package/dist/tsup/inspector/mod.cjs.map +1 -1
  79. package/dist/tsup/inspector/mod.js +3 -4
  80. package/dist/tsup/inspector/mod.js.map +1 -1
  81. package/dist/tsup/inspector-tab/mod.cjs +2 -2
  82. package/dist/tsup/inspector-tab/mod.cjs.map +1 -1
  83. package/dist/tsup/inspector-tab/mod.d.cts +5 -5
  84. package/dist/tsup/inspector-tab/mod.d.ts +5 -5
  85. package/dist/tsup/inspector-tab/mod.js +1 -1
  86. package/dist/tsup/inspector-tab/mod.js.map +1 -1
  87. package/dist/tsup/mod.cjs +605 -158
  88. package/dist/tsup/mod.cjs.map +1 -1
  89. package/dist/tsup/mod.d.cts +5 -5
  90. package/dist/tsup/mod.d.ts +5 -5
  91. package/dist/tsup/mod.js +528 -81
  92. package/dist/tsup/mod.js.map +1 -1
  93. package/dist/tsup/test/mod.cjs +10 -11
  94. package/dist/tsup/test/mod.cjs.map +1 -1
  95. package/dist/tsup/test/mod.d.cts +3 -3
  96. package/dist/tsup/test/mod.d.ts +3 -3
  97. package/dist/tsup/test/mod.js +6 -7
  98. package/dist/tsup/test/mod.js.map +1 -1
  99. package/dist/tsup/utils-CV0akuDD.d.ts +162 -0
  100. package/dist/tsup/utils-DQDCphrl.d.cts +162 -0
  101. package/dist/tsup/utils.cjs +6 -2
  102. package/dist/tsup/utils.cjs.map +1 -1
  103. package/dist/tsup/utils.d.cts +3 -146
  104. package/dist/tsup/utils.d.ts +3 -146
  105. package/dist/tsup/utils.js +5 -1
  106. package/dist/tsup/workflow/mod.cjs +198 -151
  107. package/dist/tsup/workflow/mod.cjs.map +1 -1
  108. package/dist/tsup/workflow/mod.d.cts +5 -5
  109. package/dist/tsup/workflow/mod.d.ts +5 -5
  110. package/dist/tsup/workflow/mod.js +187 -140
  111. package/dist/tsup/workflow/mod.js.map +1 -1
  112. package/package.json +8 -8
  113. package/src/actor/config.test.ts +14 -0
  114. package/src/actor/config.ts +104 -16
  115. package/src/actor/definition.ts +18 -7
  116. package/src/agent-os/actor/index.ts +2 -2
  117. package/src/common/bare/generated/actor-runtime-socket-protocol/v1.ts +848 -0
  118. package/src/common/bare/generated/inspector/v6.ts +1367 -0
  119. package/src/common/database/config.ts +24 -0
  120. package/src/common/database/mod.test.ts +148 -59
  121. package/src/common/database/mod.ts +93 -23
  122. package/src/common/database/native-database.test.ts +9 -0
  123. package/src/common/database/native-database.ts +151 -0
  124. package/src/common/database/shared.ts +15 -0
  125. package/src/common/encoding.ts +28 -21
  126. package/src/db/drizzle.test.ts +157 -0
  127. package/src/db/drizzle.ts +170 -79
  128. package/src/dynamic/isolate-runtime.ts +0 -2
  129. package/src/engine-client/driver.ts +0 -15
  130. package/src/engine-client/mod.ts +0 -41
  131. package/src/inspector/client.browser.ts +69 -8
  132. package/src/inspector-tab/mod.ts +1 -0
  133. package/src/registry/config/index.ts +1 -35
  134. package/src/registry/napi-runtime.ts +204 -16
  135. package/src/registry/native.ts +200 -30
  136. package/src/registry/runtime.ts +146 -3
  137. package/src/registry/wasm-runtime.ts +217 -6
  138. package/src/utils.ts +3 -0
  139. package/src/workflow/context.ts +5 -27
  140. package/src/workflow/driver.test.ts +62 -0
  141. package/src/workflow/driver.ts +231 -113
  142. package/src/workflow/mod.ts +3 -3
  143. package/dist/tsup/chunk-3MHDOUD7.cjs.map +0 -1
  144. package/dist/tsup/chunk-7X4M7ZRA.cjs.map +0 -1
  145. package/dist/tsup/chunk-JZ7TWV65.js.map +0 -1
  146. package/dist/tsup/chunk-M7DCRMOZ.js.map +0 -1
  147. package/dist/tsup/chunk-Q7NBMTF3.cjs.map +0 -1
  148. package/dist/tsup/chunk-SRT2DPN3.cjs.map +0 -1
  149. package/dist/tsup/chunk-VE2X4KMG.cjs.map +0 -1
  150. package/dist/tsup/chunk-W6WPKZ6I.js +0 -382
  151. package/dist/tsup/chunk-W6WPKZ6I.js.map +0 -1
  152. package/dist/tsup/chunk-XIX5DOZN.js.map +0 -1
  153. package/dist/tsup/chunk-Z7PSQWMC.cjs +0 -382
  154. package/dist/tsup/chunk-Z7PSQWMC.cjs.map +0 -1
  155. package/dist/tsup/chunk-ZA7FLHKH.cjs.map +0 -1
  156. package/src/actor/driver.ts +0 -141
  157. package/src/actor/instance/mod.ts +0 -2484
  158. package/src/drivers/engine/actor-driver.ts +0 -2473
  159. /package/dist/tsup/{chunk-EOSGY3DC.js.map → chunk-ENPXVDGZ.js.map} +0 -0
  160. /package/dist/tsup/{chunk-GXW3VFJQ.js.map → chunk-FJQ36P7Q.js.map} +0 -0
  161. /package/dist/tsup/{chunk-M5TAM4JV.js.map → chunk-NYEGEML6.js.map} +0 -0
@@ -1,9 +1,11 @@
1
1
  import { decodeBridgeRivetError } from "@/actor/errors";
2
2
  import type {
3
+ SqliteBatchStatement,
3
4
  SqliteBindings,
4
5
  SqliteDatabase,
5
6
  SqliteExecuteResult,
6
7
  SqliteNativeMetrics,
8
+ SqliteTransactionDatabase,
7
9
  } from "./config";
8
10
 
9
11
  type NativeBindNoValues = {
@@ -65,12 +67,21 @@ interface NativeExecuteResult {
65
67
  lastInsertRowId?: number | null;
66
68
  }
67
69
 
70
+ interface NativeBatchStatement {
71
+ sql: string;
72
+ params?: NativeBindParam[] | null;
73
+ }
74
+
68
75
  export interface JsNativeDatabaseLike {
69
76
  exec(sql: string): Promise<NativeExecResult>;
70
77
  execute(
71
78
  sql: string,
72
79
  params?: NativeBindParam[] | null,
73
80
  ): Promise<NativeExecuteResult>;
81
+ executeBatch?(
82
+ statements: NativeBatchStatement[],
83
+ ): Promise<NativeExecuteResult[]>;
84
+ beginTransaction(timeoutMs?: number): Promise<JsNativeTransactionLike>;
74
85
  query(
75
86
  sql: string,
76
87
  params?: NativeBindParam[] | null,
@@ -84,6 +95,16 @@ export interface JsNativeDatabaseLike {
84
95
  close(): Promise<void>;
85
96
  }
86
97
 
98
+ export interface JsNativeTransactionLike {
99
+ exec(sql: string): Promise<NativeExecResult>;
100
+ execute(
101
+ sql: string,
102
+ params?: NativeBindParam[] | null,
103
+ ): Promise<NativeExecuteResult>;
104
+ commit(): Promise<void>;
105
+ rollback(): Promise<void>;
106
+ }
107
+
87
108
  function shouldAttachNativeKvError(message: string): boolean {
88
109
  return /i\/o error|unable to open database file/i.test(message);
89
110
  }
@@ -207,6 +228,15 @@ function toNativeBindings(
207
228
  });
208
229
  }
209
230
 
231
+ function toNativeBatchStatement(
232
+ statement: SqliteBatchStatement,
233
+ ): NativeBatchStatement {
234
+ return {
235
+ sql: statement.sql,
236
+ params: toNativeBindings(statement.sql, statement.params),
237
+ };
238
+ }
239
+
210
240
  function normalizeNativeMetrics(
211
241
  metrics: SqliteNativeMetrics | null | undefined,
212
242
  ): SqliteNativeMetrics | null {
@@ -343,6 +373,68 @@ export function wrapJsNativeDatabase(
343
373
  ): Promise<SqliteExecuteResult> {
344
374
  return await executeNative(sql, params);
345
375
  },
376
+ async executeBatch(
377
+ statements: SqliteBatchStatement[],
378
+ ): Promise<SqliteExecuteResult[]> {
379
+ const nativeStatements = statements.map(toNativeBatchStatement);
380
+ const release = gate.enter();
381
+ let transaction: JsNativeTransactionLike | undefined;
382
+ try {
383
+ const results = database.executeBatch
384
+ ? await database.executeBatch(nativeStatements)
385
+ : await (async () => {
386
+ transaction = await database.beginTransaction();
387
+ const transactionResults: NativeExecuteResult[] =
388
+ [];
389
+ for (const statement of nativeStatements) {
390
+ transactionResults.push(
391
+ await transaction.execute(
392
+ statement.sql,
393
+ statement.params,
394
+ ),
395
+ );
396
+ }
397
+ await transaction.commit();
398
+ transaction = undefined;
399
+ return transactionResults;
400
+ })();
401
+ for (const result of results) {
402
+ if (result.lastInsertRowId !== undefined) {
403
+ lastInsertRowId = result.lastInsertRowId;
404
+ }
405
+ }
406
+ return results;
407
+ } catch (error) {
408
+ if (transaction) {
409
+ try {
410
+ await transaction.rollback();
411
+ } catch {
412
+ // The original batch error is more actionable than cleanup failure.
413
+ }
414
+ }
415
+ enrichNativeDatabaseError(database, error);
416
+ } finally {
417
+ release();
418
+ }
419
+ },
420
+ async beginTransaction(
421
+ timeoutMs?: number,
422
+ ): Promise<SqliteTransactionDatabase> {
423
+ const release = gate.enter();
424
+ let transaction: JsNativeTransactionLike;
425
+ try {
426
+ transaction = await database.beginTransaction(timeoutMs);
427
+ } catch (error) {
428
+ enrichNativeDatabaseError(database, error);
429
+ } finally {
430
+ release();
431
+ }
432
+ return wrapTransaction(database, transaction, gate, (result) => {
433
+ if (result.lastInsertRowId !== undefined) {
434
+ lastInsertRowId = result.lastInsertRowId;
435
+ }
436
+ });
437
+ },
346
438
  async run(sql: string, params?: SqliteBindings): Promise<void> {
347
439
  await executeNative(sql, params);
348
440
  },
@@ -360,6 +452,65 @@ export function wrapJsNativeDatabase(
360
452
  };
361
453
  }
362
454
 
455
+ function wrapTransaction(
456
+ database: JsNativeDatabaseLike,
457
+ transaction: JsNativeTransactionLike,
458
+ gate: NativeCloseGate,
459
+ onExecute: (result: NativeExecuteResult) => void,
460
+ ): SqliteTransactionDatabase {
461
+ return {
462
+ async exec(sql, callback) {
463
+ const release = gate.enter();
464
+ let result: NativeExecResult;
465
+ try {
466
+ result = await transaction.exec(sql);
467
+ } catch (error) {
468
+ enrichNativeDatabaseError(database, error);
469
+ } finally {
470
+ release();
471
+ }
472
+ if (callback) {
473
+ for (const row of result.rows) callback(row, result.columns);
474
+ }
475
+ },
476
+ async execute(sql, params) {
477
+ const release = gate.enter();
478
+ try {
479
+ const result = await transaction.execute(
480
+ sql,
481
+ toNativeBindings(sql, params),
482
+ );
483
+ onExecute(result);
484
+ return result;
485
+ } catch (error) {
486
+ enrichNativeDatabaseError(database, error);
487
+ } finally {
488
+ release();
489
+ }
490
+ },
491
+ async commit() {
492
+ const release = gate.enter();
493
+ try {
494
+ await transaction.commit();
495
+ } catch (error) {
496
+ enrichNativeDatabaseError(database, error);
497
+ } finally {
498
+ release();
499
+ }
500
+ },
501
+ async rollback() {
502
+ const release = gate.enter();
503
+ try {
504
+ await transaction.rollback();
505
+ } catch (error) {
506
+ enrichNativeDatabaseError(database, error);
507
+ } finally {
508
+ release();
509
+ }
510
+ },
511
+ };
512
+ }
513
+
363
514
  function lastInsertRowIdColumnName(sql: string): string | undefined {
364
515
  const match = sql.match(
365
516
  /^\s*SELECT\s+last_insert_rowid\s*\(\s*\)\s*(?:AS\s+("[^"]+"|`[^`]+`|\[[^\]]+\]|\w+))?\s*;?\s*$/i,
@@ -1,5 +1,20 @@
1
1
  import type { SqliteBindings } from "./config";
2
2
 
3
+ /** Migrations may legitimately do substantially more work than request transactions. */
4
+ export const MIGRATION_TRANSACTION_TIMEOUT_MS = 5 * 60_000;
5
+
6
+ export function isManualTransactionControl(query: string): boolean {
7
+ return /^\s*(?:BEGIN|SAVEPOINT|COMMIT|END|ROLLBACK)\b/i.test(query);
8
+ }
9
+
10
+ export function validateTransactionTimeout(timeout: number | undefined): void {
11
+ if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {
12
+ throw new Error(
13
+ "db.transaction() timeout must be a positive finite number of milliseconds",
14
+ );
15
+ }
16
+ }
17
+
3
18
  type SqliteBindingObject = Record<string, unknown>;
4
19
 
5
20
  function isSqliteBindingValue(value: unknown): boolean {
@@ -473,27 +473,34 @@ function base64DecodeToArrayBuffer(base64: string): ArrayBuffer {
473
473
  }
474
474
 
475
475
  /** Stringifies with compat for values that BARE & CBOR supports. */
476
- export function jsonStringifyCompat(input: any): string {
477
- return JSON.stringify(input, (_key, value) => {
478
- if (typeof value === "bigint") {
479
- return [JSON_COMPAT_BIGINT, value.toString()];
480
- }
481
- if (value instanceof ArrayBuffer) {
482
- return [JSON_COMPAT_ARRAY_BUFFER, base64EncodeArrayBuffer(value)];
483
- }
484
- if (value instanceof Uint8Array) {
485
- return [JSON_COMPAT_UINT8_ARRAY, base64EncodeUint8Array(value)];
486
- }
487
- if (
488
- Array.isArray(value) &&
489
- value.length === 2 &&
490
- typeof value[0] === "string" &&
491
- value[0].startsWith("$")
492
- ) {
493
- return [`$${value[0]}`, value[1]];
494
- }
495
- return value;
496
- });
476
+ export function jsonStringifyCompat(
477
+ input: any,
478
+ space?: string | number,
479
+ ): string {
480
+ return JSON.stringify(
481
+ input,
482
+ (_key, value) => {
483
+ if (typeof value === "bigint") {
484
+ return [JSON_COMPAT_BIGINT, value.toString()];
485
+ }
486
+ if (value instanceof ArrayBuffer) {
487
+ return [JSON_COMPAT_ARRAY_BUFFER, base64EncodeArrayBuffer(value)];
488
+ }
489
+ if (value instanceof Uint8Array) {
490
+ return [JSON_COMPAT_UINT8_ARRAY, base64EncodeUint8Array(value)];
491
+ }
492
+ if (
493
+ Array.isArray(value) &&
494
+ value.length === 2 &&
495
+ typeof value[0] === "string" &&
496
+ value[0].startsWith("$")
497
+ ) {
498
+ return [`$${value[0]}`, value[1]];
499
+ }
500
+ return value;
501
+ },
502
+ space,
503
+ );
497
504
  }
498
505
 
499
506
  /** Parses JSON with compat for values that BARE & CBOR supports. */
@@ -0,0 +1,157 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type {
3
+ DatabaseProviderContext,
4
+ SqliteBatchStatement,
5
+ SqliteBindings,
6
+ SqliteDatabase,
7
+ SqliteExecuteResult,
8
+ SqliteTransactionDatabase,
9
+ } from "@/common/database/config";
10
+ import { db } from "./drizzle";
11
+
12
+ class FakeSqliteDatabase implements SqliteDatabase {
13
+ executeCalls: Array<{ sql: string; params?: SqliteBindings }> = [];
14
+ transactionTimeouts: Array<number | undefined> = [];
15
+
16
+ async exec(): Promise<void> {}
17
+
18
+ async execute(
19
+ sql: string,
20
+ params?: SqliteBindings,
21
+ ): Promise<SqliteExecuteResult> {
22
+ this.executeCalls.push({ sql, params });
23
+ return emptyResult();
24
+ }
25
+
26
+ async executeBatch(
27
+ statements: SqliteBatchStatement[],
28
+ ): Promise<SqliteExecuteResult[]> {
29
+ const transaction = await this.beginTransaction();
30
+ try {
31
+ const results: SqliteExecuteResult[] = [];
32
+ for (const statement of statements) {
33
+ results.push(
34
+ await transaction.execute(statement.sql, statement.params),
35
+ );
36
+ }
37
+ await transaction.commit();
38
+ return results;
39
+ } catch (error) {
40
+ await transaction.rollback();
41
+ throw error;
42
+ }
43
+ }
44
+
45
+ async beginTransaction(
46
+ timeoutMs?: number,
47
+ ): Promise<SqliteTransactionDatabase> {
48
+ this.transactionTimeouts.push(timeoutMs);
49
+ this.executeCalls.push({ sql: "BEGIN" });
50
+ return {
51
+ exec: async () => {},
52
+ execute: async (sql, params) => {
53
+ this.executeCalls.push({ sql, params });
54
+ return emptyResult();
55
+ },
56
+ commit: async () => {
57
+ this.executeCalls.push({ sql: "COMMIT" });
58
+ },
59
+ rollback: async () => {
60
+ this.executeCalls.push({ sql: "ROLLBACK" });
61
+ },
62
+ };
63
+ }
64
+
65
+ async run(sql: string, params?: SqliteBindings): Promise<void> {
66
+ await this.execute(sql, params);
67
+ }
68
+
69
+ async query(sql: string, params?: SqliteBindings) {
70
+ const { columns, rows } = await this.execute(sql, params);
71
+ return { columns, rows };
72
+ }
73
+
74
+ async close(): Promise<void> {}
75
+ }
76
+
77
+ function emptyResult(): SqliteExecuteResult {
78
+ return {
79
+ columns: [],
80
+ rows: [],
81
+ changes: 0,
82
+ lastInsertRowId: null,
83
+ };
84
+ }
85
+
86
+ function testProviderContext(
87
+ database: SqliteDatabase,
88
+ ): DatabaseProviderContext {
89
+ return {
90
+ actorId: "actor-a",
91
+ kv: {
92
+ batchPut: async () => {},
93
+ batchGet: async (keys) => keys.map(() => null),
94
+ batchDelete: async () => {},
95
+ deleteRange: async () => {},
96
+ },
97
+ nativeDatabaseProvider: { open: async () => database },
98
+ };
99
+ }
100
+
101
+ describe("Drizzle database transactions", () => {
102
+ test("runs migrations in the shared transaction with a generous timeout", async () => {
103
+ const nativeDb = new FakeSqliteDatabase();
104
+ const provider = db({
105
+ onMigrate: async (client) => {
106
+ await client.execute(
107
+ "CREATE TABLE items(id INTEGER PRIMARY KEY)",
108
+ );
109
+ },
110
+ });
111
+ const client = await provider.createClient(
112
+ testProviderContext(nativeDb),
113
+ );
114
+ await provider.onMigrate(client);
115
+
116
+ expect(nativeDb.transactionTimeouts).toEqual([300_000]);
117
+ expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
118
+ "BEGIN",
119
+ "SAVEPOINT __rivet_on_migrate",
120
+ "CREATE TABLE items(id INTEGER PRIMARY KEY)",
121
+ "RELEASE SAVEPOINT __rivet_on_migrate",
122
+ "COMMIT",
123
+ ]);
124
+ });
125
+
126
+ test("routes transaction work through the transaction handle", async () => {
127
+ const nativeDb = new FakeSqliteDatabase();
128
+ const client = await db().createClient(testProviderContext(nativeDb));
129
+ await client.transaction(
130
+ async (tx) => {
131
+ await tx.execute(
132
+ "INSERT INTO items(value) VALUES (?)",
133
+ "inside",
134
+ );
135
+ },
136
+ { timeout: 120_000 },
137
+ );
138
+
139
+ expect(nativeDb.transactionTimeouts).toEqual([120_000]);
140
+ expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
141
+ "BEGIN",
142
+ "INSERT INTO items(value) VALUES (?)",
143
+ "COMMIT",
144
+ ]);
145
+ });
146
+
147
+ test("validates zero, negative, and non-finite transaction timeouts", async () => {
148
+ const client = await db().createClient(
149
+ testProviderContext(new FakeSqliteDatabase()),
150
+ );
151
+ for (const timeout of [0, -1, Number.NaN, Number.POSITIVE_INFINITY]) {
152
+ await expect(
153
+ client.transaction(async () => {}, { timeout }),
154
+ ).rejects.toThrow("positive finite");
155
+ }
156
+ });
157
+ });