rivetkit 2.3.30 → 3.0.0-preview.2

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 (135) hide show
  1. package/dist/browser/client.d.ts +58 -9
  2. package/dist/browser/client.js +193 -20
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.js +1 -2
  5. package/dist/browser/inspector/client.js.map +1 -1
  6. package/dist/tsup/{chunk-P2PHHJMH.cjs → chunk-25MUHNWE.cjs} +326 -166
  7. package/dist/tsup/chunk-25MUHNWE.cjs.map +1 -0
  8. package/dist/tsup/{chunk-5SN6GAYX.cjs → chunk-3SPYZMME.cjs} +3 -3
  9. package/dist/tsup/{chunk-5SN6GAYX.cjs.map → chunk-3SPYZMME.cjs.map} +1 -1
  10. package/dist/tsup/{chunk-4SNS3PYZ.js → chunk-3URL73AV.js} +2 -2
  11. package/dist/tsup/{chunk-EKAQ7HG2.js → chunk-442XBCAF.js} +3 -3
  12. package/dist/tsup/{chunk-CORYIKDC.js → chunk-5F7FWAJI.js} +10 -11
  13. package/dist/tsup/chunk-5F7FWAJI.js.map +1 -0
  14. package/dist/tsup/{chunk-I472KTIC.js → chunk-6IGGYL2C.js} +185 -25
  15. package/dist/tsup/chunk-6IGGYL2C.js.map +1 -0
  16. package/dist/tsup/{chunk-JI6GZ2C2.js → chunk-A2CF6PFH.js} +18 -1
  17. package/dist/tsup/chunk-A2CF6PFH.js.map +1 -0
  18. package/dist/tsup/{chunk-OUB7SGSA.js → chunk-ANNVZW6B.js} +2 -2
  19. package/dist/tsup/{chunk-ZIIQQHC3.cjs → chunk-BCG7PTBS.cjs} +4 -4
  20. package/dist/tsup/{chunk-ZIIQQHC3.cjs.map → chunk-BCG7PTBS.cjs.map} +1 -1
  21. package/dist/tsup/{chunk-OJFVABRH.cjs → chunk-CJ3MX5MI.cjs} +7 -7
  22. package/dist/tsup/{chunk-OJFVABRH.cjs.map → chunk-CJ3MX5MI.cjs.map} +1 -1
  23. package/dist/tsup/{chunk-GEUBEODU.cjs → chunk-EPPV7WB2.cjs} +3 -3
  24. package/dist/tsup/{chunk-GEUBEODU.cjs.map → chunk-EPPV7WB2.cjs.map} +1 -1
  25. package/dist/tsup/{chunk-MXV26ZBB.js → chunk-OLZ4KAFL.js} +4 -4
  26. package/dist/tsup/{chunk-5UUOECX2.cjs → chunk-QDRZMCX5.cjs} +10 -10
  27. package/dist/tsup/{chunk-5UUOECX2.cjs.map → chunk-QDRZMCX5.cjs.map} +1 -1
  28. package/dist/tsup/{chunk-MYWZCDML.cjs → chunk-RTWH3AX3.cjs} +19 -2
  29. package/dist/tsup/chunk-RTWH3AX3.cjs.map +1 -0
  30. package/dist/tsup/{chunk-H3SGQEHZ.js → chunk-WGKL7HMH.js} +2 -2
  31. package/dist/tsup/{chunk-6LOYTFPZ.js → chunk-WGQHG26S.js} +2 -2
  32. package/dist/tsup/{chunk-2BIRGEIU.cjs → chunk-XSOHGU4G.cjs} +10 -11
  33. package/dist/tsup/chunk-XSOHGU4G.cjs.map +1 -0
  34. package/dist/tsup/{chunk-ABPZG7QV.cjs → chunk-YZVVID47.cjs} +3 -3
  35. package/dist/tsup/{chunk-ABPZG7QV.cjs.map → chunk-YZVVID47.cjs.map} +1 -1
  36. package/dist/tsup/client/mod.cjs +6 -6
  37. package/dist/tsup/client/mod.d.cts +15 -3
  38. package/dist/tsup/client/mod.d.ts +15 -3
  39. package/dist/tsup/client/mod.js +5 -5
  40. package/dist/tsup/common/log.cjs +2 -2
  41. package/dist/tsup/common/log.js +1 -1
  42. package/dist/tsup/common/websocket.cjs +3 -3
  43. package/dist/tsup/common/websocket.js +2 -2
  44. package/dist/tsup/{config-osdJqQwX.d.cts → config-8NgsxIcT.d.cts} +43 -12
  45. package/dist/tsup/{config-ey2I2zz5.d.ts → config-B2wlj9wx.d.cts} +7 -1
  46. package/dist/tsup/{config-ey2I2zz5.d.cts → config-B2wlj9wx.d.ts} +7 -1
  47. package/dist/tsup/{config-Cv6pjCt-.d.ts → config-DJ3KW-fe.d.ts} +43 -12
  48. package/dist/tsup/{context-CoVbKBuk.d.ts → context-BL8kUGjq.d.ts} +2 -2
  49. package/dist/tsup/{context-DpjrM7n7.d.cts → context-_RoE_YHL.d.cts} +2 -2
  50. package/dist/tsup/db/drizzle.cjs +2 -2
  51. package/dist/tsup/db/drizzle.d.cts +1 -1
  52. package/dist/tsup/db/drizzle.d.ts +1 -1
  53. package/dist/tsup/db/drizzle.js +1 -1
  54. package/dist/tsup/db/mod.cjs +3 -3
  55. package/dist/tsup/db/mod.d.cts +2 -2
  56. package/dist/tsup/db/mod.d.ts +2 -2
  57. package/dist/tsup/db/mod.js +2 -2
  58. package/dist/tsup/dynamic/mod.d.cts +2 -2
  59. package/dist/tsup/dynamic/mod.d.ts +2 -2
  60. package/dist/tsup/inspector/mod.cjs +5 -5
  61. package/dist/tsup/inspector/mod.js +4 -4
  62. package/dist/tsup/inspector/workflow.cjs +4 -4
  63. package/dist/tsup/inspector/workflow.js +3 -3
  64. package/dist/tsup/inspector-tab/mod.d.cts +3 -3
  65. package/dist/tsup/inspector-tab/mod.d.ts +3 -3
  66. package/dist/tsup/mod.cjs +254 -166
  67. package/dist/tsup/mod.cjs.map +1 -1
  68. package/dist/tsup/mod.d.cts +4 -4
  69. package/dist/tsup/mod.d.ts +4 -4
  70. package/dist/tsup/mod.js +168 -80
  71. package/dist/tsup/mod.js.map +1 -1
  72. package/dist/tsup/test/mod.cjs +7 -7
  73. package/dist/tsup/test/mod.d.cts +2 -2
  74. package/dist/tsup/test/mod.d.ts +2 -2
  75. package/dist/tsup/test/mod.js +5 -5
  76. package/dist/tsup/unstable/migrations.d.cts +1 -1
  77. package/dist/tsup/unstable/migrations.d.ts +1 -1
  78. package/dist/tsup/utils.cjs +2 -2
  79. package/dist/tsup/utils.js +1 -1
  80. package/dist/tsup/workflow/mod.cjs +17 -17
  81. package/dist/tsup/workflow/mod.d.cts +4 -4
  82. package/dist/tsup/workflow/mod.d.ts +4 -4
  83. package/dist/tsup/workflow/mod.js +5 -5
  84. package/package.json +9 -10
  85. package/src/actor/keys.ts +86 -0
  86. package/src/client/actor-conn.ts +92 -6
  87. package/src/client/actor-handle.ts +21 -3
  88. package/src/client/client.ts +6 -0
  89. package/src/client/config.ts +12 -1
  90. package/src/client/lifecycle-errors.ts +16 -1
  91. package/src/client/retry-budget.ts +78 -0
  92. package/src/client/utils.ts +13 -2
  93. package/src/common/database/config.ts +7 -1
  94. package/src/common/database/native-database.ts +17 -6
  95. package/src/common/router.ts +1 -1
  96. package/src/engine-api/actors.ts +3 -3
  97. package/src/engine-client/actor-websocket-client.ts +4 -2
  98. package/src/engine-client/api-endpoints.ts +44 -14
  99. package/src/engine-client/mod.ts +2 -2
  100. package/src/registry/config/envoy.ts +1 -1
  101. package/src/registry/config/index.ts +28 -31
  102. package/src/registry/index.ts +51 -19
  103. package/src/registry/napi-runtime.ts +3 -2
  104. package/src/registry/native.ts +126 -34
  105. package/src/registry/runtime.ts +27 -10
  106. package/src/registry/wasm-runtime.ts +6 -3
  107. package/src/utils/env-vars.ts +15 -10
  108. package/dist/tsup/chunk-2BIRGEIU.cjs.map +0 -1
  109. package/dist/tsup/chunk-CORYIKDC.js.map +0 -1
  110. package/dist/tsup/chunk-I472KTIC.js.map +0 -1
  111. package/dist/tsup/chunk-JI6GZ2C2.js.map +0 -1
  112. package/dist/tsup/chunk-MYWZCDML.cjs.map +0 -1
  113. package/dist/tsup/chunk-P2PHHJMH.cjs.map +0 -1
  114. package/src/actor/config.test.ts +0 -78
  115. package/src/actor/keys.test.ts +0 -275
  116. package/src/actor/utils.test.ts +0 -48
  117. package/src/client/test.ts +0 -44
  118. package/src/client/token-provider.test.ts +0 -86
  119. package/src/common/database/mod.test.ts +0 -364
  120. package/src/common/database/native-database.test.ts +0 -220
  121. package/src/common/inline-websocket-adapter.test.ts +0 -51
  122. package/src/db/drizzle.test.ts +0 -164
  123. package/src/registry/config/index.test.ts +0 -25
  124. package/src/registry/run-handler-coordinator.test.ts +0 -252
  125. package/src/registry/runtime.test.ts +0 -124
  126. package/src/unstable/migrations.test.ts +0 -170
  127. package/src/utils/endpoint-parser.test.ts +0 -206
  128. package/src/utils.test.ts +0 -34
  129. package/src/workflow/driver.test.ts +0 -62
  130. /package/dist/tsup/{chunk-4SNS3PYZ.js.map → chunk-3URL73AV.js.map} +0 -0
  131. /package/dist/tsup/{chunk-EKAQ7HG2.js.map → chunk-442XBCAF.js.map} +0 -0
  132. /package/dist/tsup/{chunk-OUB7SGSA.js.map → chunk-ANNVZW6B.js.map} +0 -0
  133. /package/dist/tsup/{chunk-MXV26ZBB.js.map → chunk-OLZ4KAFL.js.map} +0 -0
  134. /package/dist/tsup/{chunk-H3SGQEHZ.js.map → chunk-WGKL7HMH.js.map} +0 -0
  135. /package/dist/tsup/{chunk-6LOYTFPZ.js.map → chunk-WGQHG26S.js.map} +0 -0
@@ -1,364 +0,0 @@
1
- import { pino } from "pino";
2
- import { beforeEach, describe, expect, test } from "vitest";
3
- import { configureBaseLogger } from "@/common/log";
4
- import type {
5
- DatabaseProviderContext,
6
- SqliteBindings,
7
- SqliteDatabase,
8
- SqliteExecuteResult,
9
- SqliteTransactionDatabase,
10
- } from "./config";
11
- import { db, registerNativeStateTransactionOpener } from "./mod";
12
-
13
- let logLines: string[];
14
-
15
- class FakeSqliteDatabase implements SqliteDatabase {
16
- failSql = new Map<string, Error>();
17
- executeCalls: { sql: string; params?: SqliteBindings }[] = [];
18
- stateTransactionTimeouts: Array<number | undefined> = [];
19
- transactionTimeouts: Array<number | undefined> = [];
20
- transactionNames: Array<string | undefined> = [];
21
-
22
- async exec(): Promise<void> {}
23
-
24
- async execute(
25
- sql: string,
26
- params?: SqliteBindings,
27
- ): Promise<SqliteExecuteResult> {
28
- this.record(sql, params);
29
- return emptyResult();
30
- }
31
-
32
- async beginTransaction(
33
- timeoutMs?: number,
34
- name?: string,
35
- ): Promise<SqliteTransactionDatabase> {
36
- this.transactionTimeouts.push(timeoutMs);
37
- this.transactionNames.push(name);
38
- this.record("BEGIN");
39
- return {
40
- exec: async () => {},
41
- execute: async (sql, params) => {
42
- this.record(sql, params);
43
- return emptyResult();
44
- },
45
- commit: async () => this.record("COMMIT"),
46
- rollback: async () => this.record("ROLLBACK"),
47
- };
48
- }
49
- async beginStateTransaction(
50
- timeoutMs?: number,
51
- ): Promise<SqliteTransactionDatabase> {
52
- this.stateTransactionTimeouts.push(timeoutMs);
53
- this.record("BEGIN_STATE");
54
- return {
55
- exec: async () => {},
56
- execute: async (sql, params) => {
57
- this.record(sql, params);
58
- return emptyResult();
59
- },
60
- commit: async () => this.record("COMMIT"),
61
- rollback: async () => this.record("ROLLBACK"),
62
- };
63
- }
64
-
65
- async executeBatch(
66
- statements: Array<{ sql: string; params?: SqliteBindings }>,
67
- ): Promise<SqliteExecuteResult[]> {
68
- const results: SqliteExecuteResult[] = [];
69
- for (const statement of statements) {
70
- results.push(await this.execute(statement.sql, statement.params));
71
- }
72
- return results;
73
- }
74
-
75
- async run(sql: string, params?: SqliteBindings): Promise<void> {
76
- await this.execute(sql, params);
77
- }
78
-
79
- async query(sql: string, params?: SqliteBindings) {
80
- const { columns, rows } = await this.execute(sql, params);
81
- return { columns, rows };
82
- }
83
-
84
- async close(): Promise<void> {}
85
-
86
- private record(sql: string, params?: SqliteBindings): void {
87
- this.executeCalls.push({ sql, params });
88
- const error = this.failSql.get(sql);
89
- if (error) throw error;
90
- }
91
- }
92
-
93
- function emptyResult(): SqliteExecuteResult {
94
- return {
95
- columns: [],
96
- rows: [],
97
- changes: 0,
98
- lastInsertRowId: null,
99
- };
100
- }
101
-
102
- function testProviderContext(
103
- database: FakeSqliteDatabase,
104
- includeStateTransactions = false,
105
- ): DatabaseProviderContext {
106
- return {
107
- actorId: "actor-a",
108
- kv: {
109
- batchPut: async () => {},
110
- batchGet: async (keys) => keys.map(() => null),
111
- batchDelete: async () => {},
112
- deleteRange: async () => {},
113
- },
114
- nativeDatabaseProvider: includeStateTransactions
115
- ? registerNativeStateTransactionOpener(
116
- { open: async () => database },
117
- async (timeoutMs?: number) =>
118
- await database.beginStateTransaction(timeoutMs),
119
- )
120
- : { open: async () => database },
121
- };
122
- }
123
-
124
- describe("db", () => {
125
- beforeEach(() => {
126
- logLines = [];
127
- configureBaseLogger(
128
- pino(
129
- { level: "warn", base: {}, timestamp: false },
130
- { write: (line: string) => logLines.push(line) },
131
- ),
132
- );
133
- });
134
-
135
- test("runs onMigrate through the shared transaction handle", async () => {
136
- const nativeDb = new FakeSqliteDatabase();
137
- const provider = db({
138
- onMigrate: async (client) => {
139
- await client.execute(
140
- "CREATE TABLE items(id INTEGER PRIMARY KEY)",
141
- );
142
- },
143
- });
144
- const client = await provider.createClient(
145
- testProviderContext(nativeDb),
146
- );
147
- await provider.onMigrate(client);
148
-
149
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
150
- "BEGIN",
151
- "SAVEPOINT __rivet_on_migrate",
152
- "CREATE TABLE items(id INTEGER PRIMARY KEY)",
153
- "RELEASE SAVEPOINT __rivet_on_migrate",
154
- "COMMIT",
155
- ]);
156
- expect(nativeDb.transactionTimeouts).toEqual([300_000]);
157
- expect(nativeDb.transactionNames).toEqual(["rivetkit-migration"]);
158
- });
159
-
160
- test("exposes profiling controls to the actor runtime", () => {
161
- const provider = db({
162
- profiling: {
163
- enabled: false,
164
- maxTrackedStatementFingerprints: 12,
165
- baselineSampleRate: 0.25,
166
- },
167
- });
168
-
169
- expect(provider.sqliteProfiling).toEqual({
170
- enabled: false,
171
- maxTrackedStatementFingerprints: 12,
172
- baselineSampleRate: 0.25,
173
- });
174
- });
175
-
176
- test("rolls back migrations when onMigrate fails", async () => {
177
- const nativeDb = new FakeSqliteDatabase();
178
- const provider = db({
179
- onMigrate: async () => {
180
- throw new Error("migration failed");
181
- },
182
- });
183
- const client = await provider.createClient(
184
- testProviderContext(nativeDb),
185
- );
186
- await expect(provider.onMigrate(client)).rejects.toThrow(
187
- "migration failed",
188
- );
189
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
190
- "BEGIN",
191
- "SAVEPOINT __rivet_on_migrate",
192
- "ROLLBACK TO SAVEPOINT __rivet_on_migrate",
193
- "RELEASE SAVEPOINT __rivet_on_migrate",
194
- "ROLLBACK",
195
- ]);
196
- });
197
-
198
- test("commits transaction work and forwards timeout", async () => {
199
- const nativeDb = new FakeSqliteDatabase();
200
- const client = await db().createClient(testProviderContext(nativeDb));
201
- const value = await client.transaction(
202
- async (tx) => {
203
- await tx.execute(
204
- "INSERT INTO items(value) VALUES (?)",
205
- "inside",
206
- );
207
- return 42;
208
- },
209
- { name: "insert-item", timeout: 120_000 },
210
- );
211
- expect(value).toBe(42);
212
- expect(nativeDb.transactionTimeouts).toEqual([120_000]);
213
- expect(nativeDb.transactionNames).toEqual(["insert-item"]);
214
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
215
- "BEGIN",
216
- "INSERT INTO items(value) VALUES (?)",
217
- "COMMIT",
218
- ]);
219
- });
220
-
221
- test("validates transaction names", async () => {
222
- const client = await db().createClient(
223
- testProviderContext(new FakeSqliteDatabase()),
224
- );
225
- await expect(
226
- client.transaction(async () => {}, { name: "" }),
227
- ).rejects.toThrow("must not be empty");
228
- });
229
-
230
- test("defers the configured transaction name byte limit to the runtime", async () => {
231
- const nativeDb = new FakeSqliteDatabase();
232
- const client = await db().createClient(testProviderContext(nativeDb));
233
- const name = "x".repeat(129);
234
-
235
- await client.transaction(async () => {}, { name });
236
-
237
- expect(nativeDb.transactionNames).toEqual([name]);
238
- });
239
-
240
- test("rolls back a transaction when the callback throws", async () => {
241
- const nativeDb = new FakeSqliteDatabase();
242
- const client = await db().createClient(testProviderContext(nativeDb));
243
- await expect(
244
- client.transaction(async (tx) => {
245
- await tx.execute(
246
- "INSERT INTO items(value) VALUES (?)",
247
- "inside",
248
- );
249
- throw new Error("callback failed");
250
- }),
251
- ).rejects.toThrow("callback failed");
252
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
253
- "BEGIN",
254
- "INSERT INTO items(value) VALUES (?)",
255
- "ROLLBACK",
256
- ]);
257
- });
258
-
259
- test("uses the state-aware transaction bridge when explicitly requested", async () => {
260
- const nativeDb = new FakeSqliteDatabase();
261
- const client = await db().createClient(
262
- testProviderContext(nativeDb, true),
263
- );
264
-
265
- await client.transaction(
266
- async (tx) => {
267
- await tx.execute(
268
- "INSERT INTO items(value) VALUES (?)",
269
- "inside",
270
- );
271
- },
272
- {
273
- timeout: 120_000,
274
- experimental: { includeState: true },
275
- },
276
- );
277
-
278
- expect(nativeDb.transactionTimeouts).toEqual([]);
279
- expect(nativeDb.stateTransactionTimeouts).toEqual([120_000]);
280
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
281
- "BEGIN_STATE",
282
- "INSERT INTO items(value) VALUES (?)",
283
- "COMMIT",
284
- ]);
285
- });
286
-
287
- test("rolls back state-aware transactions when the callback throws", async () => {
288
- const nativeDb = new FakeSqliteDatabase();
289
- const client = await db().createClient(
290
- testProviderContext(nativeDb, true),
291
- );
292
-
293
- await expect(
294
- client.transaction(
295
- async (tx) => {
296
- await tx.execute(
297
- "INSERT INTO items(value) VALUES (?)",
298
- "inside",
299
- );
300
- throw new Error("callback failed");
301
- },
302
- { experimental: { includeState: true } },
303
- ),
304
- ).rejects.toThrow("callback failed");
305
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
306
- "BEGIN_STATE",
307
- "INSERT INTO items(value) VALUES (?)",
308
- "ROLLBACK",
309
- ]);
310
- });
311
-
312
- test("rejects nested state-aware transactions", async () => {
313
- const nativeDb = new FakeSqliteDatabase();
314
- const client = await db().createClient(
315
- testProviderContext(nativeDb, true),
316
- );
317
-
318
- await expect(
319
- client.transaction(
320
- async (tx) => {
321
- await tx.transaction(async () => {}, {
322
- experimental: { includeState: true },
323
- });
324
- },
325
- { experimental: { includeState: true } },
326
- ),
327
- ).rejects.toThrow("not supported for nested transactions");
328
- expect(nativeDb.executeCalls.map(({ sql }) => sql)).toEqual([
329
- "BEGIN_STATE",
330
- "ROLLBACK",
331
- ]);
332
- });
333
-
334
- test("validates transaction timeouts", async () => {
335
- const client = await db().createClient(
336
- testProviderContext(new FakeSqliteDatabase()),
337
- );
338
- for (const timeout of [0, -1, Number.NaN, Number.POSITIVE_INFINITY]) {
339
- await expect(
340
- client.transaction(async () => {}, { timeout }),
341
- ).rejects.toThrow("positive finite");
342
- }
343
- });
344
-
345
- test("warns once for manual cross-call transactions and names the opt-out", async () => {
346
- const client = await db().createClient(
347
- testProviderContext(new FakeSqliteDatabase()),
348
- );
349
- await client.execute("BEGIN");
350
- await client.execute("COMMIT");
351
- expect(logLines).toHaveLength(1);
352
- expect(JSON.parse(logLines[0] ?? "{}").msg).toContain(
353
- "Set warnOnManualTransactions: false",
354
- );
355
- });
356
-
357
- test("can suppress the manual transaction warning", async () => {
358
- const client = await db({
359
- warnOnManualTransactions: false,
360
- }).createClient(testProviderContext(new FakeSqliteDatabase()));
361
- await client.execute("BEGIN");
362
- expect(logLines).toHaveLength(0);
363
- });
364
- });
@@ -1,220 +0,0 @@
1
- import { describe, expect, test } from "vitest";
2
- import { BRIDGE_RIVET_ERROR_PREFIX } from "@/actor/errors";
3
- import {
4
- type JsNativeDatabaseLike,
5
- wrapJsNativeDatabase,
6
- } from "./native-database";
7
-
8
- type NativeParams = Parameters<JsNativeDatabaseLike["execute"]>[1];
9
- type NativeExecuteResult = Awaited<ReturnType<JsNativeDatabaseLike["execute"]>>;
10
-
11
- function deferred<T>() {
12
- let resolve!: (value: T) => void;
13
- let reject!: (error: unknown) => void;
14
- const promise = new Promise<T>((resolvePromise, rejectPromise) => {
15
- resolve = resolvePromise;
16
- reject = rejectPromise;
17
- });
18
- return { promise, resolve, reject };
19
- }
20
-
21
- class FakeNativeDatabase implements JsNativeDatabaseLike {
22
- async beginTransaction() {
23
- return {
24
- exec: async (_sql: string) => this.exec(),
25
- execute: async (sql: string, params?: NativeParams) =>
26
- this.execute(sql, params),
27
- commit: async () => {},
28
- rollback: async () => {},
29
- };
30
- }
31
- active = 0;
32
- maxActive = 0;
33
- closed = false;
34
- executeCalls: { sql: string; params?: NativeParams; write: boolean }[] = [];
35
- #pending: ReturnType<typeof deferred<NativeExecuteResult>>[] = [];
36
-
37
- async exec() {
38
- return { columns: [], rows: [] };
39
- }
40
-
41
- async execute(sql: string, params?: NativeParams) {
42
- return await this.#startExecute(sql, params, false);
43
- }
44
-
45
- async query(sql: string, params?: NativeParams) {
46
- const { columns, rows } = await this.execute(sql, params);
47
- return { columns, rows };
48
- }
49
-
50
- async run(sql: string, params?: NativeParams) {
51
- const { changes } = await this.execute(sql, params);
52
- return { changes };
53
- }
54
-
55
- takeLastKvError() {
56
- return null;
57
- }
58
-
59
- async close() {
60
- this.closed = true;
61
- }
62
-
63
- resolveNext(result: Partial<NativeExecuteResult> = {}) {
64
- const pending = this.#pending.shift();
65
- if (!pending) {
66
- throw new Error("no pending native execute call");
67
- }
68
- pending.resolve({
69
- columns: [],
70
- rows: [],
71
- changes: 0,
72
- lastInsertRowId: null,
73
- ...result,
74
- });
75
- }
76
-
77
- rejectNext(error: unknown) {
78
- const pending = this.#pending.shift();
79
- if (!pending) {
80
- throw new Error("no pending native execute call");
81
- }
82
- pending.reject(error);
83
- }
84
-
85
- async #startExecute(
86
- sql: string,
87
- params: NativeParams,
88
- write: boolean,
89
- ): Promise<NativeExecuteResult> {
90
- this.executeCalls.push({ sql, params, write });
91
- this.active++;
92
- this.maxActive = Math.max(this.maxActive, this.active);
93
- const pending = deferred<NativeExecuteResult>();
94
- this.#pending.push(pending);
95
- try {
96
- return await pending.promise;
97
- } finally {
98
- this.active--;
99
- }
100
- }
101
- }
102
-
103
- describe("wrapJsNativeDatabase", () => {
104
- test("admits Promise.all read queries concurrently", async () => {
105
- const native = new FakeNativeDatabase();
106
- const db = wrapJsNativeDatabase(native);
107
-
108
- const first = db.query("SELECT 1");
109
- const second = db.query("SELECT 2");
110
-
111
- expect(native.maxActive).toBe(2);
112
- native.resolveNext({ columns: ["value"], rows: [[1]] });
113
- native.resolveNext({ columns: ["value"], rows: [[2]] });
114
-
115
- await expect(first).resolves.toEqual({
116
- columns: ["value"],
117
- rows: [[1]],
118
- });
119
- await expect(second).resolves.toEqual({
120
- columns: ["value"],
121
- rows: [[2]],
122
- });
123
- });
124
-
125
- test("normalizes supported sqlite bind values", async () => {
126
- const native = new FakeNativeDatabase();
127
- const db = wrapJsNativeDatabase(native);
128
- const blob = new Uint8Array([1, 2, 3]);
129
-
130
- const query = db.query("SELECT ?, ?, ?, ?, ?, ?, ?", [
131
- 1n,
132
- true,
133
- "text",
134
- 1.5,
135
- null,
136
- undefined,
137
- blob,
138
- ]);
139
-
140
- expect(native.executeCalls[0]?.params).toEqual([
141
- { kind: "int", intValue: 1 },
142
- { kind: "int", intValue: 1 },
143
- { kind: "text", textValue: "text" },
144
- { kind: "float", floatValue: 1.5 },
145
- { kind: "null" },
146
- { kind: "null" },
147
- { kind: "blob", blobValue: Buffer.from(blob) },
148
- ]);
149
-
150
- native.resolveNext({ columns: ["value"], rows: [[1]] });
151
-
152
- await expect(query).resolves.toEqual({
153
- columns: ["value"],
154
- rows: [[1]],
155
- });
156
- });
157
-
158
- test("returns native execute metadata", async () => {
159
- const native = new FakeNativeDatabase();
160
- const db = wrapJsNativeDatabase(native);
161
-
162
- const write = db.execute("INSERT INTO test VALUES (1)");
163
- native.resolveNext({ changes: 1, lastInsertRowId: 7 });
164
- await expect(write).resolves.toMatchObject({
165
- changes: 1,
166
- lastInsertRowId: 7,
167
- });
168
-
169
- const fallback = db.execute("SELECT last_insert_rowid()");
170
- await expect(fallback).resolves.toMatchObject({
171
- rows: [[7]],
172
- });
173
- });
174
-
175
- test("close waits for admitted native calls and rejects new work", async () => {
176
- const native = new FakeNativeDatabase();
177
- const db = wrapJsNativeDatabase(native);
178
-
179
- const query = db.query("SELECT 1");
180
- const close = db.close();
181
- await Promise.resolve();
182
-
183
- expect(native.closed).toBe(false);
184
- native.resolveNext({ columns: ["value"], rows: [[1]] });
185
-
186
- await query;
187
- await close;
188
-
189
- expect(native.closed).toBe(true);
190
- await expect(db.query("SELECT 2")).rejects.toThrow(
191
- "Database is closed",
192
- );
193
- });
194
-
195
- test("decodes sanitized native bridge errors without logging", async () => {
196
- const native = new FakeNativeDatabase();
197
- const db = wrapJsNativeDatabase(native);
198
- const query = db.execute("SELECT broken", [1, "two"]);
199
- const bridgeReason = `${BRIDGE_RIVET_ERROR_PREFIX}${JSON.stringify({
200
- group: "rivetkit",
201
- code: "internal_error",
202
- message: "An internal error occurred",
203
- statusCode: 500,
204
- })}`;
205
-
206
- native.rejectNext(new Error(bridgeReason));
207
-
208
- await expect(query).rejects.toMatchObject({
209
- name: "RivetError",
210
- group: "rivetkit",
211
- code: "internal_error",
212
- message: "An internal error occurred",
213
- });
214
-
215
- await query.catch((error) => {
216
- expect(error.stack).toContain("decodeBridgeRivetError");
217
- expect(error.stack).toContain("enrichNativeDatabaseError");
218
- });
219
- });
220
- });
@@ -1,51 +0,0 @@
1
- import { describe, expect, test, vi } from "vitest";
2
- import { InlineWebSocketAdapter } from "./inline-websocket-adapter";
3
-
4
- describe("InlineWebSocketAdapter", () => {
5
- test("buffers client messages until open completes", async () => {
6
- const events: string[] = [];
7
- const adapter = new InlineWebSocketAdapter({
8
- onOpen: () => {
9
- events.push("handler.open");
10
- },
11
- onMessage: (event: {
12
- data: string;
13
- rivetMessageIndex?: number;
14
- }) => {
15
- events.push(
16
- `handler.message:${event.data}:${event.rivetMessageIndex ?? "none"}`,
17
- );
18
- },
19
- onClose: () => {},
20
- onError: () => {},
21
- });
22
-
23
- adapter.clientWebSocket.addEventListener("open", () => {
24
- events.push("client.open");
25
- });
26
- adapter.actorWebSocket.addEventListener("open", () => {
27
- events.push("actor.open");
28
- });
29
- adapter.actorWebSocket.addEventListener(
30
- "message",
31
- (event: { data: string; rivetMessageIndex?: number }) => {
32
- events.push(
33
- `actor.message:${event.data}:${event.rivetMessageIndex ?? "none"}`,
34
- );
35
- },
36
- );
37
-
38
- adapter.dispatchClientMessageWithMetadata("hello", 7);
39
- expect(events).toEqual([]);
40
-
41
- await vi.waitFor(() => {
42
- expect(events).toEqual([
43
- "handler.open",
44
- "client.open",
45
- "actor.open",
46
- "handler.message:hello:7",
47
- "actor.message:hello:7",
48
- ]);
49
- });
50
- });
51
- });