@vibeorm/adapter-bun 1.0.2 → 1.1.0
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.
- package/package.json +2 -2
- package/src/index.ts +70 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeorm/adapter-bun",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Bun-native database adapter for VibeORM using bun:sql",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"bun": ">=1.1.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@vibeorm/runtime": "1.0
|
|
36
|
+
"@vibeorm/runtime": "1.1.0"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* tagged templates.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { DatabaseAdapter, QueryResult } from "@vibeorm/runtime";
|
|
9
|
+
import type { DatabaseAdapter, QueryResult, TransactionOptions } from "@vibeorm/runtime";
|
|
10
|
+
import { normalizeError } from "@vibeorm/runtime";
|
|
10
11
|
|
|
11
12
|
export type BunAdapterOptions = {
|
|
12
13
|
/** PostgreSQL connection URL. Falls back to DATABASE_URL env var if not provided. */
|
|
@@ -73,6 +74,7 @@ export type BunAdapterOptions = {
|
|
|
73
74
|
|
|
74
75
|
type SqlReserved = {
|
|
75
76
|
(strings: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
|
|
77
|
+
unsafe(query: string, values?: unknown[]): Promise<unknown[]>;
|
|
76
78
|
release(): void;
|
|
77
79
|
};
|
|
78
80
|
|
|
@@ -228,34 +230,80 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
|
|
|
228
230
|
return result as Record<string, unknown>[];
|
|
229
231
|
}
|
|
230
232
|
|
|
233
|
+
function isolationLevelToSql(params: { level: NonNullable<TransactionOptions["isolationLevel"]> }): string {
|
|
234
|
+
switch (params.level) {
|
|
235
|
+
case "ReadCommitted": return "READ COMMITTED";
|
|
236
|
+
case "RepeatableRead": return "REPEATABLE READ";
|
|
237
|
+
case "Serializable": return "SERIALIZABLE";
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
231
241
|
function createAdapter(sql: SqlInstance): DatabaseAdapter {
|
|
232
242
|
return {
|
|
233
243
|
async execute(params) {
|
|
234
|
-
|
|
244
|
+
try {
|
|
245
|
+
return await executeQuery(sql, params);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
throw normalizeError({ error: err });
|
|
248
|
+
}
|
|
235
249
|
},
|
|
236
250
|
|
|
237
251
|
async executeUnsafe(params) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
try {
|
|
253
|
+
const result = await sql.unsafe(params.text, params.values);
|
|
254
|
+
const resultAny = result as unknown as Record<string, unknown>;
|
|
255
|
+
let affectedRows: number;
|
|
256
|
+
if (typeof resultAny.count === "number") {
|
|
257
|
+
affectedRows = resultAny.count;
|
|
258
|
+
} else if (typeof resultAny.affectedRows === "number") {
|
|
259
|
+
affectedRows = resultAny.affectedRows;
|
|
260
|
+
} else {
|
|
261
|
+
affectedRows = (result as unknown[]).length;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
rows: result as Record<string, unknown>[],
|
|
265
|
+
affectedRows,
|
|
266
|
+
};
|
|
267
|
+
} catch (err) {
|
|
268
|
+
throw normalizeError({ error: err });
|
|
247
269
|
}
|
|
248
|
-
return {
|
|
249
|
-
rows: result as Record<string, unknown>[],
|
|
250
|
-
affectedRows,
|
|
251
|
-
};
|
|
252
270
|
},
|
|
253
271
|
|
|
254
|
-
async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
272
|
+
async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T> {
|
|
273
|
+
// Fast path: no custom options — use native sql.begin() for best performance
|
|
274
|
+
if (!options?.isolationLevel && !options?.timeout) {
|
|
275
|
+
try {
|
|
276
|
+
return await sql.begin(async (txSql: SqlInstance) => {
|
|
277
|
+
const txAdapter = createAdapter(txSql);
|
|
278
|
+
return fn(txAdapter);
|
|
279
|
+
});
|
|
280
|
+
} catch (err) {
|
|
281
|
+
throw normalizeError({ error: err });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Manual path: reserve a connection for custom BEGIN options
|
|
286
|
+
const reserved = await sql.reserve();
|
|
287
|
+
try {
|
|
288
|
+
const isolation = options?.isolationLevel
|
|
289
|
+
? ` ISOLATION LEVEL ${isolationLevelToSql({ level: options.isolationLevel })}`
|
|
290
|
+
: "";
|
|
291
|
+
await reserved.unsafe(`BEGIN${isolation}`);
|
|
292
|
+
|
|
293
|
+
if (options?.timeout) {
|
|
294
|
+
await reserved.unsafe(`SET LOCAL statement_timeout = ${Number(options.timeout)}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const txAdapter = createAdapter(reserved as unknown as SqlInstance);
|
|
298
|
+
const result = await fn(txAdapter);
|
|
299
|
+
await reserved.unsafe("COMMIT");
|
|
300
|
+
return result;
|
|
301
|
+
} catch (err) {
|
|
302
|
+
try { await reserved.unsafe("ROLLBACK"); } catch { /* rollback best-effort */ }
|
|
303
|
+
throw normalizeError({ error: err });
|
|
304
|
+
} finally {
|
|
305
|
+
reserved.release();
|
|
306
|
+
}
|
|
259
307
|
},
|
|
260
308
|
|
|
261
309
|
async connect() {
|
|
@@ -284,8 +332,8 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
|
|
|
284
332
|
return createAdapter(getSql()).executeUnsafe(params);
|
|
285
333
|
},
|
|
286
334
|
|
|
287
|
-
async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T
|
|
288
|
-
return createAdapter(getSql()).transaction(fn);
|
|
335
|
+
async transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T> {
|
|
336
|
+
return createAdapter(getSql()).transaction(fn, options);
|
|
289
337
|
},
|
|
290
338
|
|
|
291
339
|
async connect() {
|