rivetkit 0.0.0-stack-chore-sync-engine-changes-from-enterprise-mszropyy.677d526 → 0.0.0-sync-sqlite.0ed739a

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 (123) hide show
  1. package/dist/browser/client.d.ts +25 -0
  2. package/dist/browser/client.js +17 -5
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.js +1 -1
  5. package/dist/browser/inspector/client.js.map +1 -1
  6. package/dist/tsup/agent-os/index.cjs +145 -3
  7. package/dist/tsup/agent-os/index.cjs.map +1 -1
  8. package/dist/tsup/agent-os/index.d.cts +40 -2
  9. package/dist/tsup/agent-os/index.d.ts +40 -2
  10. package/dist/tsup/agent-os/index.js +144 -2
  11. package/dist/tsup/agent-os/index.js.map +1 -1
  12. package/dist/tsup/{chunk-FSD52J3H.cjs → chunk-24PL45D7.cjs} +3 -3
  13. package/dist/tsup/{chunk-FSD52J3H.cjs.map → chunk-24PL45D7.cjs.map} +1 -1
  14. package/dist/tsup/{chunk-7ZPRFCTV.cjs → chunk-3TNKROAF.cjs} +156 -144
  15. package/dist/tsup/chunk-3TNKROAF.cjs.map +1 -0
  16. package/dist/tsup/{chunk-ZYHZOZUQ.js → chunk-75H2CLHI.js} +2 -2
  17. package/dist/tsup/{chunk-OWSXJF7Z.js → chunk-BERJKIW5.js} +22 -10
  18. package/dist/tsup/chunk-BERJKIW5.js.map +1 -0
  19. package/dist/tsup/{chunk-XPT7SGBW.js → chunk-BZOI63CT.js} +4 -4
  20. package/dist/tsup/{chunk-M4RSGECU.js → chunk-EHAGZJ34.js} +2 -2
  21. package/dist/tsup/{chunk-VXJ26VPL.cjs → chunk-FZIA3IQ2.cjs} +10 -10
  22. package/dist/tsup/{chunk-VXJ26VPL.cjs.map → chunk-FZIA3IQ2.cjs.map} +1 -1
  23. package/dist/tsup/{chunk-LHT3TRDM.js → chunk-J7WM3KL5.js} +115 -4
  24. package/dist/tsup/chunk-J7WM3KL5.js.map +1 -0
  25. package/dist/tsup/{chunk-XNLSA7TO.cjs → chunk-JJMBATG7.cjs} +4 -4
  26. package/dist/tsup/{chunk-XNLSA7TO.cjs.map → chunk-JJMBATG7.cjs.map} +1 -1
  27. package/dist/tsup/{chunk-ZXV3CG4Y.cjs → chunk-K3DXAJQ2.cjs} +7 -7
  28. package/dist/tsup/{chunk-ZXV3CG4Y.cjs.map → chunk-K3DXAJQ2.cjs.map} +1 -1
  29. package/dist/tsup/{chunk-YHYNYVNZ.js → chunk-M7FOORLQ.js} +3 -3
  30. package/dist/tsup/{chunk-2TAUHMAC.cjs → chunk-MRFLWAPE.cjs} +2 -2
  31. package/dist/tsup/chunk-MRFLWAPE.cjs.map +1 -0
  32. package/dist/tsup/{chunk-GPD77M3M.js → chunk-NNPIJLBE.js} +2 -2
  33. package/dist/tsup/chunk-NNPIJLBE.js.map +1 -0
  34. package/dist/tsup/{chunk-TFACXNOP.cjs → chunk-R75W2PPM.cjs} +121 -10
  35. package/dist/tsup/chunk-R75W2PPM.cjs.map +1 -0
  36. package/dist/tsup/{chunk-UBGN2UE2.cjs → chunk-SHAZSNYB.cjs} +3 -3
  37. package/dist/tsup/{chunk-UBGN2UE2.cjs.map → chunk-SHAZSNYB.cjs.map} +1 -1
  38. package/dist/tsup/{chunk-L6DNN72S.js → chunk-SLAKWYOH.js} +2 -2
  39. package/dist/tsup/{chunk-JTHHCZCZ.js → chunk-WCEJTJHO.js} +34 -1
  40. package/dist/tsup/chunk-WCEJTJHO.js.map +1 -0
  41. package/dist/tsup/{chunk-KVFIONKQ.cjs → chunk-XMNL3AE2.cjs} +35 -2
  42. package/dist/tsup/chunk-XMNL3AE2.cjs.map +1 -0
  43. package/dist/tsup/client/mod.cjs +6 -6
  44. package/dist/tsup/client/mod.d.cts +3 -3
  45. package/dist/tsup/client/mod.d.ts +3 -3
  46. package/dist/tsup/client/mod.js +5 -5
  47. package/dist/tsup/common/log.cjs +2 -2
  48. package/dist/tsup/common/log.js +1 -1
  49. package/dist/tsup/common/websocket.cjs +3 -3
  50. package/dist/tsup/common/websocket.js +2 -2
  51. package/dist/tsup/{config-ey2I2zz5.d.cts → config-B0q37XhG.d.cts} +31 -1
  52. package/dist/tsup/{config-ey2I2zz5.d.ts → config-B0q37XhG.d.ts} +31 -1
  53. package/dist/tsup/{config-BGTanB_3.d.ts → config-BPJQ0mDo.d.ts} +9 -1
  54. package/dist/tsup/{config-NHz6oMb9.d.cts → config-CGUJyT5L.d.cts} +9 -1
  55. package/dist/tsup/{context-SHomdVV8.d.ts → context-DZ_lu12i.d.ts} +2 -2
  56. package/dist/tsup/{context-DB8zG_Ib.d.cts → context-WLBsDXrQ.d.cts} +2 -2
  57. package/dist/tsup/db/drizzle.cjs +113 -10
  58. package/dist/tsup/db/drizzle.cjs.map +1 -1
  59. package/dist/tsup/db/drizzle.d.cts +2 -2
  60. package/dist/tsup/db/drizzle.d.ts +2 -2
  61. package/dist/tsup/db/drizzle.js +106 -3
  62. package/dist/tsup/db/drizzle.js.map +1 -1
  63. package/dist/tsup/db/mod.cjs +4 -4
  64. package/dist/tsup/db/mod.d.cts +4 -4
  65. package/dist/tsup/db/mod.d.ts +4 -4
  66. package/dist/tsup/db/mod.js +3 -3
  67. package/dist/tsup/dynamic/mod.d.cts +2 -2
  68. package/dist/tsup/dynamic/mod.d.ts +2 -2
  69. package/dist/tsup/inspector/mod.cjs +5 -5
  70. package/dist/tsup/inspector/mod.js +4 -4
  71. package/dist/tsup/inspector/workflow.cjs +4 -4
  72. package/dist/tsup/inspector/workflow.js +3 -3
  73. package/dist/tsup/inspector-tab/mod.d.cts +3 -3
  74. package/dist/tsup/inspector-tab/mod.d.ts +3 -3
  75. package/dist/tsup/mod.cjs +296 -88
  76. package/dist/tsup/mod.cjs.map +1 -1
  77. package/dist/tsup/mod.d.cts +4 -4
  78. package/dist/tsup/mod.d.ts +4 -4
  79. package/dist/tsup/mod.js +225 -17
  80. package/dist/tsup/mod.js.map +1 -1
  81. package/dist/tsup/test/mod.cjs +7 -7
  82. package/dist/tsup/test/mod.d.cts +2 -2
  83. package/dist/tsup/test/mod.d.ts +2 -2
  84. package/dist/tsup/test/mod.js +5 -5
  85. package/dist/tsup/unstable/migrations.d.cts +1 -1
  86. package/dist/tsup/unstable/migrations.d.ts +1 -1
  87. package/dist/tsup/utils.cjs +2 -2
  88. package/dist/tsup/utils.js +1 -1
  89. package/dist/tsup/workflow/mod.cjs +7 -7
  90. package/dist/tsup/workflow/mod.d.cts +4 -4
  91. package/dist/tsup/workflow/mod.d.ts +4 -4
  92. package/dist/tsup/workflow/mod.js +4 -4
  93. package/package.json +9 -9
  94. package/src/agent-os/actor/index.ts +6 -3
  95. package/src/agent-os/actor/preview.ts +5 -2
  96. package/src/client/actor-handle.ts +15 -8
  97. package/src/client/raw-utils.ts +14 -0
  98. package/src/common/database/config.ts +54 -0
  99. package/src/common/database/mod.test.ts +162 -1
  100. package/src/common/database/mod.ts +154 -6
  101. package/src/common/database/native-database.test.ts +81 -2
  102. package/src/common/database/native-database.ts +172 -3
  103. package/src/common/database/shared.ts +49 -1
  104. package/src/db/drizzle.test.ts +82 -1
  105. package/src/db/drizzle.ts +136 -6
  106. package/src/db/mod.ts +2 -0
  107. package/src/registry/napi-runtime.ts +71 -0
  108. package/src/registry/native.ts +48 -14
  109. package/src/registry/runtime.ts +46 -0
  110. package/src/registry/wasm-runtime.ts +63 -0
  111. package/dist/tsup/chunk-2TAUHMAC.cjs.map +0 -1
  112. package/dist/tsup/chunk-7ZPRFCTV.cjs.map +0 -1
  113. package/dist/tsup/chunk-GPD77M3M.js.map +0 -1
  114. package/dist/tsup/chunk-JTHHCZCZ.js.map +0 -1
  115. package/dist/tsup/chunk-KVFIONKQ.cjs.map +0 -1
  116. package/dist/tsup/chunk-LHT3TRDM.js.map +0 -1
  117. package/dist/tsup/chunk-OWSXJF7Z.js.map +0 -1
  118. package/dist/tsup/chunk-TFACXNOP.cjs.map +0 -1
  119. /package/dist/tsup/{chunk-ZYHZOZUQ.js.map → chunk-75H2CLHI.js.map} +0 -0
  120. /package/dist/tsup/{chunk-XPT7SGBW.js.map → chunk-BZOI63CT.js.map} +0 -0
  121. /package/dist/tsup/{chunk-M4RSGECU.js.map → chunk-EHAGZJ34.js.map} +0 -0
  122. /package/dist/tsup/{chunk-YHYNYVNZ.js.map → chunk-M7FOORLQ.js.map} +0 -0
  123. /package/dist/tsup/{chunk-L6DNN72S.js.map → chunk-SLAKWYOH.js.map} +0 -0
@@ -6,6 +6,7 @@ import type {
6
6
  SqliteExecuteResult,
7
7
  SqliteNativeMetrics,
8
8
  SqliteTransactionDatabase,
9
+ SynchronousSqliteTransactionDatabase,
9
10
  } from "./config";
10
11
 
11
12
  type NativeBindNoValues = {
@@ -74,10 +75,15 @@ interface NativeBatchStatement {
74
75
 
75
76
  export interface JsNativeDatabaseLike {
76
77
  exec(sql: string): Promise<NativeExecResult>;
78
+ execSync(sql: string): NativeExecResult;
77
79
  execute(
78
80
  sql: string,
79
81
  params?: NativeBindParam[] | null,
80
82
  ): Promise<NativeExecuteResult>;
83
+ executeSync(
84
+ sql: string,
85
+ params?: NativeBindParam[] | null,
86
+ ): NativeExecuteResult;
81
87
  executeBatch?(
82
88
  statements: NativeBatchStatement[],
83
89
  ): Promise<NativeExecuteResult[]>;
@@ -88,7 +94,11 @@ export interface JsNativeDatabaseLike {
88
94
  beginTransaction(
89
95
  timeoutMs?: number,
90
96
  name?: string,
91
- ): Promise<JsNativeTransactionLike>;
97
+ ): Promise<JsNativeSynchronousTransactionLike>;
98
+ beginTransactionSync(
99
+ timeoutMs?: number,
100
+ name?: string,
101
+ ): JsNativeSynchronousTransactionLike;
92
102
  query(
93
103
  sql: string,
94
104
  params?: NativeBindParam[] | null,
@@ -110,14 +120,36 @@ export type StateAwareSqliteDatabase = SqliteDatabase & {
110
120
 
111
121
  export interface JsNativeTransactionLike {
112
122
  exec(sql: string): Promise<NativeExecResult>;
123
+ execSync(sql: string): NativeExecResult;
113
124
  execute(
114
125
  sql: string,
115
126
  params?: NativeBindParam[] | null,
116
127
  ): Promise<NativeExecuteResult>;
128
+ executeSync(
129
+ sql: string,
130
+ params?: NativeBindParam[] | null,
131
+ ): NativeExecuteResult;
117
132
  commit(): Promise<void>;
118
133
  rollback(): Promise<void>;
119
134
  }
120
135
 
136
+ export interface JsNativeSynchronousTransactionLike
137
+ extends JsNativeTransactionLike {
138
+ commitSync(): void;
139
+ rollbackSync(): void;
140
+ }
141
+
142
+ function isSynchronousTransaction(
143
+ transaction: JsNativeTransactionLike,
144
+ ): transaction is JsNativeSynchronousTransactionLike {
145
+ const candidate =
146
+ transaction as Partial<JsNativeSynchronousTransactionLike>;
147
+ return (
148
+ typeof candidate.commitSync === "function" &&
149
+ typeof candidate.rollbackSync === "function"
150
+ );
151
+ }
152
+
121
153
  function shouldAttachNativeKvError(message: string): boolean {
122
154
  return /i\/o error|unable to open database file/i.test(message);
123
155
  }
@@ -358,6 +390,34 @@ export function wrapJsNativeDatabase(
358
390
  release();
359
391
  }
360
392
  };
393
+ const executeNativeSync = (
394
+ sql: string,
395
+ params?: SqliteBindings,
396
+ ): SqliteExecuteResult => {
397
+ const lastInsertRowIdColumn = lastInsertRowIdColumnName(sql);
398
+ if (lastInsertRowIdColumn) {
399
+ return {
400
+ columns: [lastInsertRowIdColumn],
401
+ rows: [[lastInsertRowId ?? 0]],
402
+ changes: 0,
403
+ lastInsertRowId,
404
+ };
405
+ }
406
+
407
+ const release = gate.enter();
408
+ try {
409
+ const nativeParams = toNativeBindings(sql, params);
410
+ const result = database.executeSync(sql, nativeParams);
411
+ if (result.lastInsertRowId !== undefined) {
412
+ lastInsertRowId = result.lastInsertRowId;
413
+ }
414
+ return result;
415
+ } catch (error) {
416
+ enrichNativeDatabaseError(database, error);
417
+ } finally {
418
+ release();
419
+ }
420
+ };
361
421
 
362
422
  return {
363
423
  async exec(
@@ -380,12 +440,34 @@ export function wrapJsNativeDatabase(
380
440
  callback(row, result.columns);
381
441
  }
382
442
  },
443
+ execSync(
444
+ sql: string,
445
+ callback?: (row: unknown[], columns: string[]) => void,
446
+ ): void {
447
+ const release = gate.enter();
448
+ let result: NativeExecResult;
449
+ try {
450
+ result = database.execSync(sql);
451
+ } catch (error) {
452
+ enrichNativeDatabaseError(database, error);
453
+ } finally {
454
+ release();
455
+ }
456
+ if (callback) {
457
+ for (const row of result.rows) {
458
+ callback(row, result.columns);
459
+ }
460
+ }
461
+ },
383
462
  async execute(
384
463
  sql: string,
385
464
  params?: SqliteBindings,
386
465
  ): Promise<SqliteExecuteResult> {
387
466
  return await executeNative(sql, params);
388
467
  },
468
+ executeSync(sql: string, params?: SqliteBindings): SqliteExecuteResult {
469
+ return executeNativeSync(sql, params);
470
+ },
389
471
  async executeBatch(
390
472
  statements: SqliteBatchStatement[],
391
473
  ): Promise<SqliteExecuteResult[]> {
@@ -435,7 +517,7 @@ export function wrapJsNativeDatabase(
435
517
  name?: string,
436
518
  ): Promise<SqliteTransactionDatabase> {
437
519
  const release = gate.enter();
438
- let transaction: JsNativeTransactionLike;
520
+ let transaction: JsNativeSynchronousTransactionLike;
439
521
  try {
440
522
  transaction = await database.beginTransaction(timeoutMs, name);
441
523
  } catch (error) {
@@ -449,6 +531,25 @@ export function wrapJsNativeDatabase(
449
531
  }
450
532
  });
451
533
  },
534
+ beginTransactionSync(
535
+ timeoutMs?: number,
536
+ name?: string,
537
+ ): SynchronousSqliteTransactionDatabase {
538
+ const release = gate.enter();
539
+ let transaction: JsNativeSynchronousTransactionLike;
540
+ try {
541
+ transaction = database.beginTransactionSync(timeoutMs, name);
542
+ } catch (error) {
543
+ enrichNativeDatabaseError(database, error);
544
+ } finally {
545
+ release();
546
+ }
547
+ return wrapTransaction(database, transaction, gate, (result) => {
548
+ if (result.lastInsertRowId !== undefined) {
549
+ lastInsertRowId = result.lastInsertRowId;
550
+ }
551
+ });
552
+ },
452
553
  async beginStateTransaction(
453
554
  timeoutMs?: number,
454
555
  context?: unknown,
@@ -491,13 +592,25 @@ export function wrapJsNativeDatabase(
491
592
  };
492
593
  }
493
594
 
595
+ function wrapTransaction(
596
+ database: JsNativeDatabaseLike,
597
+ transaction: JsNativeSynchronousTransactionLike,
598
+ gate: NativeCloseGate,
599
+ onExecute: (result: NativeExecuteResult) => void,
600
+ ): SynchronousSqliteTransactionDatabase;
601
+ function wrapTransaction(
602
+ database: JsNativeDatabaseLike,
603
+ transaction: JsNativeTransactionLike,
604
+ gate: NativeCloseGate,
605
+ onExecute: (result: NativeExecuteResult) => void,
606
+ ): SqliteTransactionDatabase;
494
607
  function wrapTransaction(
495
608
  database: JsNativeDatabaseLike,
496
609
  transaction: JsNativeTransactionLike,
497
610
  gate: NativeCloseGate,
498
611
  onExecute: (result: NativeExecuteResult) => void,
499
612
  ): SqliteTransactionDatabase {
500
- return {
613
+ const wrapped: SqliteTransactionDatabase = {
501
614
  async exec(sql, callback) {
502
615
  const release = gate.enter();
503
616
  let result: NativeExecResult;
@@ -512,6 +625,20 @@ function wrapTransaction(
512
625
  for (const row of result.rows) callback(row, result.columns);
513
626
  }
514
627
  },
628
+ execSync(sql, callback) {
629
+ const release = gate.enter();
630
+ let result: NativeExecResult;
631
+ try {
632
+ result = transaction.execSync(sql);
633
+ } catch (error) {
634
+ enrichNativeDatabaseError(database, error);
635
+ } finally {
636
+ release();
637
+ }
638
+ if (callback) {
639
+ for (const row of result.rows) callback(row, result.columns);
640
+ }
641
+ },
515
642
  async execute(sql, params) {
516
643
  const release = gate.enter();
517
644
  try {
@@ -527,6 +654,21 @@ function wrapTransaction(
527
654
  release();
528
655
  }
529
656
  },
657
+ executeSync(sql, params) {
658
+ const release = gate.enter();
659
+ try {
660
+ const result = transaction.executeSync(
661
+ sql,
662
+ toNativeBindings(sql, params),
663
+ );
664
+ onExecute(result);
665
+ return result;
666
+ } catch (error) {
667
+ enrichNativeDatabaseError(database, error);
668
+ } finally {
669
+ release();
670
+ }
671
+ },
530
672
  async commit() {
531
673
  const release = gate.enter();
532
674
  try {
@@ -548,6 +690,33 @@ function wrapTransaction(
548
690
  }
549
691
  },
550
692
  };
693
+
694
+ if (isSynchronousTransaction(transaction)) {
695
+ return Object.assign(wrapped, {
696
+ commitSync() {
697
+ const release = gate.enter();
698
+ try {
699
+ transaction.commitSync();
700
+ } catch (error) {
701
+ enrichNativeDatabaseError(database, error);
702
+ } finally {
703
+ release();
704
+ }
705
+ },
706
+ rollbackSync() {
707
+ const release = gate.enter();
708
+ try {
709
+ transaction.rollbackSync();
710
+ } catch (error) {
711
+ enrichNativeDatabaseError(database, error);
712
+ } finally {
713
+ release();
714
+ }
715
+ },
716
+ }) as SynchronousSqliteTransactionDatabase;
717
+ }
718
+
719
+ return wrapped;
551
720
  }
552
721
 
553
722
  function lastInsertRowIdColumnName(sql: string): string | undefined {
@@ -1,4 +1,9 @@
1
- import type { SqliteBindings } from "./config";
1
+ import type {
2
+ SqliteBindings,
3
+ SqliteDatabase,
4
+ SqliteTransactionOptions,
5
+ SynchronousSqliteTransactionDatabase,
6
+ } from "./config";
2
7
 
3
8
  /** Migrations may legitimately do substantially more work than request transactions. */
4
9
  export const MIGRATION_TRANSACTION_TIMEOUT_MS = 5 * 60_000;
@@ -24,6 +29,49 @@ export function validateTransactionName(name: string | undefined): void {
24
29
  }
25
30
  }
26
31
 
32
+ export function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
33
+ return (typeof value === "object" && value !== null) ||
34
+ typeof value === "function"
35
+ ? typeof (value as { then?: unknown }).then === "function"
36
+ : false;
37
+ }
38
+
39
+ export function runSqliteTransactionSync<T>(
40
+ database: SqliteDatabase,
41
+ callback: (transaction: SynchronousSqliteTransactionDatabase) => T,
42
+ options?: Omit<SqliteTransactionOptions, "experimental">,
43
+ ): T {
44
+ validateTransactionTimeout(options?.timeout);
45
+ validateTransactionName(options?.name);
46
+ if (!database.beginTransactionSync) {
47
+ throw new Error(
48
+ "Synchronous SQLite transactions are only available in the Node.js native runtime.",
49
+ );
50
+ }
51
+
52
+ const transaction = database.beginTransactionSync(
53
+ options?.timeout,
54
+ options?.name,
55
+ );
56
+ try {
57
+ const result = callback(transaction);
58
+ if (isPromiseLike(result)) {
59
+ throw new Error(
60
+ "db.transactionSync() callback must complete synchronously and must not return a promise.",
61
+ );
62
+ }
63
+ transaction.commitSync();
64
+ return result;
65
+ } catch (error) {
66
+ try {
67
+ transaction.rollbackSync();
68
+ } catch {
69
+ // Preserve the callback or commit error after cleanup failure.
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
27
75
  type SqliteBindingObject = Record<string, unknown>;
28
76
 
29
77
  function isSqliteBindingValue(value: unknown): boolean {
@@ -6,6 +6,7 @@ import type {
6
6
  SqliteDatabase,
7
7
  SqliteExecuteResult,
8
8
  SqliteTransactionDatabase,
9
+ SynchronousSqliteTransactionDatabase,
9
10
  } from "@/common/database/config";
10
11
  import { db } from "./drizzle";
11
12
 
@@ -14,7 +15,20 @@ class FakeSqliteDatabase implements SqliteDatabase {
14
15
  transactionTimeouts: Array<number | undefined> = [];
15
16
  transactionNames: Array<string | undefined> = [];
16
17
 
17
- async exec(): Promise<void> {}
18
+ async exec(
19
+ sql: string,
20
+ callback?: (row: unknown[], columns: string[]) => void,
21
+ ): Promise<void> {
22
+ this.execSync(sql, callback);
23
+ }
24
+
25
+ execSync(
26
+ sql: string,
27
+ callback?: (row: unknown[], columns: string[]) => void,
28
+ ): void {
29
+ this.executeCalls.push({ sql });
30
+ callback?.([1], ["value"]);
31
+ }
18
32
 
19
33
  async execute(
20
34
  sql: string,
@@ -24,6 +38,11 @@ class FakeSqliteDatabase implements SqliteDatabase {
24
38
  return emptyResult();
25
39
  }
26
40
 
41
+ executeSync(sql: string, params?: SqliteBindings): SqliteExecuteResult {
42
+ this.executeCalls.push({ sql, params });
43
+ return emptyResult();
44
+ }
45
+
27
46
  async executeBatch(
28
47
  statements: SqliteBatchStatement[],
29
48
  ): Promise<SqliteExecuteResult[]> {
@@ -47,21 +66,39 @@ class FakeSqliteDatabase implements SqliteDatabase {
47
66
  timeoutMs?: number,
48
67
  name?: string,
49
68
  ): Promise<SqliteTransactionDatabase> {
69
+ return this.beginTransactionSync(timeoutMs, name);
70
+ }
71
+
72
+ beginTransactionSync(
73
+ timeoutMs?: number,
74
+ name?: string,
75
+ ): SynchronousSqliteTransactionDatabase {
50
76
  this.transactionTimeouts.push(timeoutMs);
51
77
  this.transactionNames.push(name);
52
78
  this.executeCalls.push({ sql: "BEGIN" });
53
79
  return {
54
80
  exec: async () => {},
81
+ execSync: () => {},
55
82
  execute: async (sql, params) => {
56
83
  this.executeCalls.push({ sql, params });
57
84
  return emptyResult();
58
85
  },
86
+ executeSync: (sql, params) => {
87
+ this.executeCalls.push({ sql, params });
88
+ return emptyResult();
89
+ },
59
90
  commit: async () => {
60
91
  this.executeCalls.push({ sql: "COMMIT" });
61
92
  },
93
+ commitSync: () => {
94
+ this.executeCalls.push({ sql: "COMMIT" });
95
+ },
62
96
  rollback: async () => {
63
97
  this.executeCalls.push({ sql: "ROLLBACK" });
64
98
  },
99
+ rollbackSync: () => {
100
+ this.executeCalls.push({ sql: "ROLLBACK" });
101
+ },
65
102
  };
66
103
  }
67
104
 
@@ -151,6 +188,50 @@ describe("Drizzle database transactions", () => {
151
188
  ]);
152
189
  });
153
190
 
191
+ test("exposes synchronous raw queries", async () => {
192
+ const nativeDb = new FakeSqliteDatabase();
193
+ const client = await db().createClient(testProviderContext(nativeDb));
194
+
195
+ client.executeSync("SELECT ?", 42);
196
+ expect(
197
+ client.executeSync<{ value: number }>("SELECT 1; SELECT 2"),
198
+ ).toEqual([{ value: 1 }]);
199
+
200
+ expect(nativeDb.executeCalls).toEqual([
201
+ { sql: "SELECT ?", params: [42] },
202
+ { sql: "SELECT 1; SELECT 2" },
203
+ ]);
204
+ });
205
+
206
+ test("commits and rolls back synchronous raw transactions", async () => {
207
+ const nativeDb = new FakeSqliteDatabase();
208
+ const client = await db().createClient(testProviderContext(nativeDb));
209
+
210
+ const value = client.transactionSync(
211
+ (tx) => {
212
+ tx.executeSync("INSERT INTO items(value) VALUES (?)", "inside");
213
+ return 42;
214
+ },
215
+ { name: "sync-drizzle", timeout: 120_000 },
216
+ );
217
+ expect(value).toBe(42);
218
+ expect(() =>
219
+ client.transactionSync(() => {
220
+ throw new Error("callback failed");
221
+ }),
222
+ ).toThrow("callback failed");
223
+
224
+ expect(nativeDb.transactionTimeouts).toEqual([120_000, undefined]);
225
+ expect(nativeDb.transactionNames).toEqual(["sync-drizzle", undefined]);
226
+ expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
227
+ "BEGIN",
228
+ "INSERT INTO items(value) VALUES (?)",
229
+ "COMMIT",
230
+ "BEGIN",
231
+ "ROLLBACK",
232
+ ]);
233
+ });
234
+
154
235
  test("validates zero, negative, and non-finite transaction timeouts", async () => {
155
236
  const client = await db().createClient(
156
237
  testProviderContext(new FakeSqliteDatabase()),
package/src/db/drizzle.ts CHANGED
@@ -6,20 +6,22 @@ import {
6
6
  import type {
7
7
  DatabaseProvider,
8
8
  DatabaseProviderContext,
9
- RawAccess,
10
- SqliteProfilingOptions,
11
- SqliteTransactionOptions,
12
9
  SqliteDatabase,
10
+ SqliteProfilingOptions,
13
11
  SqliteTransactionDatabase,
12
+ SqliteTransactionOptions,
13
+ SynchronousRawAccess,
14
+ SynchronousTransactionAccess,
14
15
  } from "@/common/database/config";
15
- import { getLogger } from "@/common/log";
16
16
  import {
17
17
  isManualTransactionControl,
18
18
  MIGRATION_TRANSACTION_TIMEOUT_MS,
19
+ runSqliteTransactionSync,
19
20
  toSqliteBindings,
20
21
  validateTransactionName,
21
22
  validateTransactionTimeout,
22
23
  } from "@/common/database/shared";
24
+ import { getLogger } from "@/common/log";
23
25
  import { sha256Hex } from "@/utils/crypto";
24
26
 
25
27
  export type { SQLiteTable } from "drizzle-orm/sqlite-core";
@@ -42,7 +44,7 @@ type DrizzleDatabase<TSchema extends DrizzleSchema> = Omit<
42
44
  SqliteRemoteDatabase<TSchema>,
43
45
  "transaction"
44
46
  > &
45
- Omit<RawAccess, "transaction"> & {
47
+ Omit<SynchronousRawAccess, "transaction"> & {
46
48
  transaction: <T>(
47
49
  callback: (tx: DrizzleDatabase<TSchema>) => Promise<T> | T,
48
50
  options?: SqliteTransactionOptions,
@@ -114,6 +116,7 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
114
116
  const nativeDb = await nativeDatabaseProvider.open(ctx.actorId);
115
117
  let closed = false;
116
118
  let manualTransactionWarned = false;
119
+ let synchronousTransactionActive = false;
117
120
  const ensureOpen = () => {
118
121
  if (closed) {
119
122
  throw new Error(
@@ -121,6 +124,15 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
121
124
  );
122
125
  }
123
126
  };
127
+ const ensureSynchronousTransactionClient = (
128
+ transactionScoped: boolean,
129
+ ) => {
130
+ if (!transactionScoped && synchronousTransactionActive) {
131
+ throw new Error(
132
+ "Use the transaction callback's tx value for queries inside db.transactionSync().",
133
+ );
134
+ }
135
+ };
124
136
 
125
137
  const createDrizzleClient = (
126
138
  target: SqliteDatabase | SqliteTransactionDatabase,
@@ -132,6 +144,7 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
132
144
  method: "run" | "all" | "values" | "get",
133
145
  ) => {
134
146
  ensureOpen();
147
+ ensureSynchronousTransactionClient(transactionScoped);
135
148
  warnForManualTransaction(query, transactionScoped);
136
149
 
137
150
  const start = performance.now();
@@ -186,6 +199,7 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
186
199
  query: string,
187
200
  ...args: unknown[]
188
201
  ): Promise<TRow[]> => {
202
+ ensureSynchronousTransactionClient(transactionScoped);
189
203
  return await executeRaw<TRow>(
190
204
  target,
191
205
  ctx,
@@ -196,12 +210,34 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
196
210
  warnForManualTransaction(query, transactionScoped),
197
211
  );
198
212
  };
213
+ drizzleDb.executeSync = <
214
+ TRow extends Record<string, unknown> = Record<
215
+ string,
216
+ unknown
217
+ >,
218
+ >(
219
+ query: string,
220
+ ...args: unknown[]
221
+ ): TRow[] => {
222
+ ensureSynchronousTransactionClient(transactionScoped);
223
+ return executeRawSync<TRow>(
224
+ target,
225
+ ctx,
226
+ ensureOpen,
227
+ query,
228
+ args,
229
+ () =>
230
+ warnForManualTransaction(query, transactionScoped),
231
+ );
232
+ };
199
233
  drizzleDb.transaction = async <T>(
200
234
  transactionCallback: (
201
235
  tx: DrizzleDatabase<TSchema>,
202
236
  ) => Promise<T> | T,
203
237
  options?: SqliteTransactionOptions,
204
238
  ): Promise<T> => {
239
+ ensureOpen();
240
+ ensureSynchronousTransactionClient(transactionScoped);
205
241
  validateTransactionTimeout(options?.timeout);
206
242
  validateTransactionName(options?.name);
207
243
  const transaction = await nativeDb.beginTransaction(
@@ -222,6 +258,38 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
222
258
  throw error;
223
259
  }
224
260
  };
261
+ drizzleDb.transactionSync = <T>(
262
+ transactionCallback: (
263
+ tx: SynchronousTransactionAccess,
264
+ ) => T,
265
+ options?: Omit<SqliteTransactionOptions, "experimental">,
266
+ ): T => {
267
+ ensureOpen();
268
+ if (transactionScoped || synchronousTransactionActive) {
269
+ throw new Error(
270
+ "Nested synchronous SQLite transactions are not supported.",
271
+ );
272
+ }
273
+ return runSqliteTransactionSync(
274
+ nativeDb,
275
+ (transaction) => {
276
+ const transactionClient = createDrizzleClient(
277
+ transaction,
278
+ true,
279
+ );
280
+ const tx: SynchronousTransactionAccess = {
281
+ executeSync: transactionClient.executeSync,
282
+ };
283
+ synchronousTransactionActive = true;
284
+ try {
285
+ return transactionCallback(tx);
286
+ } finally {
287
+ synchronousTransactionActive = false;
288
+ }
289
+ },
290
+ options,
291
+ );
292
+ };
225
293
  drizzleDb.close = async () => {
226
294
  if (!closed) {
227
295
  closed = true;
@@ -248,7 +316,7 @@ export function db<TSchema extends DrizzleSchema = Record<string, never>>({
248
316
  manualTransactionWarned = true;
249
317
  getLogger("database").warn(
250
318
  { actorId: ctx.actorId },
251
- "Manual cross-call SQLite transactions can interleave with other actor work. Use db.transaction() for coordinated transactions. Set warnOnManualTransactions: false in your db(...) configuration to disable this warning.",
319
+ "Manual cross-call SQLite transactions can interleave with other actor work. Use db.transaction() or db.transactionSync() for coordinated transactions. Set warnOnManualTransactions: false in your db(...) configuration to disable this warning.",
252
320
  );
253
321
  };
254
322
 
@@ -431,3 +499,65 @@ async function executeRaw<TRow extends Record<string, unknown>>(
431
499
  }
432
500
  }
433
501
  }
502
+
503
+ function executeRawSync<TRow extends Record<string, unknown>>(
504
+ db: SqliteDatabase | SqliteTransactionDatabase,
505
+ ctx: DatabaseProviderContext,
506
+ ensureOpen: () => void,
507
+ query: string,
508
+ args: unknown[],
509
+ warnForManualTransaction: () => void,
510
+ ): TRow[] {
511
+ ensureOpen();
512
+ warnForManualTransaction();
513
+ if (!db.executeSync) {
514
+ throw new Error(
515
+ "Synchronous SQLite queries are only available in the Node.js native runtime.",
516
+ );
517
+ }
518
+
519
+ const start = performance.now();
520
+ const kvReadsBefore = ctx.metrics?.totalKvReads ?? 0;
521
+ const kvWritesBefore = ctx.metrics?.totalKvWrites ?? 0;
522
+ try {
523
+ if (args.length > 0) {
524
+ const { rows, columns } = db.executeSync(
525
+ query,
526
+ toSqliteBindings(args),
527
+ );
528
+ return rows.map((row) => rowToObject<TRow>(row, columns));
529
+ }
530
+
531
+ if (!hasMultipleStatements(query)) {
532
+ const { rows, columns } = db.executeSync(query, undefined);
533
+ return rows.map((row) => rowToObject<TRow>(row, columns));
534
+ }
535
+
536
+ if (!db.execSync) {
537
+ throw new Error(
538
+ "Synchronous SQLite queries are only available in the Node.js native runtime.",
539
+ );
540
+ }
541
+ const results: Record<string, unknown>[] = [];
542
+ let columnNames: string[] | null = null;
543
+ db.execSync(query, (row, columns) => {
544
+ if (!columnNames) {
545
+ columnNames = columns;
546
+ }
547
+ results.push(rowToObject(row, columnNames));
548
+ });
549
+ return results as TRow[];
550
+ } finally {
551
+ const durationMs = performance.now() - start;
552
+ ctx.metrics?.trackSql(query, durationMs);
553
+ if (ctx.metrics) {
554
+ ctx.log?.debug({
555
+ msg: "sql query",
556
+ query: query.slice(0, 120),
557
+ durationMs,
558
+ kvReads: ctx.metrics.totalKvReads - kvReadsBefore,
559
+ kvWrites: ctx.metrics.totalKvWrites - kvWritesBefore,
560
+ });
561
+ }
562
+ }
563
+ }
package/src/db/mod.ts CHANGED
@@ -12,5 +12,7 @@ export type {
12
12
  SqliteProfilingOptions,
13
13
  SqliteQueryResult,
14
14
  SqliteTransactionOptions,
15
+ SynchronousRawAccess,
16
+ SynchronousTransactionAccess,
15
17
  } from "@/common/database/config";
16
18
  export { db } from "@/common/database/mod";