@vibeorm/adapter-bun 2.4.1 → 4.0.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/LICENSE +21 -0
- package/README.md +44 -25
- package/dist/bun-version.d.ts +11 -0
- package/dist/config.d.ts +12 -0
- package/dist/connection-url.d.ts +18 -3
- package/dist/driver.d.ts +6 -2
- package/dist/index.d.ts +32 -10
- package/dist/index.js +430 -219
- package/dist/index.js.map +12 -11
- package/dist/params.d.ts +36 -22
- package/dist/template-cache.d.ts +2 -0
- package/dist/transaction-sql.d.ts +28 -1
- package/package.json +8 -6
- package/dist/config.d.ts.map +0 -1
- package/dist/connection-url.d.ts.map +0 -1
- package/dist/driver.d.ts.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/params.d.ts.map +0 -1
- package/dist/template-cache.d.ts.map +0 -1
- package/dist/transaction-sql.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,39 +2,57 @@
|
|
|
2
2
|
// packages/adapter-bun/src/driver.ts
|
|
3
3
|
var {SQL } = globalThis.Bun;
|
|
4
4
|
function createBunSql(params) {
|
|
5
|
-
return
|
|
5
|
+
return new SQL(params.url, params.options);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
// packages/adapter-bun/src/config.ts
|
|
9
|
+
var MINIMUM_BUN_VERSION = "1.4.1";
|
|
10
|
+
var MAX_BIND_PARAMETERS = 65535;
|
|
9
11
|
var SESSION_CONNECTION = Object.freeze({
|
|
10
12
|
max: 1,
|
|
11
13
|
closeGraceSeconds: 0.001
|
|
12
14
|
});
|
|
13
15
|
|
|
16
|
+
// packages/adapter-bun/src/bun-version.ts
|
|
17
|
+
import { VibeError } from "@vibeorm/schema";
|
|
18
|
+
function refuseUnsupportedBun(params) {
|
|
19
|
+
if (Bun.semver.satisfies(params.version, `>=${MINIMUM_BUN_VERSION}`))
|
|
20
|
+
return;
|
|
21
|
+
throw new VibeError({
|
|
22
|
+
code: "VIBE_CONFIG",
|
|
23
|
+
message: `@vibeorm/adapter-bun requires Bun ${MINIMUM_BUN_VERSION} or newer (running ${params.version}): older bun:sql ` + "releases hang after COPY, read timestamps without a time zone in host-local time and turn infinity dates into Invalid Date. " + "Upgrade Bun, or use @vibeorm/adapter-pg.",
|
|
24
|
+
meta: { bunVersion: params.version, minimumBunVersion: MINIMUM_BUN_VERSION }
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
// packages/adapter-bun/src/index.ts
|
|
15
29
|
import { postgresDialect, renderDiagnostic } from "@vibeorm/sql";
|
|
16
30
|
import {
|
|
17
31
|
abortTransactionOnServerFailure,
|
|
32
|
+
createDeferredControl,
|
|
18
33
|
observeRowChanges,
|
|
19
34
|
readRowChanges,
|
|
20
35
|
statementRowChanges,
|
|
21
|
-
|
|
36
|
+
createAdapterLifecycleGate,
|
|
22
37
|
runPinnedSession,
|
|
38
|
+
runShutdownCleanup,
|
|
39
|
+
transactionLifecycleRefusal,
|
|
23
40
|
createSavepointScope,
|
|
24
|
-
savepointRollbackFailedError,
|
|
25
41
|
engineStatementBudgetMs,
|
|
26
|
-
|
|
42
|
+
markTransactionAborted,
|
|
27
43
|
runNestedSavepoint,
|
|
28
44
|
runRootScope,
|
|
29
45
|
scopeTurn,
|
|
46
|
+
settleFailedTransaction,
|
|
30
47
|
startTransactionBudget,
|
|
31
|
-
|
|
32
|
-
validateTransactionDeadline
|
|
48
|
+
transactionAbortedError,
|
|
49
|
+
validateTransactionDeadline,
|
|
50
|
+
readOnlyRequested
|
|
33
51
|
} from "@vibeorm/runtime/adapter-kit";
|
|
34
|
-
import { VibeError as
|
|
52
|
+
import { VibeError as VibeError7 } from "@vibeorm/schema";
|
|
35
53
|
|
|
36
54
|
// packages/adapter-bun/src/connection-url.ts
|
|
37
|
-
import { VibeError } from "@vibeorm/schema";
|
|
55
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
38
56
|
function buildStartupOptions(params) {
|
|
39
57
|
const { planCacheMode, statementTimeout } = params;
|
|
40
58
|
const parts = [];
|
|
@@ -42,7 +60,7 @@ function buildStartupOptions(params) {
|
|
|
42
60
|
parts.push(`-c plan_cache_mode=${planCacheMode}`);
|
|
43
61
|
if (statementTimeout !== undefined) {
|
|
44
62
|
if (!Number.isInteger(statementTimeout) || statementTimeout < 0) {
|
|
45
|
-
throw new
|
|
63
|
+
throw new VibeError2({
|
|
46
64
|
code: "VIBE_VALIDATION",
|
|
47
65
|
message: `statementTimeout must be a non-negative integer of milliseconds, received ${String(statementTimeout)}`,
|
|
48
66
|
meta: { statementTimeout }
|
|
@@ -52,9 +70,28 @@ function buildStartupOptions(params) {
|
|
|
52
70
|
}
|
|
53
71
|
return parts.length === 0 ? undefined : parts.join(" ");
|
|
54
72
|
}
|
|
73
|
+
function takeConnectTimeout(params) {
|
|
74
|
+
if (!URL.canParse(params.url))
|
|
75
|
+
return { url: params.url };
|
|
76
|
+
const url = new URL(params.url);
|
|
77
|
+
const raw = url.searchParams.get("connect_timeout");
|
|
78
|
+
if (raw === null)
|
|
79
|
+
return { url: params.url };
|
|
80
|
+
const seconds = Number(raw.trim());
|
|
81
|
+
if (raw.trim() === "" || !Number.isInteger(seconds)) {
|
|
82
|
+
throw new VibeError2({
|
|
83
|
+
code: "VIBE_VALIDATION",
|
|
84
|
+
message: `connect_timeout in the connection URL must be an integer number of seconds, received "${raw}"`,
|
|
85
|
+
meta: { connectTimeout: raw }
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
url.searchParams.delete("connect_timeout");
|
|
89
|
+
url.search = url.searchParams.toString().replace(/\+/g, "%20");
|
|
90
|
+
return seconds > 0 ? { url: url.toString(), seconds } : { url: url.toString() };
|
|
91
|
+
}
|
|
55
92
|
function applyStartupOptions(params) {
|
|
56
93
|
if (!URL.canParse(params.url)) {
|
|
57
|
-
throw new
|
|
94
|
+
throw new VibeError2({ code: "VIBE_VALIDATION", message: "invalid PostgreSQL connection URL" });
|
|
58
95
|
}
|
|
59
96
|
const url = new URL(params.url);
|
|
60
97
|
const merged = [...url.searchParams.getAll("options"), params.startupOptions].filter(Boolean).join(" ");
|
|
@@ -64,7 +101,7 @@ function applyStartupOptions(params) {
|
|
|
64
101
|
}
|
|
65
102
|
|
|
66
103
|
// packages/adapter-bun/src/errors.ts
|
|
67
|
-
import { VibeError as
|
|
104
|
+
import { VibeError as VibeError3 } from "@vibeorm/schema";
|
|
68
105
|
var BUN_SQLSTATE_ERROR_CODES = Object.freeze({
|
|
69
106
|
"23P01": "VIBE_EXCLUSION_VIOLATION",
|
|
70
107
|
"23505": "VIBE_UNIQUE_VIOLATION",
|
|
@@ -81,16 +118,18 @@ var BUN_SQLSTATE_ERROR_CODES = Object.freeze({
|
|
|
81
118
|
"40001": "VIBE_TRANSACTION",
|
|
82
119
|
"40P01": "VIBE_TRANSACTION",
|
|
83
120
|
"57014": "VIBE_TRANSACTION",
|
|
84
|
-
"55P03": "VIBE_LOCK_NOT_AVAILABLE"
|
|
121
|
+
"55P03": "VIBE_LOCK_NOT_AVAILABLE",
|
|
122
|
+
"25006": "VIBE_TRANSACTION"
|
|
85
123
|
});
|
|
86
124
|
var BUN_SQLSTATE_REASONS = Object.freeze({
|
|
87
125
|
"23P01": "exclusion-violation",
|
|
88
126
|
"57014": "timeout",
|
|
89
127
|
"55P03": "lock-not-available",
|
|
90
128
|
"40P01": "deadlock",
|
|
91
|
-
"40001": "serialization-failure"
|
|
129
|
+
"40001": "serialization-failure",
|
|
130
|
+
"25006": "read-only-transaction"
|
|
92
131
|
});
|
|
93
|
-
var SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
|
|
132
|
+
var SQLSTATE_PATTERN = /^(?=[A-Z]*\d)[0-9A-Z]{5}$/;
|
|
94
133
|
function readStringField(params) {
|
|
95
134
|
const value = params.source[params.key];
|
|
96
135
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
@@ -109,7 +148,7 @@ function bunErrorSqlstate(params) {
|
|
|
109
148
|
}
|
|
110
149
|
function mapBunDriverError(params) {
|
|
111
150
|
const { error } = params;
|
|
112
|
-
if (error instanceof
|
|
151
|
+
if (error instanceof VibeError3)
|
|
113
152
|
return error;
|
|
114
153
|
const source = error !== null && typeof error === "object" ? error : {};
|
|
115
154
|
const sqlstate = bunErrorSqlstate({ error });
|
|
@@ -133,7 +172,7 @@ function mapBunDriverError(params) {
|
|
|
133
172
|
if (driverCode !== undefined && driverCode !== sqlstate)
|
|
134
173
|
meta.driverCode = driverCode;
|
|
135
174
|
const message = readStringField({ source, key: "message" }) ?? (typeof error === "string" ? error : "unknown bun:sql driver error");
|
|
136
|
-
return new
|
|
175
|
+
return new VibeError3({ code, message, meta, cause: error });
|
|
137
176
|
}
|
|
138
177
|
async function runMapped(fn) {
|
|
139
178
|
try {
|
|
@@ -144,7 +183,8 @@ async function runMapped(fn) {
|
|
|
144
183
|
}
|
|
145
184
|
|
|
146
185
|
// packages/adapter-bun/src/params.ts
|
|
147
|
-
import {
|
|
186
|
+
import { Buffer } from "buffer";
|
|
187
|
+
import { VibeError as VibeError4 } from "@vibeorm/schema";
|
|
148
188
|
function quoteElement(value) {
|
|
149
189
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
|
|
150
190
|
}
|
|
@@ -157,8 +197,11 @@ function toPgArrayLiteral(values) {
|
|
|
157
197
|
}
|
|
158
198
|
if (value instanceof Date)
|
|
159
199
|
return quoteElement(value.toISOString());
|
|
200
|
+
if (value instanceof Uint8Array) {
|
|
201
|
+
return quoteElement(`\\x${Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString("hex")}`);
|
|
202
|
+
}
|
|
160
203
|
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
|
|
161
|
-
throw new
|
|
204
|
+
throw new VibeError4({
|
|
162
205
|
code: "VIBE_VALIDATION",
|
|
163
206
|
message: "binary values are not supported inside a postgres array literal",
|
|
164
207
|
meta: { driver: "bun:sql" }
|
|
@@ -181,21 +224,31 @@ function serializeDate(params) {
|
|
|
181
224
|
return value;
|
|
182
225
|
return value.toISOString();
|
|
183
226
|
}
|
|
227
|
+
function serializeScalar(value) {
|
|
228
|
+
if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean")
|
|
229
|
+
return String(value);
|
|
230
|
+
if (value instanceof Date)
|
|
231
|
+
return serializeDate({ value });
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
function isJsonDocument(value) {
|
|
235
|
+
return typeof value === "object" && value !== null && !(value instanceof Date) && !ArrayBuffer.isView(value) && !(value instanceof ArrayBuffer);
|
|
236
|
+
}
|
|
184
237
|
function serializeParams(params) {
|
|
185
|
-
|
|
238
|
+
const stringifyDocuments = params.namedStatements !== true;
|
|
239
|
+
return params.values.map((value) => stringifyDocuments && isJsonDocument(value) ? JSON.stringify(value) : serializeScalar(value));
|
|
186
240
|
}
|
|
187
241
|
function serializeParamsRaw(params) {
|
|
242
|
+
const stringifyDocuments = params.namedStatements !== true;
|
|
188
243
|
return params.values.map((value) => {
|
|
189
244
|
if (Array.isArray(value))
|
|
190
245
|
return toPgArrayLiteral(value);
|
|
191
|
-
|
|
192
|
-
return serializeDate({ value });
|
|
193
|
-
return value;
|
|
246
|
+
return stringifyDocuments && isJsonDocument(value) ? JSON.stringify(value) : serializeScalar(value);
|
|
194
247
|
});
|
|
195
248
|
}
|
|
196
249
|
|
|
197
250
|
// packages/adapter-bun/src/template-cache.ts
|
|
198
|
-
import { VibeError as
|
|
251
|
+
import { VibeError as VibeError5 } from "@vibeorm/schema";
|
|
199
252
|
function isIdentChar(char) {
|
|
200
253
|
if (char === undefined)
|
|
201
254
|
return false;
|
|
@@ -292,7 +345,7 @@ function scanPlaceholders(params) {
|
|
|
292
345
|
}
|
|
293
346
|
chunks.push(text.slice(chunkStart));
|
|
294
347
|
const template = Object.assign(chunks, { raw: chunks });
|
|
295
|
-
return { template, indices };
|
|
348
|
+
return { template, indices, identityOrder: indices.every((placeholder, index) => placeholder === index + 1) };
|
|
296
349
|
}
|
|
297
350
|
function templateChunks(params) {
|
|
298
351
|
return [...scanPlaceholders(params).template];
|
|
@@ -301,7 +354,7 @@ function bindTemplateValues(params) {
|
|
|
301
354
|
const { text, indices, values } = params;
|
|
302
355
|
return indices.map((placeholder) => {
|
|
303
356
|
if (placeholder < 1 || placeholder > values.length) {
|
|
304
|
-
throw new
|
|
357
|
+
throw new VibeError5({
|
|
305
358
|
code: "VIBE_VALIDATION",
|
|
306
359
|
message: `bun:sql prepared statement references $${placeholder} but only ${values.length} parameter(s) were given`,
|
|
307
360
|
meta: { text, placeholder, provided: values.length }
|
|
@@ -338,7 +391,7 @@ function createTemplateCache(params) {
|
|
|
338
391
|
}
|
|
339
392
|
|
|
340
393
|
// packages/adapter-bun/src/transaction-sql.ts
|
|
341
|
-
import { VibeError as
|
|
394
|
+
import { VibeError as VibeError6 } from "@vibeorm/schema";
|
|
342
395
|
import { refuseNestedTransactionOptions } from "@vibeorm/runtime/adapter-kit";
|
|
343
396
|
var ISOLATION_LEVEL_SQL = Object.freeze({
|
|
344
397
|
ReadCommitted: "READ COMMITTED",
|
|
@@ -346,13 +399,23 @@ var ISOLATION_LEVEL_SQL = Object.freeze({
|
|
|
346
399
|
Serializable: "SERIALIZABLE"
|
|
347
400
|
});
|
|
348
401
|
function beginStatement(params) {
|
|
349
|
-
const { isolationLevel } = params;
|
|
350
|
-
|
|
402
|
+
const { isolationLevel, readOnly } = params;
|
|
403
|
+
const modes = [];
|
|
404
|
+
if (isolationLevel !== undefined)
|
|
405
|
+
modes.push(`ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[isolationLevel]}`);
|
|
406
|
+
if (readOnly === true)
|
|
407
|
+
modes.push("READ ONLY");
|
|
408
|
+
return modes.length === 0 ? "BEGIN" : `BEGIN ${modes.join(", ")}`;
|
|
409
|
+
}
|
|
410
|
+
function openTransactionStatement(params) {
|
|
411
|
+
const begin = beginStatement(params);
|
|
412
|
+
return params.timeout === undefined ? begin : `${begin}; ${statementTimeoutStatement({ timeout: params.timeout })}`;
|
|
351
413
|
}
|
|
414
|
+
var STATEMENT_BOUND_TEXT = "SELECT set_config('statement_timeout', $1, true)";
|
|
352
415
|
function statementTimeoutStatement(params) {
|
|
353
416
|
const { timeout } = params;
|
|
354
417
|
if (!Number.isInteger(timeout) || timeout < 0) {
|
|
355
|
-
throw new
|
|
418
|
+
throw new VibeError6({
|
|
356
419
|
code: "VIBE_VALIDATION",
|
|
357
420
|
message: `transaction timeout must be a non-negative integer of milliseconds, received ${String(timeout)}`,
|
|
358
421
|
meta: { timeout }
|
|
@@ -375,78 +438,176 @@ import { createSavepointCounter, nextSavepointName } from "@vibeorm/runtime/adap
|
|
|
375
438
|
import {
|
|
376
439
|
DEFAULT_DEADLINE_CLOCK,
|
|
377
440
|
engineStatementBudgetMs as engineStatementBudgetMs2,
|
|
378
|
-
isTransactionDeadlineError
|
|
441
|
+
isTransactionDeadlineError,
|
|
379
442
|
startTransactionBudget as startTransactionBudget2,
|
|
380
443
|
transactionClosedError,
|
|
381
444
|
transactionDeadlineError,
|
|
382
|
-
transactionOutcomeUnknownError
|
|
445
|
+
transactionOutcomeUnknownError,
|
|
383
446
|
validateTransactionDeadline as validateTransactionDeadline2
|
|
384
447
|
} from "@vibeorm/runtime/adapter-kit";
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
constructor(inner) {
|
|
389
|
-
super("vibeorm: transaction callback failed");
|
|
390
|
-
this.name = "CallbackError";
|
|
391
|
-
this.inner = inner;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
448
|
+
var ignore = () => {
|
|
449
|
+
return;
|
|
450
|
+
};
|
|
394
451
|
var DIALECT = "postgres";
|
|
395
452
|
var PROVIDER = "postgresql";
|
|
396
453
|
var BUDGETS = Object.freeze({
|
|
397
454
|
acquireTimeout: false,
|
|
398
455
|
statementTimeout: "all-statements",
|
|
399
|
-
transactionDeadline: "cancel-running-statements"
|
|
456
|
+
transactionDeadline: "cancel-running-statements",
|
|
457
|
+
accessModes: Object.freeze(["readOnly"])
|
|
400
458
|
});
|
|
401
459
|
var WIRE_TEXT = Object.freeze({ dateTime: "native", json: "text", numbers: "native" });
|
|
402
460
|
var WIRE_PARSED = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
|
|
461
|
+
var ROUND_TRIPS = "network";
|
|
462
|
+
function requireUrl(params) {
|
|
463
|
+
const url = params.options?.url;
|
|
464
|
+
if (typeof url === "string" && url.trim() !== "")
|
|
465
|
+
return url;
|
|
466
|
+
throw new VibeError7({
|
|
467
|
+
code: "VIBE_CONFIG",
|
|
468
|
+
message: "bunAdapter needs an explicit address: pass `url` (a PostgreSQL URL). " + "The adapter does not read DATABASE_URL, POSTGRES_URL or the PG* environment variables.",
|
|
469
|
+
meta: { adapter: "bun", reason: "missing-address" }
|
|
470
|
+
});
|
|
471
|
+
}
|
|
403
472
|
function bunAdapter(options) {
|
|
473
|
+
refuseUnsupportedBun({ version: Bun.version });
|
|
404
474
|
const usePrepared = options?.preparedStatements ?? false;
|
|
475
|
+
const address = takeConnectTimeout({ url: requireUrl({ options }) });
|
|
405
476
|
const templates = createTemplateCache({ max: options?.stmtCacheMax ?? 1000 });
|
|
406
477
|
let activeWire = WIRE_TEXT;
|
|
407
478
|
let wireSettled = false;
|
|
408
479
|
let wireProbe = null;
|
|
409
480
|
let instance = null;
|
|
410
|
-
const
|
|
411
|
-
function
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
481
|
+
const controlTemplates = createTemplateCache({ max: 64 });
|
|
482
|
+
function controlQuery(params) {
|
|
483
|
+
return params.connection(controlTemplates.getTemplate({ text: params.text }).template, ...params.values ?? []);
|
|
484
|
+
}
|
|
485
|
+
function createReservedControl(params) {
|
|
486
|
+
const { reserved, budget, scope } = params;
|
|
487
|
+
const deferred = createDeferredControl({ opening: params.opening === null ? [] : [params.opening] });
|
|
488
|
+
let openingFailure;
|
|
489
|
+
let openingStep = null;
|
|
490
|
+
let openingPending = null;
|
|
491
|
+
let waitTail = null;
|
|
492
|
+
let deadlineTail = Promise.resolve();
|
|
493
|
+
let lastFailure;
|
|
494
|
+
const step = (query) => new Promise((resolve) => resolve(query())).then(undefined, (error) => {
|
|
495
|
+
const mapped = mapBunDriverError({ error });
|
|
496
|
+
if (mapped.meta.sqlstate === "25P02") {
|
|
497
|
+
throw transactionAbortedError({ cause: lastFailure ?? scope.state.aborted?.cause });
|
|
423
498
|
}
|
|
424
|
-
|
|
499
|
+
if (typeof mapped.meta.sqlstate === "string")
|
|
500
|
+
lastFailure = mapped;
|
|
501
|
+
throw mapped;
|
|
502
|
+
});
|
|
503
|
+
const trackOpening = (sent) => {
|
|
504
|
+
openingStep = sent;
|
|
505
|
+
const settled = sent.then(ignore, (error) => {
|
|
506
|
+
openingFailure = mapBunDriverError({ error });
|
|
507
|
+
}).then(() => {
|
|
508
|
+
if (openingPending === settled) {
|
|
509
|
+
openingPending = null;
|
|
510
|
+
openingStep = null;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
openingPending = settled;
|
|
514
|
+
return settled;
|
|
515
|
+
};
|
|
516
|
+
const ridesOpening = (send) => send.extended && send.effect === "read";
|
|
517
|
+
const sendNow = (send) => {
|
|
518
|
+
const cleanup = send.cleanup === true;
|
|
519
|
+
if (!cleanup) {
|
|
520
|
+
if (openingFailure !== undefined)
|
|
521
|
+
throw transactionAbortedError({ cause: openingFailure });
|
|
522
|
+
budget.assertUsable({ stage: send.stage ?? "statement" });
|
|
523
|
+
send.guard?.();
|
|
524
|
+
}
|
|
525
|
+
const carriesOpening = !cleanup && !deferred.opened();
|
|
526
|
+
const control = cleanup ? [] : [...deferred.take()];
|
|
527
|
+
if (carriesOpening && !ridesOpening(send)) {
|
|
528
|
+
const [opening, ...rest] = control;
|
|
529
|
+
const confirmed = trackOpening(step(() => controlQuery({ connection: reserved, text: opening })));
|
|
530
|
+
const riding = rest.map((text) => {
|
|
531
|
+
const sent = step(() => controlQuery({ connection: reserved, text }));
|
|
532
|
+
sent.catch(ignore);
|
|
533
|
+
return sent;
|
|
534
|
+
});
|
|
535
|
+
const next = { ...send, riding };
|
|
536
|
+
if (budget.totalMs !== null)
|
|
537
|
+
return confirmed.then(() => sendNow(next));
|
|
538
|
+
return enqueue(next);
|
|
539
|
+
}
|
|
540
|
+
const ms = cleanup ? null : engineStatementBudgetMs({ budget });
|
|
541
|
+
const steps = [...send.riding ?? []];
|
|
542
|
+
if (!cleanup && !carriesOpening && openingStep !== null)
|
|
543
|
+
steps.push(openingStep);
|
|
544
|
+
control.forEach((text, index) => {
|
|
545
|
+
const sent = step(() => controlQuery({ connection: reserved, text }));
|
|
546
|
+
if (index === 0 && carriesOpening)
|
|
547
|
+
trackOpening(sent);
|
|
548
|
+
steps.push(sent);
|
|
549
|
+
});
|
|
550
|
+
if (ms !== null)
|
|
551
|
+
steps.push(step(() => controlQuery({ connection: reserved, text: STATEMENT_BOUND_TEXT, values: [String(ms)] })));
|
|
552
|
+
const statement = step(() => send.send(reserved));
|
|
553
|
+
steps.push(statement);
|
|
554
|
+
return Promise.allSettled(steps).then((settled) => {
|
|
555
|
+
const failed = settled.find((outcome) => outcome.status === "rejected");
|
|
556
|
+
if (failed !== undefined)
|
|
557
|
+
throw failed.reason;
|
|
558
|
+
return statement;
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
const enqueue = (send) => {
|
|
562
|
+
const prior = waitTail ?? openingPending ?? Promise.resolve();
|
|
563
|
+
let markStarted = ignore;
|
|
564
|
+
const started = new Promise((resolve) => {
|
|
565
|
+
markStarted = resolve;
|
|
566
|
+
});
|
|
567
|
+
waitTail = started;
|
|
568
|
+
return prior.then(() => {
|
|
569
|
+
try {
|
|
570
|
+
return sendNow(send);
|
|
571
|
+
} finally {
|
|
572
|
+
if (waitTail === started)
|
|
573
|
+
waitTail = null;
|
|
574
|
+
markStarted();
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
const dispatch = (send) => {
|
|
579
|
+
if (budget.totalMs !== null) {
|
|
580
|
+
const pending = deadlineTail.then(() => sendNow(send));
|
|
581
|
+
deadlineTail = pending.then(ignore, ignore);
|
|
582
|
+
return pending;
|
|
583
|
+
}
|
|
584
|
+
if (openingPending !== null && (waitTail !== null || !ridesOpening(send)))
|
|
585
|
+
return enqueue(send);
|
|
586
|
+
return new Promise((resolve) => resolve(sendNow(send)));
|
|
587
|
+
};
|
|
588
|
+
return {
|
|
589
|
+
deferred,
|
|
590
|
+
dispatch,
|
|
591
|
+
executor: ({ effect, guard }) => Object.assign((strings, ...values) => dispatch({ effect, guard, extended: true, send: (connection) => connection(strings, ...values) }), { unsafe: (text, values) => dispatch({ effect, guard, extended: values !== undefined && values.length > 0, send: (connection) => connection.unsafe(text, values) }) })
|
|
425
592
|
};
|
|
426
|
-
if (budget === undefined || budget.totalMs === null)
|
|
427
|
-
return run();
|
|
428
|
-
const result = (deadlineTurns.get(budget) ?? Promise.resolve()).then(run);
|
|
429
|
-
deadlineTurns.set(budget, result.then(() => {
|
|
430
|
-
return;
|
|
431
|
-
}, () => {
|
|
432
|
-
return;
|
|
433
|
-
}));
|
|
434
|
-
return result;
|
|
435
593
|
}
|
|
436
594
|
let rawSession = null;
|
|
437
595
|
function createSqlInstance(params) {
|
|
438
|
-
const sqlOptions = { max: params.max };
|
|
596
|
+
const sqlOptions = { max: params.max, prepare: usePrepared };
|
|
439
597
|
if (options?.connectionTimeout !== undefined) {
|
|
440
598
|
sqlOptions.connectionTimeout = Math.max(1, Math.ceil(options.connectionTimeout / 1000));
|
|
599
|
+
} else if (address.seconds !== undefined) {
|
|
600
|
+
sqlOptions.connectionTimeout = address.seconds;
|
|
441
601
|
}
|
|
442
602
|
const startupOptions = buildStartupOptions({
|
|
443
603
|
planCacheMode: options?.planCacheMode ?? "force_custom_plan",
|
|
444
604
|
statementTimeout: options?.statementTimeout
|
|
445
605
|
});
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
606
|
+
const url = startupOptions === undefined ? address.url : applyStartupOptions({ url: address.url, startupOptions });
|
|
607
|
+
if (URL.canParse(url)) {
|
|
608
|
+
const path = new URL(url).pathname;
|
|
609
|
+
if (path.length > 1)
|
|
610
|
+
sqlOptions.database = decodeURIComponent(path.slice(1));
|
|
450
611
|
}
|
|
451
612
|
return createBunSql({ url, options: sqlOptions });
|
|
452
613
|
}
|
|
@@ -463,7 +624,7 @@ function bunAdapter(options) {
|
|
|
463
624
|
else if (typeof probed === "string" && /^\{\s*"__vibeorm"\s*:\s*1\s*\}$/.test(probed))
|
|
464
625
|
activeWire = WIRE_TEXT;
|
|
465
626
|
else {
|
|
466
|
-
throw new
|
|
627
|
+
throw new VibeError7({
|
|
467
628
|
code: "VIBE_ADAPTER",
|
|
468
629
|
message: "Bun returned an unrecognized JSON wire probe result; no ORM statement was sent",
|
|
469
630
|
meta: { reason: "json-wire-probe" }
|
|
@@ -479,40 +640,44 @@ function bunAdapter(options) {
|
|
|
479
640
|
if (results.length === 0)
|
|
480
641
|
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect }) });
|
|
481
642
|
for (const rows of results)
|
|
482
|
-
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: rows.count ?? rows.affectedRows }) });
|
|
643
|
+
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: rows.count ?? rows.affectedRows, rows: rows.length, resultRows: rows }) });
|
|
483
644
|
}
|
|
484
645
|
async function runExecute(params) {
|
|
485
646
|
if (!wireSettled) {
|
|
486
|
-
|
|
487
|
-
wireProbe
|
|
488
|
-
|
|
489
|
-
|
|
647
|
+
if (params.scope === undefined) {
|
|
648
|
+
wireProbe ??= probeJsonWire(params.source).finally(() => {
|
|
649
|
+
wireProbe = null;
|
|
650
|
+
});
|
|
651
|
+
await wireProbe;
|
|
652
|
+
} else {
|
|
653
|
+
await probeJsonWire(params.source);
|
|
654
|
+
}
|
|
490
655
|
}
|
|
491
|
-
const values = serializeParams({ values: params.values });
|
|
656
|
+
const values = serializeParams({ values: params.values, namedStatements: usePrepared });
|
|
492
657
|
if (usePrepared) {
|
|
493
658
|
const prepared = templates.getTemplate({ text: params.text });
|
|
494
|
-
const bound = bindTemplateValues({ text: params.text, indices: prepared.indices, values });
|
|
495
|
-
const result = await runMapped(() => params.source(prepared.template, ...bound));
|
|
659
|
+
const bound = prepared.identityOrder && prepared.indices.length === values.length ? values : bindTemplateValues({ text: params.text, indices: prepared.indices, values });
|
|
660
|
+
const result = bound.length > MAX_BIND_PARAMETERS ? await runMapped(() => params.source.unsafe(params.text, values)) : await runMapped(() => params.source(prepared.template, ...bound));
|
|
496
661
|
observeResult({ scope: params.scope, effect: params.effect, result });
|
|
497
|
-
return finalRows({ result });
|
|
662
|
+
return plainRows({ rows: finalRows({ result }) });
|
|
498
663
|
}
|
|
499
664
|
const result = await runMapped(() => params.source.unsafe(params.text, values));
|
|
500
665
|
observeResult({ scope: params.scope, effect: params.effect, result });
|
|
501
|
-
return finalRows({ result });
|
|
666
|
+
return plainRows({ rows: finalRows({ result }) });
|
|
502
667
|
}
|
|
503
668
|
async function runExecuteUnsafe(params) {
|
|
504
|
-
const values = params.values === undefined ? undefined : serializeParamsRaw({ values: params.values });
|
|
669
|
+
const values = params.values === undefined ? undefined : serializeParamsRaw({ values: params.values, namedStatements: usePrepared });
|
|
505
670
|
const result = await runMapped(() => params.source.unsafe(params.text, values));
|
|
506
671
|
observeResult({ scope: params.scope, effect: params.effect, result });
|
|
507
672
|
const rows = finalRows({ result });
|
|
508
|
-
return { rows, affectedRows: readAffectedRows({ rows }) };
|
|
673
|
+
return { rows: plainRows({ rows }), affectedRows: readAffectedRows({ rows }) };
|
|
509
674
|
}
|
|
510
675
|
function createAdapter(params) {
|
|
511
|
-
const { source, parentMode, scope, budget, lifetime } = params;
|
|
676
|
+
const { source, parentMode, scope, budget, lifetime, control } = params;
|
|
512
677
|
function assertHandle() {
|
|
513
678
|
budget?.assertUsable({ stage: "statement" });
|
|
514
679
|
if (lifetime?.active === false) {
|
|
515
|
-
throw new
|
|
680
|
+
throw new VibeError7({
|
|
516
681
|
code: "VIBE_TRANSACTION",
|
|
517
682
|
message: "this savepoint callback has finished; its transaction handle has expired",
|
|
518
683
|
meta: { reason: "expired" }
|
|
@@ -521,56 +686,32 @@ function bunAdapter(options) {
|
|
|
521
686
|
if (scope.state.poisoned !== null)
|
|
522
687
|
throw scope.state.poisoned;
|
|
523
688
|
}
|
|
524
|
-
|
|
525
|
-
async function transactionViaSavepoint(fn, nestedOptions) {
|
|
689
|
+
async function transactionViaSavepoint(reservedControl, fn, nestedOptions) {
|
|
526
690
|
refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
|
|
527
691
|
assertHandle();
|
|
528
692
|
budget?.assertUsable({ stage: "nested" });
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
childLifetime.active = false;
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
const driverSavepoint = transactional.savepoint?.bind(transactional);
|
|
540
|
-
if (driverSavepoint !== undefined) {
|
|
541
|
-
return runNestedSavepoint({
|
|
542
|
-
scope,
|
|
543
|
-
body: async (child) => {
|
|
544
|
-
let callbackFailure;
|
|
545
|
-
try {
|
|
546
|
-
return await driverSavepoint(async (savepointSql) => {
|
|
547
|
-
markSavepointOpened({ scope: child });
|
|
548
|
-
try {
|
|
549
|
-
return await runChild(child, savepointSql);
|
|
550
|
-
} catch (error) {
|
|
551
|
-
callbackFailure = new CallbackError(error);
|
|
552
|
-
throw callbackFailure;
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
} catch (error) {
|
|
556
|
-
if (callbackFailure !== undefined && error === callbackFailure)
|
|
557
|
-
throw callbackFailure.inner;
|
|
558
|
-
const poisoned = callbackFailure !== undefined ? savepointRollbackFailedError({ cause: callbackFailure.inner, rollbackFailure: mapBunDriverError({ error }) }) : unknownOutcome({ cause: mapBunDriverError({ error }), reason: "savepoint-outcome-unknown" });
|
|
559
|
-
scope.state.poisoned = poisoned;
|
|
560
|
-
throw poisoned;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
693
|
+
const savepointControl = (statement, cleanup) => {
|
|
694
|
+
if (reservedControl.deferred.routeSavepoint({ text: statement }) !== "send")
|
|
695
|
+
return;
|
|
696
|
+
return reservedControl.dispatch({
|
|
697
|
+
extended: true,
|
|
698
|
+
...cleanup ? { cleanup: true } : {},
|
|
699
|
+
send: (connection) => controlQuery({ connection, text: statement })
|
|
563
700
|
});
|
|
564
|
-
}
|
|
701
|
+
};
|
|
565
702
|
return runNestedSavepoint({
|
|
566
703
|
scope,
|
|
567
|
-
execute: (statement) =>
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
704
|
+
execute: (statement) => savepointControl(statement, false),
|
|
705
|
+
cleanup: (statement) => savepointControl(statement, true),
|
|
706
|
+
body: async (child) => {
|
|
707
|
+
assertHandle();
|
|
708
|
+
const childLifetime = { active: true };
|
|
709
|
+
try {
|
|
710
|
+
return await fn(createAdapter({ source, parentMode: "tx", scope: child, budget, lifetime: childLifetime, control: reservedControl }));
|
|
711
|
+
} finally {
|
|
712
|
+
childLifetime.active = false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
574
715
|
});
|
|
575
716
|
}
|
|
576
717
|
async function transactionViaPool(fn, transactionOptions) {
|
|
@@ -580,76 +721,76 @@ function bunAdapter(options) {
|
|
|
580
721
|
support: BUDGETS.transactionDeadline,
|
|
581
722
|
provider: PROVIDER
|
|
582
723
|
});
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
let callbackFailure;
|
|
587
|
-
try {
|
|
588
|
-
const result = await pool.begin(async (txSql) => {
|
|
589
|
-
const txAdapter = createAdapter({
|
|
590
|
-
source: txSql,
|
|
591
|
-
parentMode: "tx",
|
|
592
|
-
scope: txScope,
|
|
593
|
-
budget
|
|
594
|
-
});
|
|
595
|
-
try {
|
|
596
|
-
return await runRootScope({ scope: txScope, body: () => fn(txAdapter) });
|
|
597
|
-
} catch (error) {
|
|
598
|
-
callbackFailure = new CallbackError(error);
|
|
599
|
-
throw callbackFailure;
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
budget.close({ outcome: "committed" });
|
|
603
|
-
return result;
|
|
604
|
-
} catch (error) {
|
|
605
|
-
const knownRollback = callbackFailure !== undefined && error === callbackFailure && txScope.state.poisoned === null;
|
|
606
|
-
budget.close({ outcome: knownRollback ? "rolled-back" : "unknown" });
|
|
607
|
-
if (callbackFailure !== undefined && error === callbackFailure)
|
|
608
|
-
throw callbackFailure.inner;
|
|
609
|
-
throw unknownOutcome({ cause: callbackFailure?.inner ?? mapBunDriverError({ error }) });
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
const timeoutText = transactionOptions.timeout === undefined ? undefined : statementTimeoutStatement({ timeout: transactionOptions.timeout });
|
|
724
|
+
const readOnly = readOnlyRequested({ options: transactionOptions, provider: PROVIDER });
|
|
725
|
+
const openingOptions = { isolationLevel: transactionOptions?.isolationLevel, readOnly };
|
|
726
|
+
const eagerOpen = transactionOptions?.timeout === undefined ? null : openTransactionStatement({ ...openingOptions, timeout: transactionOptions.timeout });
|
|
613
727
|
const reserved = await runMapped(() => pool.reserve());
|
|
614
728
|
const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });
|
|
615
729
|
const txScope = createSavepointScope();
|
|
616
|
-
|
|
730
|
+
const control = createReservedControl({
|
|
731
|
+
reserved,
|
|
732
|
+
budget,
|
|
733
|
+
scope: txScope,
|
|
734
|
+
opening: eagerOpen === null ? beginStatement(openingOptions) : null
|
|
735
|
+
});
|
|
736
|
+
const commit = { progress: "not-sent" };
|
|
617
737
|
let discard = false;
|
|
618
738
|
try {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
await runMapped(() => reserved.unsafe(timeoutText));
|
|
739
|
+
if (eagerOpen !== null)
|
|
740
|
+
await runMapped(() => reserved.unsafe(eagerOpen));
|
|
622
741
|
const txAdapter = createAdapter({
|
|
623
742
|
source: reserved,
|
|
624
743
|
parentMode: "tx",
|
|
625
744
|
scope: txScope,
|
|
626
|
-
budget
|
|
745
|
+
budget,
|
|
746
|
+
control
|
|
627
747
|
});
|
|
628
748
|
const result = await runRootScope({ scope: txScope, body: () => fn(txAdapter) });
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
749
|
+
budget.assertUsable({ stage: "commit" });
|
|
750
|
+
if (!control.deferred.opened()) {
|
|
751
|
+
budget.close({ outcome: "committed" });
|
|
752
|
+
return result;
|
|
753
|
+
}
|
|
754
|
+
const reply = await control.dispatch({
|
|
755
|
+
extended: true,
|
|
756
|
+
effect: "write",
|
|
757
|
+
stage: "commit",
|
|
758
|
+
send: (connection) => {
|
|
759
|
+
commit.progress = "sent";
|
|
760
|
+
return controlQuery({ connection, text: "COMMIT" });
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
if (finalRows({ result: reply }).command === "ROLLBACK") {
|
|
764
|
+
commit.progress = "answered";
|
|
765
|
+
throw transactionAbortedError({ cause: txScope.state.aborted?.cause });
|
|
766
|
+
}
|
|
633
767
|
budget.close({ outcome: "committed" });
|
|
634
768
|
return result;
|
|
635
769
|
} catch (error) {
|
|
770
|
+
if (txScope.state.poisoned !== null)
|
|
771
|
+
discard = true;
|
|
772
|
+
if (commit.progress === "sent")
|
|
773
|
+
commit.progress = isConnectionLoss({ error }) ? "unanswered" : "answered";
|
|
774
|
+
let rollback = "not-needed";
|
|
636
775
|
try {
|
|
637
|
-
|
|
638
|
-
|
|
776
|
+
if (control.deferred.opened()) {
|
|
777
|
+
await control.dispatch({ extended: false, cleanup: true, send: (connection) => connection.unsafe("ROLLBACK") });
|
|
778
|
+
rollback = "acknowledged";
|
|
779
|
+
}
|
|
639
780
|
} catch {
|
|
640
781
|
discard = true;
|
|
641
|
-
|
|
642
|
-
if (budget.totalMs !== null && isTransactionDeadlineError(error)) {
|
|
643
|
-
throw transactionOutcomeUnknownError({
|
|
644
|
-
provider: PROVIDER,
|
|
645
|
-
totalMs: budget.totalMs,
|
|
646
|
-
cause: error
|
|
647
|
-
});
|
|
648
|
-
}
|
|
782
|
+
rollback = "failed";
|
|
649
783
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
784
|
+
const own = commit.progress === "unanswered" && txScope.state.poisoned === null ? unknownOutcome({ cause: error }) : error;
|
|
785
|
+
throw settleFailedTransaction({
|
|
786
|
+
error: own,
|
|
787
|
+
provider: PROVIDER,
|
|
788
|
+
budget,
|
|
789
|
+
scope: txScope,
|
|
790
|
+
commit: commit.progress,
|
|
791
|
+
rollback,
|
|
792
|
+
connection: discard ? "discarded" : "returned"
|
|
793
|
+
});
|
|
653
794
|
} finally {
|
|
654
795
|
if (discard)
|
|
655
796
|
await runMapped(() => reserved.close());
|
|
@@ -661,6 +802,7 @@ function bunAdapter(options) {
|
|
|
661
802
|
dialect: DIALECT,
|
|
662
803
|
provider: PROVIDER,
|
|
663
804
|
budgets: BUDGETS,
|
|
805
|
+
roundTrips: ROUND_TRIPS,
|
|
664
806
|
...parentMode === "pool" ? {} : { rowChanges: () => {
|
|
665
807
|
assertHandle();
|
|
666
808
|
return readRowChanges({ scope });
|
|
@@ -674,8 +816,9 @@ function bunAdapter(options) {
|
|
|
674
816
|
if (turn !== undefined)
|
|
675
817
|
await turn;
|
|
676
818
|
assertHandle();
|
|
677
|
-
const
|
|
678
|
-
|
|
819
|
+
const executor = control === undefined ? source : control.executor({ effect: execParams.effect, guard: assertHandle });
|
|
820
|
+
const result = runExecute({ source: executor, ...parentMode === "pool" ? {} : { scope }, ...execParams });
|
|
821
|
+
return parentMode === "pool" ? result : result.catch((error) => abortTransactionOnStatementFailure({ scope, error }));
|
|
679
822
|
},
|
|
680
823
|
async executeUnsafe(execParams) {
|
|
681
824
|
assertHandle();
|
|
@@ -683,21 +826,35 @@ function bunAdapter(options) {
|
|
|
683
826
|
if (turn !== undefined)
|
|
684
827
|
await turn;
|
|
685
828
|
assertHandle();
|
|
686
|
-
const
|
|
687
|
-
|
|
829
|
+
const executor = control === undefined ? source : control.executor({ effect: execParams.effect, guard: assertHandle });
|
|
830
|
+
const result = runExecuteUnsafe({ source: executor, ...parentMode === "pool" ? {} : { scope }, ...execParams });
|
|
831
|
+
return parentMode === "pool" ? result : result.catch((error) => abortTransactionOnStatementFailure({ scope, error }));
|
|
688
832
|
},
|
|
689
833
|
async transaction(fn, transactionOptions) {
|
|
690
|
-
return
|
|
834
|
+
return control === undefined ? transactionViaPool(fn, transactionOptions) : transactionViaSavepoint(control, fn, transactionOptions);
|
|
691
835
|
},
|
|
692
836
|
async connect() {
|
|
693
|
-
if (parentMode !== "pool")
|
|
694
|
-
|
|
837
|
+
if (parentMode !== "pool") {
|
|
838
|
+
assertHandle();
|
|
839
|
+
throw transactionLifecycleRefusal({ method: "connect" });
|
|
840
|
+
}
|
|
695
841
|
const reserved = await runMapped(() => source.reserve());
|
|
696
|
-
|
|
842
|
+
try {
|
|
843
|
+
if (!wireSettled && wireProbe === null) {
|
|
844
|
+
wireProbe = probeJsonWire(reserved).finally(() => {
|
|
845
|
+
wireProbe = null;
|
|
846
|
+
});
|
|
847
|
+
await wireProbe;
|
|
848
|
+
}
|
|
849
|
+
} finally {
|
|
850
|
+
reserved.release();
|
|
851
|
+
}
|
|
697
852
|
},
|
|
698
853
|
async disconnect() {
|
|
699
|
-
if (parentMode !== "pool")
|
|
700
|
-
|
|
854
|
+
if (parentMode !== "pool") {
|
|
855
|
+
assertHandle();
|
|
856
|
+
throw transactionLifecycleRefusal({ method: "disconnect" });
|
|
857
|
+
}
|
|
701
858
|
await runMapped(() => source.close());
|
|
702
859
|
instance = null;
|
|
703
860
|
},
|
|
@@ -713,29 +870,53 @@ function bunAdapter(options) {
|
|
|
713
870
|
scope: createSavepointScope()
|
|
714
871
|
});
|
|
715
872
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
873
|
+
function closeOwnedResources() {
|
|
874
|
+
return runShutdownCleanup({
|
|
875
|
+
steps: [
|
|
876
|
+
async () => {
|
|
877
|
+
if (rawSession === null)
|
|
878
|
+
return;
|
|
879
|
+
const session = rawSession;
|
|
880
|
+
rawSession = null;
|
|
881
|
+
await runMapped(() => session.unsafe("ROLLBACK")).then(() => {
|
|
882
|
+
session.release();
|
|
883
|
+
}, (error) => {
|
|
884
|
+
session.release();
|
|
885
|
+
throw error;
|
|
886
|
+
});
|
|
887
|
+
},
|
|
888
|
+
async () => {
|
|
889
|
+
if (instance === null)
|
|
890
|
+
return;
|
|
891
|
+
const closing = instance;
|
|
892
|
+
instance = null;
|
|
893
|
+
await runMapped(() => closing.close());
|
|
894
|
+
}
|
|
895
|
+
]
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
const lifecycle = createAdapterLifecycleGate({ driver: "bun", cleanup: closeOwnedResources });
|
|
899
|
+
const root = {
|
|
723
900
|
async withSession({ run }) {
|
|
724
901
|
const sessionPool = createSqlInstance({ max: SESSION_CONNECTION.max });
|
|
725
902
|
const close = () => runMapped(() => sessionPool.close({ timeout: SESSION_CONNECTION.closeGraceSeconds }));
|
|
726
903
|
const reserved = await runMapped(() => sessionPool.reserve()).catch(async (cause) => {
|
|
727
|
-
await close()
|
|
904
|
+
await close().catch((cleanupCause) => {
|
|
905
|
+
if (cause instanceof VibeError7)
|
|
906
|
+
throw new VibeError7({ code: cause.code, message: cause.message, meta: { ...cause.meta, cleanupCause }, cause });
|
|
907
|
+
throw cause;
|
|
908
|
+
});
|
|
728
909
|
throw cause;
|
|
729
910
|
});
|
|
730
911
|
return runPinnedSession({
|
|
731
912
|
run,
|
|
732
913
|
execute: async (query) => (await runExecuteUnsafe({ source: reserved, ...query })).rows,
|
|
733
|
-
isLost: (error) => error instanceof
|
|
914
|
+
isLost: (error) => error instanceof VibeError7 && isConnectionLoss({ error }),
|
|
734
915
|
release: close
|
|
735
916
|
});
|
|
736
917
|
},
|
|
737
918
|
async execute(params) {
|
|
738
|
-
return
|
|
919
|
+
return runExecute({ source: getSql(), ...params });
|
|
739
920
|
},
|
|
740
921
|
async executeUnsafe(params) {
|
|
741
922
|
const control = classifyRawTransactionControl({ text: params.text });
|
|
@@ -762,28 +943,41 @@ function bunAdapter(options) {
|
|
|
762
943
|
throw error;
|
|
763
944
|
}
|
|
764
945
|
}
|
|
765
|
-
return
|
|
946
|
+
return runExecuteUnsafe({ source: getSql(), ...params });
|
|
766
947
|
},
|
|
767
948
|
async transaction(fn, transactionOptions) {
|
|
768
949
|
return poolAdapter().transaction(fn, transactionOptions);
|
|
769
950
|
},
|
|
770
951
|
async connect() {
|
|
771
952
|
return poolAdapter().connect();
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
return {
|
|
956
|
+
dialect: DIALECT,
|
|
957
|
+
provider: PROVIDER,
|
|
958
|
+
budgets: BUDGETS,
|
|
959
|
+
roundTrips: ROUND_TRIPS,
|
|
960
|
+
transactionOutcome: "reported",
|
|
961
|
+
get wire() {
|
|
962
|
+
return activeWire;
|
|
772
963
|
},
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
964
|
+
withSession({ run }) {
|
|
965
|
+
return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });
|
|
966
|
+
},
|
|
967
|
+
execute(params) {
|
|
968
|
+
return lifecycle.admit({ run: () => root.execute(params) });
|
|
969
|
+
},
|
|
970
|
+
executeUnsafe(params) {
|
|
971
|
+
return lifecycle.admit({ run: () => root.executeUnsafe(params) });
|
|
972
|
+
},
|
|
973
|
+
transaction(fn, transactionOptions) {
|
|
974
|
+
return lifecycle.admit({ run: () => root.transaction((txAdapter) => lifecycle.inCallback({ run: () => fn(txAdapter) }), transactionOptions) });
|
|
975
|
+
},
|
|
976
|
+
connect() {
|
|
977
|
+
return lifecycle.admit({ run: () => root.connect() });
|
|
978
|
+
},
|
|
979
|
+
disconnect() {
|
|
980
|
+
return lifecycle.disconnect();
|
|
787
981
|
},
|
|
788
982
|
formatArrayParam(values) {
|
|
789
983
|
return toPgArrayLiteral(values);
|
|
@@ -798,14 +992,31 @@ function readAffectedRows(params) {
|
|
|
798
992
|
return rows.affectedRows;
|
|
799
993
|
return rows.length;
|
|
800
994
|
}
|
|
995
|
+
function plainRows(params) {
|
|
996
|
+
return Array.from(params.rows);
|
|
997
|
+
}
|
|
801
998
|
function finalRows(params) {
|
|
802
999
|
const { result } = params;
|
|
803
1000
|
if (result === null)
|
|
804
1001
|
return [];
|
|
805
1002
|
return Array.isArray(result[0]) ? result.at(-1) ?? [] : result;
|
|
806
1003
|
}
|
|
1004
|
+
function isConnectionLoss(params) {
|
|
1005
|
+
const { error } = params;
|
|
1006
|
+
if (!(error instanceof VibeError7))
|
|
1007
|
+
return true;
|
|
1008
|
+
const sqlstate = error.meta.sqlstate;
|
|
1009
|
+
return typeof sqlstate !== "string" || sqlstate.startsWith("08") || ["57P01", "57P02", "57P03"].includes(sqlstate);
|
|
1010
|
+
}
|
|
1011
|
+
function abortTransactionOnStatementFailure(params) {
|
|
1012
|
+
const { scope, error } = params;
|
|
1013
|
+
if (error instanceof VibeError7 && error.meta.driverCode === "ERR_POSTGRES_TOO_MANY_PARAMETERS") {
|
|
1014
|
+
markTransactionAborted({ scope, cause: error });
|
|
1015
|
+
}
|
|
1016
|
+
return abortTransactionOnServerFailure({ scope, error });
|
|
1017
|
+
}
|
|
807
1018
|
function unknownOutcome(params) {
|
|
808
|
-
return new
|
|
1019
|
+
return new VibeError7({
|
|
809
1020
|
code: "VIBE_TRANSACTION",
|
|
810
1021
|
message: "Bun could not confirm transaction recovery or completion; the outcome is unknown. Nothing is retried automatically.",
|
|
811
1022
|
meta: { reason: params.reason ?? "transaction-outcome-unknown", outcome: "unknown" },
|
|
@@ -826,7 +1037,7 @@ export {
|
|
|
826
1037
|
createSavepointCounter,
|
|
827
1038
|
createTemplateCache,
|
|
828
1039
|
engineStatementBudgetMs2 as engineStatementBudgetMs,
|
|
829
|
-
|
|
1040
|
+
isTransactionDeadlineError,
|
|
830
1041
|
mapBunDriverError,
|
|
831
1042
|
nextSavepointName,
|
|
832
1043
|
refuseNestedTransactionOptions,
|
|
@@ -838,8 +1049,8 @@ export {
|
|
|
838
1049
|
toPgArrayLiteral,
|
|
839
1050
|
transactionClosedError,
|
|
840
1051
|
transactionDeadlineError,
|
|
841
|
-
|
|
1052
|
+
transactionOutcomeUnknownError,
|
|
842
1053
|
validateTransactionDeadline2 as validateTransactionDeadline
|
|
843
1054
|
};
|
|
844
1055
|
|
|
845
|
-
//# debugId=
|
|
1056
|
+
//# debugId=ABB8B79ECB9DC73B64756E2164756E21
|