@vibeorm/adapter-sqlite 2.0.0 → 2.0.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.
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -247
- package/dist/index.js.map +4 -7
- package/package.json +2 -2
- package/dist/savepoint-gate.d.ts +0 -86
- package/dist/savepoint-gate.d.ts.map +0 -1
- package/dist/savepoints.d.ts +0 -22
- package/dist/savepoints.d.ts.map +0 -1
- package/dist/transaction-budget.d.ts +0 -144
- package/dist/transaction-budget.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -99,9 +99,9 @@ export { SQLITE_ERROR_CODES, SQLITE_ERROR_REASONS, mapSqliteDriverError, runMapp
|
|
|
99
99
|
export { normalizeRow, normalizeRows, serializeParamsRaw } from "./params.ts";
|
|
100
100
|
export { createOperationQueue } from "./queue.ts";
|
|
101
101
|
export type { OperationQueue } from "./queue.ts";
|
|
102
|
-
export { createSavepointCounter, nextSavepointName } from "
|
|
103
|
-
export type { SavepointCounter } from "
|
|
102
|
+
export { createSavepointCounter, nextSavepointName } from "@vibeorm/runtime/adapter-kit";
|
|
103
|
+
export type { SavepointCounter } from "@vibeorm/runtime/adapter-kit";
|
|
104
104
|
export { refuseNestedTransactionOptions, validateTransactionOptions } from "./transaction-options.ts";
|
|
105
|
-
export { DEFAULT_DEADLINE_CLOCK, engineStatementBudgetMs, isTransactionDeadlineError, startTransactionBudget, transactionClosedError, transactionDeadlineError, transactionOutcomeUnknownError, validateTransactionDeadline, } from "
|
|
106
|
-
export type { BudgetStage, DeadlineClock, TransactionBudget, TransactionOutcome } from "
|
|
105
|
+
export { DEFAULT_DEADLINE_CLOCK, engineStatementBudgetMs, isTransactionDeadlineError, startTransactionBudget, transactionClosedError, transactionDeadlineError, transactionOutcomeUnknownError, validateTransactionDeadline, } from "@vibeorm/runtime/adapter-kit";
|
|
106
|
+
export type { BudgetStage, DeadlineClock, TransactionBudget, TransactionOutcome } from "@vibeorm/runtime/adapter-kit";
|
|
107
107
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKtC,OAAO,KAAK,EAEV,eAAe,EAIhB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKtC,OAAO,KAAK,EAEV,eAAe,EAIhB,MAAM,kBAAkB,CAAC;AAoB1B,kHAAkH;AAClH,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B,CAAC;AAkEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,eAAe,CAkU7E;AAID,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,8BAA8B,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAEtG,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/adapter-sqlite/src/index.ts
|
|
3
3
|
import { Database } from "bun:sqlite";
|
|
4
|
-
import { AsyncLocalStorage
|
|
5
|
-
import { VibeError as
|
|
4
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
5
|
+
import { VibeError as VibeError3, internalError } from "@vibeorm/schema";
|
|
6
|
+
import {
|
|
7
|
+
createSavepointScope,
|
|
8
|
+
isTransactionDeadlineError,
|
|
9
|
+
runNestedSavepoint,
|
|
10
|
+
runRootScope,
|
|
11
|
+
scopeTurn,
|
|
12
|
+
startTransactionBudget,
|
|
13
|
+
transactionOutcomeUnknownError,
|
|
14
|
+
validateTransactionDeadline
|
|
15
|
+
} from "@vibeorm/runtime/adapter-kit";
|
|
6
16
|
|
|
7
17
|
// packages/adapter-sqlite/src/errors.ts
|
|
8
18
|
import { VibeError } from "@vibeorm/schema";
|
|
@@ -118,254 +128,21 @@ function createOperationQueue() {
|
|
|
118
128
|
};
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
// packages/adapter-sqlite/src/savepoint-gate.ts
|
|
122
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
123
|
-
|
|
124
|
-
// packages/adapter-sqlite/src/savepoints.ts
|
|
125
|
-
function createSavepointCounter() {
|
|
126
|
-
return { n: 0 };
|
|
127
|
-
}
|
|
128
|
-
function nextSavepointName(params) {
|
|
129
|
-
return `vibeorm_sp_${params.counter.n++}`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// packages/adapter-sqlite/src/savepoint-gate.ts
|
|
133
|
-
var CURRENT = new AsyncLocalStorage;
|
|
134
|
-
function createSavepointScope() {
|
|
135
|
-
return { depth: 0, state: { counter: createSavepointCounter(), open: null } };
|
|
136
|
-
}
|
|
137
|
-
function runRootScope(params) {
|
|
138
|
-
return CURRENT.run(params.scope, params.body);
|
|
139
|
-
}
|
|
140
|
-
function scopeTurn(params) {
|
|
141
|
-
const open = params.scope.state.open;
|
|
142
|
-
if (mayProceed({ scope: params.scope, open }))
|
|
143
|
-
return;
|
|
144
|
-
return waitForTurn({ scope: params.scope });
|
|
145
|
-
}
|
|
146
|
-
async function runNestedSavepoint(params) {
|
|
147
|
-
const { scope, execute, body } = params;
|
|
148
|
-
const node = await acquire({ scope });
|
|
149
|
-
try {
|
|
150
|
-
if (execute !== undefined)
|
|
151
|
-
await execute(`SAVEPOINT ${node.name}`);
|
|
152
|
-
let result;
|
|
153
|
-
try {
|
|
154
|
-
result = await CURRENT.run(node.scope, () => body(node.scope));
|
|
155
|
-
} catch (error) {
|
|
156
|
-
await drainDescendants({ node });
|
|
157
|
-
if (execute !== undefined) {
|
|
158
|
-
try {
|
|
159
|
-
await execute(`ROLLBACK TO SAVEPOINT ${node.name}`);
|
|
160
|
-
} catch {}
|
|
161
|
-
}
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
await drainDescendants({ node });
|
|
165
|
-
if (execute !== undefined)
|
|
166
|
-
await execute(`RELEASE SAVEPOINT ${node.name}`);
|
|
167
|
-
return result;
|
|
168
|
-
} finally {
|
|
169
|
-
closeNode({ node });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
function mayProceed(params) {
|
|
173
|
-
const { scope, open } = params;
|
|
174
|
-
if (open === null)
|
|
175
|
-
return true;
|
|
176
|
-
if (open.scope === scope)
|
|
177
|
-
return true;
|
|
178
|
-
if (open.scope.depth < scope.depth)
|
|
179
|
-
return true;
|
|
180
|
-
return CURRENT.getStore() === open.scope;
|
|
181
|
-
}
|
|
182
|
-
async function waitForTurn(params) {
|
|
183
|
-
for (;; ) {
|
|
184
|
-
const open = params.scope.state.open;
|
|
185
|
-
if (open === null || mayProceed({ scope: params.scope, open }))
|
|
186
|
-
return;
|
|
187
|
-
await open.settled;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
async function acquire(params) {
|
|
191
|
-
const { scope } = params;
|
|
192
|
-
for (;; ) {
|
|
193
|
-
const open = scope.state.open;
|
|
194
|
-
if (mayProceed({ scope, open }))
|
|
195
|
-
return pushNode({ scope, parent: open });
|
|
196
|
-
await open.settled;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function pushNode(params) {
|
|
200
|
-
const { scope, parent } = params;
|
|
201
|
-
const state = scope.state;
|
|
202
|
-
let settle = () => {
|
|
203
|
-
return;
|
|
204
|
-
};
|
|
205
|
-
const settled = new Promise((resolve) => {
|
|
206
|
-
settle = () => resolve();
|
|
207
|
-
});
|
|
208
|
-
const node = {
|
|
209
|
-
scope: { depth: (parent === null ? 0 : parent.scope.depth) + 1, state },
|
|
210
|
-
name: nextSavepointName({ counter: state.counter }),
|
|
211
|
-
parent,
|
|
212
|
-
settled,
|
|
213
|
-
settle
|
|
214
|
-
};
|
|
215
|
-
state.open = node;
|
|
216
|
-
return node;
|
|
217
|
-
}
|
|
218
|
-
function closeNode(params) {
|
|
219
|
-
const { node } = params;
|
|
220
|
-
const state = node.scope.state;
|
|
221
|
-
if (state.open === node)
|
|
222
|
-
state.open = node.parent;
|
|
223
|
-
node.settle();
|
|
224
|
-
}
|
|
225
|
-
async function drainDescendants(params) {
|
|
226
|
-
const state = params.node.scope.state;
|
|
227
|
-
for (;; ) {
|
|
228
|
-
const open = state.open;
|
|
229
|
-
if (open === null || open === params.node || open.scope.depth <= params.node.scope.depth)
|
|
230
|
-
return;
|
|
231
|
-
await open.settled;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// packages/adapter-sqlite/src/transaction-budget.ts
|
|
236
|
-
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
237
|
-
var DEFAULT_DEADLINE_CLOCK = () => Date.now();
|
|
238
|
-
function validateTransactionDeadline(params) {
|
|
239
|
-
const { options, support, provider } = params;
|
|
240
|
-
const deadline = options?.deadline;
|
|
241
|
-
if (deadline === undefined)
|
|
242
|
-
return;
|
|
243
|
-
if (support === "unsupported") {
|
|
244
|
-
throw new VibeError2({
|
|
245
|
-
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
246
|
-
message: `${provider} cannot enforce a transaction deadline \u2014 remove the \`deadline\` option`,
|
|
247
|
-
meta: { provider, feature: "transactionDeadline", supported: support }
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (!Number.isInteger(deadline.totalMs) || deadline.totalMs <= 0) {
|
|
251
|
-
throw new VibeError2({
|
|
252
|
-
code: "VIBE_VALIDATION",
|
|
253
|
-
message: `transaction deadline \`totalMs\` must be a positive integer of milliseconds, received ${String(deadline.totalMs)}`,
|
|
254
|
-
meta: { provider, feature: "transactionDeadline", totalMs: deadline.totalMs }
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
const requested = deadline.enforcement ?? "between-statements";
|
|
258
|
-
if (requested === "cancel-running-statements" && support !== "cancel-running-statements") {
|
|
259
|
-
throw new VibeError2({
|
|
260
|
-
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
261
|
-
message: `${provider} cannot cancel a statement that is already running, so the requested ` + "`cancel-running-statements` deadline guarantee is refused rather than silently downgraded \u2014 " + 'ask for "between-statements" if a budget checked between statements is enough',
|
|
262
|
-
meta: { provider, feature: "transactionDeadline", requested, supported: support }
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function startTransactionBudget(params) {
|
|
267
|
-
const { options, provider } = params;
|
|
268
|
-
const clock = params.clock ?? DEFAULT_DEADLINE_CLOCK;
|
|
269
|
-
const deadline = options?.deadline;
|
|
270
|
-
const totalMs = deadline === undefined ? null : deadline.totalMs;
|
|
271
|
-
const enforcement = deadline === undefined ? null : deadline.enforcement ?? "between-statements";
|
|
272
|
-
const expiresAt = totalMs === null ? null : clock() + totalMs;
|
|
273
|
-
let outcome = null;
|
|
274
|
-
const budget = {
|
|
275
|
-
enforcement,
|
|
276
|
-
totalMs,
|
|
277
|
-
statementTimeoutMs: options?.timeout ?? null,
|
|
278
|
-
remainingMs() {
|
|
279
|
-
return expiresAt === null ? null : Math.max(0, expiresAt - clock());
|
|
280
|
-
},
|
|
281
|
-
expired() {
|
|
282
|
-
return expiresAt !== null && clock() >= expiresAt;
|
|
283
|
-
},
|
|
284
|
-
assertUsable(assertParams) {
|
|
285
|
-
if (outcome !== null) {
|
|
286
|
-
throw transactionClosedError({ provider, outcome, stage: assertParams.stage });
|
|
287
|
-
}
|
|
288
|
-
if (expiresAt !== null && clock() >= expiresAt) {
|
|
289
|
-
throw transactionDeadlineError({
|
|
290
|
-
provider,
|
|
291
|
-
stage: assertParams.stage,
|
|
292
|
-
totalMs,
|
|
293
|
-
overdueMs: clock() - expiresAt
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
close(closeParams) {
|
|
298
|
-
if (outcome === null)
|
|
299
|
-
outcome = closeParams.outcome;
|
|
300
|
-
},
|
|
301
|
-
get outcome() {
|
|
302
|
-
return outcome;
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
return budget;
|
|
306
|
-
}
|
|
307
|
-
function engineStatementBudgetMs(params) {
|
|
308
|
-
const { budget } = params;
|
|
309
|
-
if (budget.enforcement !== "cancel-running-statements")
|
|
310
|
-
return null;
|
|
311
|
-
const remaining = budget.remainingMs();
|
|
312
|
-
if (remaining === null)
|
|
313
|
-
return null;
|
|
314
|
-
const totalBound = Math.max(1, Math.ceil(remaining));
|
|
315
|
-
return budget.statementTimeoutMs !== null && budget.statementTimeoutMs > 0 ? Math.min(totalBound, budget.statementTimeoutMs) : totalBound;
|
|
316
|
-
}
|
|
317
|
-
function transactionDeadlineError(params) {
|
|
318
|
-
const { provider, stage, totalMs, overdueMs } = params;
|
|
319
|
-
const what = stage === "commit" ? "the transaction was rolled back instead of committed" : "the statement was never sent and the transaction is being rolled back";
|
|
320
|
-
return new VibeError2({
|
|
321
|
-
code: "VIBE_TRANSACTION",
|
|
322
|
-
message: `transaction deadline of ${totalMs} ms expired ${Math.max(0, Math.round(overdueMs))} ms ago \u2014 ${what}. ` + "Nothing is retried automatically: work the callback did outside the database may already have happened.",
|
|
323
|
-
meta: { provider, reason: "transaction-deadline", stage, totalMs, overdueMs: Math.max(0, Math.round(overdueMs)) }
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
function transactionClosedError(params) {
|
|
327
|
-
const { provider, outcome, stage } = params;
|
|
328
|
-
return new VibeError2({
|
|
329
|
-
code: "VIBE_TRANSACTION",
|
|
330
|
-
message: `this transaction has already finished (${outcome}), so its client can no longer be used \u2014 ` + "a handle kept past the end of `$transaction` would run outside the transaction, on a connection " + "that now belongs to someone else",
|
|
331
|
-
meta: { provider, reason: "transaction-closed", outcome, stage }
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
function transactionOutcomeUnknownError(params) {
|
|
335
|
-
const { provider, totalMs, cause } = params;
|
|
336
|
-
return new VibeError2({
|
|
337
|
-
code: "VIBE_TRANSACTION",
|
|
338
|
-
message: `transaction deadline of ${totalMs} ms expired and the rollback that should have cleaned up failed \u2014 ` + "the commit state of this transaction is UNKNOWN. It is not retried automatically; inspect the data " + "before deciding.",
|
|
339
|
-
meta: {
|
|
340
|
-
provider,
|
|
341
|
-
reason: "transaction-outcome-unknown",
|
|
342
|
-
outcome: "unknown",
|
|
343
|
-
totalMs,
|
|
344
|
-
rollbackFailed: true
|
|
345
|
-
},
|
|
346
|
-
cause
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
function isTransactionDeadlineError(error) {
|
|
350
|
-
const reason = error?.meta?.reason;
|
|
351
|
-
return reason === "transaction-deadline";
|
|
352
|
-
}
|
|
353
|
-
|
|
354
131
|
// packages/adapter-sqlite/src/transaction-options.ts
|
|
355
|
-
import { VibeError as
|
|
132
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
356
133
|
function validateTransactionOptions(params) {
|
|
357
134
|
const { options } = params;
|
|
358
135
|
if (options === undefined)
|
|
359
136
|
return;
|
|
360
137
|
if (options.isolationLevel !== undefined && options.isolationLevel !== "Serializable") {
|
|
361
|
-
throw new
|
|
138
|
+
throw new VibeError2({
|
|
362
139
|
code: "VIBE_VALIDATION",
|
|
363
140
|
message: `sqlite transactions are always serializable \u2014 isolationLevel "${options.isolationLevel}" cannot be honored; pass "Serializable" or omit the option`,
|
|
364
141
|
meta: { dialect: "sqlite", isolationLevel: options.isolationLevel }
|
|
365
142
|
});
|
|
366
143
|
}
|
|
367
144
|
if (options.timeout !== undefined) {
|
|
368
|
-
throw new
|
|
145
|
+
throw new VibeError2({
|
|
369
146
|
code: "VIBE_VALIDATION",
|
|
370
147
|
message: "sqlite has no per-statement timeout, so a transaction `timeout` cannot be enforced database-side (busy_timeout only bounds lock acquisition, not statement runtime) \u2014 remove the option",
|
|
371
148
|
meta: { dialect: "sqlite", timeout: options.timeout }
|
|
@@ -392,7 +169,7 @@ function refuseNestedTransactionOptions(params) {
|
|
|
392
169
|
meta.timeout = options.timeout;
|
|
393
170
|
if (options.deadline !== undefined)
|
|
394
171
|
meta.deadline = options.deadline;
|
|
395
|
-
throw new
|
|
172
|
+
throw new VibeError2({
|
|
396
173
|
code: "VIBE_VALIDATION",
|
|
397
174
|
message: `nested transactions run as savepoints and cannot honor ${passed.join(" or ")} \u2014 a savepoint cannot change the isolation level of the transaction it joins, a nested timeout is not enforceable, and a nested deadline would need its own session setting on a connection it does not own (it inherits the top-level one instead); pass options on the top-level $transaction`,
|
|
398
175
|
meta
|
|
@@ -400,6 +177,17 @@ function refuseNestedTransactionOptions(params) {
|
|
|
400
177
|
}
|
|
401
178
|
|
|
402
179
|
// packages/adapter-sqlite/src/index.ts
|
|
180
|
+
import { createSavepointCounter, nextSavepointName } from "@vibeorm/runtime/adapter-kit";
|
|
181
|
+
import {
|
|
182
|
+
DEFAULT_DEADLINE_CLOCK,
|
|
183
|
+
engineStatementBudgetMs,
|
|
184
|
+
isTransactionDeadlineError as isTransactionDeadlineError2,
|
|
185
|
+
startTransactionBudget as startTransactionBudget2,
|
|
186
|
+
transactionClosedError,
|
|
187
|
+
transactionDeadlineError,
|
|
188
|
+
transactionOutcomeUnknownError as transactionOutcomeUnknownError2,
|
|
189
|
+
validateTransactionDeadline as validateTransactionDeadline2
|
|
190
|
+
} from "@vibeorm/runtime/adapter-kit";
|
|
403
191
|
var DIALECT = "sqlite";
|
|
404
192
|
var PROVIDER = "sqlite";
|
|
405
193
|
var WIRE = Object.freeze({ dateTime: "text", json: "text", numbers: "native" });
|
|
@@ -422,7 +210,7 @@ function withSafeIntegers(statement) {
|
|
|
422
210
|
function sqliteAdapter(options) {
|
|
423
211
|
const busyTimeoutMs = options?.busyTimeoutMs;
|
|
424
212
|
if (busyTimeoutMs !== undefined && (!Number.isInteger(busyTimeoutMs) || busyTimeoutMs < 0)) {
|
|
425
|
-
throw new
|
|
213
|
+
throw new VibeError3({
|
|
426
214
|
code: "VIBE_VALIDATION",
|
|
427
215
|
message: `busyTimeoutMs must be a non-negative integer of milliseconds, received ${String(busyTimeoutMs)}`,
|
|
428
216
|
meta: { busyTimeoutMs }
|
|
@@ -431,10 +219,10 @@ function sqliteAdapter(options) {
|
|
|
431
219
|
const queue = createOperationQueue();
|
|
432
220
|
const ownsDatabase = options?.database === undefined;
|
|
433
221
|
const filename = options?.filename ?? ":memory:";
|
|
434
|
-
const txContext = new
|
|
222
|
+
const txContext = new AsyncLocalStorage;
|
|
435
223
|
function assertNotInsideTransaction() {
|
|
436
224
|
if (txContext.getStore() === true) {
|
|
437
|
-
throw new
|
|
225
|
+
throw new VibeError3({
|
|
438
226
|
code: "VIBE_TRANSACTION",
|
|
439
227
|
message: "root client used inside a transaction callback \u2014 use the tx client the callback receives; the root connection would deadlock behind the open transaction",
|
|
440
228
|
meta: { provider: PROVIDER }
|
|
@@ -608,11 +396,11 @@ function sqliteAdapter(options) {
|
|
|
608
396
|
}
|
|
609
397
|
export {
|
|
610
398
|
validateTransactionOptions,
|
|
611
|
-
validateTransactionDeadline,
|
|
612
|
-
transactionOutcomeUnknownError,
|
|
399
|
+
validateTransactionDeadline2 as validateTransactionDeadline,
|
|
400
|
+
transactionOutcomeUnknownError2 as transactionOutcomeUnknownError,
|
|
613
401
|
transactionDeadlineError,
|
|
614
402
|
transactionClosedError,
|
|
615
|
-
startTransactionBudget,
|
|
403
|
+
startTransactionBudget2 as startTransactionBudget,
|
|
616
404
|
sqliteErrorCode,
|
|
617
405
|
sqliteAdapter,
|
|
618
406
|
serializeParamsRaw,
|
|
@@ -622,7 +410,7 @@ export {
|
|
|
622
410
|
normalizeRow,
|
|
623
411
|
nextSavepointName,
|
|
624
412
|
mapSqliteDriverError,
|
|
625
|
-
isTransactionDeadlineError,
|
|
413
|
+
isTransactionDeadlineError2 as isTransactionDeadlineError,
|
|
626
414
|
engineStatementBudgetMs,
|
|
627
415
|
createSavepointCounter,
|
|
628
416
|
createOperationQueue,
|
|
@@ -631,4 +419,4 @@ export {
|
|
|
631
419
|
DEFAULT_DEADLINE_CLOCK
|
|
632
420
|
};
|
|
633
421
|
|
|
634
|
-
//# debugId=
|
|
422
|
+
//# debugId=B88E19439473D31164756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/errors.ts", "../src/params.ts", "../src/queue.ts", "../src/
|
|
3
|
+
"sources": ["../src/index.ts", "../src/errors.ts", "../src/params.ts", "../src/queue.ts", "../src/transaction-options.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @vibeorm/adapter-sqlite — bun:sqlite (embedded SQLite) adapter for\n * VibeORM v2.\n *\n * bun:sqlite is a *synchronous* single-connection driver, so every call\n * completes without I/O waits and the adapter wraps results in\n * already-resolved promises — the cheapest possible `execute`. Like PGlite,\n * one connection means every operation runs through one serial queue:\n * a whole `transaction()` holds the queue until it commits or rolls back, so\n * concurrent client calls can never interleave into an open transaction.\n *\n * INTEGER WIDTH (the BigInt story): SQLite INTEGER columns are 64-bit, but JS\n * numbers are only exact to ±(2^53−1) — a plain read of a large BigInt column\n * silently corrupts (pinned on Bun 1.3.14: `SELECT 9007199254740993` returns\n * `9007199254740992` without safeIntegers). The adapter therefore ALWAYS reads\n * through bun:sqlite's `safeIntegers` (database-level for connections it\n * opens, statement-level `stmt.safeIntegers(true)` for every statement it\n * prepares, which also covers injected databases), then normalizes rows:\n * bigints inside the safe range become `number`, larger ones stay `bigint`.\n * That is exactly the shape the runtime's `SQLITE_CODECS` expect — Int and\n * Boolean columns arrive as numbers, and >2^53 BigInt columns round-trip\n * losslessly as bigint. See params.ts for the full reasoning.\n *\n * PRAGMAS applied on open (each documented on {@link SqliteAdapterOptions}):\n * `foreign_keys = ON` (mandatory — SQLite defaults OFF and the ORM's FK\n * semantics depend on it), `busy_timeout` (lock-wait bound), and\n * `journal_mode = WAL` for file databases (readers no longer block the\n * writer). Transactions open with `BEGIN IMMEDIATE`: the write lock is taken\n * up front, so a write after a read inside the transaction can never hit the\n * SQLITE_BUSY deadlock that lock *upgrades* are prone to.\n */\n\nimport { Database } from \"bun:sqlite\";\nimport type { SQLQueryBindings } from \"bun:sqlite\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { VibeError, internalError } from \"@vibeorm/schema\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport { mapSqliteDriverError, runMapped } from \"./errors.ts\";\nimport { normalizeRows, serializeParamsRaw } from \"./params.ts\";\nimport { createOperationQueue } from \"./queue.ts\";\nimport {\n createSavepointScope,\n runNestedSavepoint,\n runRootScope,\n scopeTurn,\n type SavepointScope,\n} from \"./savepoint-gate.ts\";\nimport {\n isTransactionDeadlineError,\n startTransactionBudget,\n type TransactionBudget,\n transactionOutcomeUnknownError,\n validateTransactionDeadline,\n} from \"./transaction-budget.ts\";\nimport { refuseNestedTransactionOptions, validateTransactionOptions } from \"./transaction-options.ts\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/** Options for {@link sqliteAdapter}. (Factories are the documented exception allowed a plain options object.) */\nexport type SqliteAdapterOptions = {\n /**\n * Database file to open. Defaults to `\":memory:\"` — a fresh private\n * in-memory database, the usual choice for tests. Ignored when `database`\n * is injected.\n */\n readonly filename?: string;\n /**\n * Open the file read-only (`SQLITE_READONLY` errors on writes, mapped to\n * `VIBE_ADAPTER`). The WAL pragma is skipped on read-only connections —\n * switching journal modes is a write. Ignored when `database` is injected.\n */\n readonly readonly?: boolean;\n /**\n * `PRAGMA busy_timeout` in milliseconds — how long a statement blocks on a\n * competing lock before failing with SQLITE_BUSY (mapped to\n * `VIBE_TRANSACTION`, `meta.reason: \"busy\"`). Default 5000. On an injected\n * `database` the pragma is applied only when this option is passed\n * explicitly, so the adapter never silently overrides the owner's setting.\n */\n readonly busyTimeoutMs?: number;\n /**\n * Switch file databases to `PRAGMA journal_mode = WAL` on open (default\n * true): readers stop blocking the writer, the sane default for anything\n * long-lived. Skipped for `\":memory:\"` databases (they have no WAL),\n * read-only connections, and injected `database` instances (the owner\n * decides their journal mode).\n */\n readonly wal?: boolean;\n /**\n * An already-open `bun:sqlite` Database to run on — sharing one in-memory\n * database across adapters makes multi-adapter tests trivial. The adapter\n * never closes an injected instance (`disconnect()` is a no-op), never\n * changes its journal mode, and still applies `PRAGMA foreign_keys = ON`\n * (the ORM requires it). For lossless >2^53 BigInt reads the adapter sets\n * statement-level safeIntegers on every statement it prepares, so an\n * injected database needs no special construction flags.\n */\n readonly database?: Database;\n};\n\n// ─── Internals ────────────────────────────────────────────────────\n\nconst DIALECT: Dialect = \"sqlite\";\nconst PROVIDER: Provider = \"sqlite\";\n\n/**\n * bun:sqlite wire (board #30, live-verified): INTEGER/REAL → numbers (large\n * INTEGER → native `bigint`, which every decoder passes through); DateTime and\n * Json ride TEXT, so their decoders do real work and are declared \"text\"\n * (prunes nothing — the declaration is the honest record).\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"text\", json: \"text\", numbers: \"native\" } as const);\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: false` — bun:sqlite is ONE synchronous connection behind\n * an in-process queue. There is no pool to exhaust and no wait to bound.\n * - `statementTimeout: \"unsupported\"` — sqlite has no per-statement timeout;\n * `busy_timeout` bounds LOCK acquisition only, which is why the `timeout`\n * option is already refused outright (./transaction-options.ts).\n * - `transactionDeadline: \"between-statements\"` — an expired budget stops the\n * next statement and the commit, which is real enforcement. It is NOT\n * cancellation: bun:sqlite runs statements synchronously on the JS thread, so\n * a long statement blocks the event loop and no timer of any kind can fire\n * while it runs. Asking for `\"cancel-running-statements\"` is refused.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: false,\n statementTimeout: \"unsupported\",\n transactionDeadline: \"between-statements\",\n} as const);\nconst DEFAULT_BUSY_TIMEOUT_MS = 5000;\n\n/** `\":memory:\"` (also in URI form) and `\"\"` (anonymous temp) have no WAL. */\nfunction isMemoryFilename(params: { filename: string }): boolean {\n return params.filename === \"\" || params.filename.includes(\":memory:\");\n}\n\n/**\n * The one cast at the driver boundary. ORM-path values were encoded by the\n * runtime codec table into driver-native shapes; raw-path values went through\n * `serializeParamsRaw`. Verified by the param-passthrough unit tests.\n */\nfunction asBindings(values: readonly unknown[]): SQLQueryBindings[] {\n return values as SQLQueryBindings[];\n}\n\n/**\n * `Statement.safeIntegers()` exists at runtime since Bun 1.1.14 (pinned by\n * the integer-width unit tests: it returns the statement and applies to\n * cached statements too) but is missing from the installed @types/bun — this\n * minimal structural view bridges the gap without `any`.\n */\ntype SafeIntegersStatement = { safeIntegers(toggle: boolean): unknown };\n\n/** Flip a prepared statement to lossless 64-bit integer reads (see module doc). */\nfunction withSafeIntegers<S>(statement: S): S {\n (statement as unknown as SafeIntegersStatement).safeIntegers(true);\n return statement;\n}\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\n/**\n * Create a VibeORM adapter backed by bun:sqlite.\n *\n * @example In-memory database (tests)\n * ```ts\n * const adapter = sqliteAdapter();\n * await adapter.executeUnsafe({ text: `CREATE TABLE \"User\" (\"id\" INTEGER PRIMARY KEY)` });\n * ```\n *\n * @example File database / shared instance\n * ```ts\n * const file = sqliteAdapter({ filename: \"./app.db\" });\n * const shared = sqliteAdapter({ database: existingDatabase });\n * ```\n *\n * `disconnect()` closes a database the adapter opened; a later call opens a\n * fresh one (lazy reconnect, matching the pool-backed adapters). With the\n * default `\":memory:\"` filename that fresh database is a new, empty one.\n *\n * @throws VibeError `VIBE_VALIDATION` when `busyTimeoutMs` is not a\n * non-negative integer — validated eagerly, the value lands in PRAGMA text.\n */\nexport function sqliteAdapter(options?: SqliteAdapterOptions): DatabaseAdapter {\n const busyTimeoutMs = options?.busyTimeoutMs;\n if (busyTimeoutMs !== undefined && (!Number.isInteger(busyTimeoutMs) || busyTimeoutMs < 0)) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `busyTimeoutMs must be a non-negative integer of milliseconds, received ${String(busyTimeoutMs)}`,\n meta: { busyTimeoutMs },\n });\n }\n\n const queue = createOperationQueue();\n const ownsDatabase = options?.database === undefined;\n const filename = options?.filename ?? \":memory:\";\n\n /**\n * Deadlock guard (the pglite F6 lesson): a whole transaction holds the\n * operation queue, so a ROOT-adapter call issued from inside a\n * `transaction()` callback would queue behind the very transaction it is\n * running in and hang forever. Root entry points refuse instead; the\n * tx-bound adapter bypasses the queue and stays fully usable.\n */\n const txContext = new AsyncLocalStorage<true>();\n\n function assertNotInsideTransaction(): void {\n if (txContext.getStore() === true) {\n throw new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n \"root client used inside a transaction callback — use the tx client the callback receives; the root connection would deadlock behind the open transaction\",\n meta: { provider: PROVIDER },\n });\n }\n }\n\n let database: Database | null = options?.database ?? null;\n let configured = false;\n\n /**\n * Session pragmas, applied once per opened connection (see module doc).\n * On an injected database only `foreign_keys` is unconditional; the\n * busy timeout is applied only when explicitly configured, and the journal\n * mode is never touched.\n */\n function applyPragmas(params: { db: Database }): void {\n const { db } = params;\n // MANDATORY: sqlite ships with foreign-key enforcement OFF; every FK\n // semantic the ORM promises (RESTRICT errors, ON DELETE CASCADE) needs it.\n db.run(\"PRAGMA foreign_keys = ON\");\n if (ownsDatabase || busyTimeoutMs !== undefined) {\n db.run(`PRAGMA busy_timeout = ${busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS}`);\n }\n if (\n ownsDatabase &&\n options?.wal !== false &&\n options?.readonly !== true &&\n !isMemoryFilename({ filename })\n ) {\n db.run(\"PRAGMA journal_mode = WAL\");\n }\n }\n\n /** Open (or reuse) the connection lazily; sync because the driver is. */\n function getDatabase(): Database {\n if (database === null) {\n database = runMapped(\n () =>\n new Database(\n filename,\n options?.readonly === true\n ? { readonly: true, safeIntegers: true }\n : { create: true, readwrite: true, safeIntegers: true },\n ),\n );\n configured = false;\n }\n if (!configured) {\n runMapped(() => applyPragmas({ db: database as Database }));\n configured = true;\n }\n return database;\n }\n\n /**\n * ORM hot path: `db.query()` prepares through bun:sqlite's built-in\n * per-connection statement cache (pinned: same text ⇒ same Statement\n * object), so no adapter-side LRU is needed. Values bind verbatim — the\n * runtime codec table already encoded them (params.ts).\n */\n function runExecute(params: {\n db: Database;\n text: string;\n values: unknown[];\n }): Record<string, unknown>[] {\n return runMapped(() => {\n const statement = withSafeIntegers(params.db.query(params.text));\n const rows = statement.all(...asBindings(params.values)) as Record<string, unknown>[];\n return normalizeRows({ rows });\n });\n }\n\n /**\n * Raw path — uncached (`db.prepare`), finalized after use, raw-path\n * serialization for user-supplied values.\n *\n * With `values` present the text must be a SINGLE statement: bun:sqlite's\n * prepare SILENTLY DROPS everything after the first statement (pinned on\n * Bun 1.3.14), so multi-statement text with parameters would half-run.\n * Rows come from `all()` when the statement has result columns\n * (`columnNames`), with `affectedRows` = the row count (exact for\n * DML…RETURNING); otherwise from `run()`, whose driver-reported `changes`\n * is exact for UPDATE/DELETE/INSERT.\n *\n * With `values` absent the text runs as a multi-statement batch through\n * `db.run()` — the shape @vibeorm/migrate emits for DDL. The driver executes\n * every statement and reports cumulative changes; rows are always `[]` on\n * this path (the sync driver cannot both batch and read), so callers that\n * want rows from a single statement pass `values: []` — the runtime's\n * `$queryRawUnsafe` always does.\n */\n function runExecuteUnsafe(params: { db: Database; text: string; values?: unknown[] }): QueryResult {\n return runMapped(() => {\n const { db, text, values } = params;\n if (values === undefined) {\n const changes = db.run(text);\n return { rows: [], affectedRows: changes.changes };\n }\n const statement = withSafeIntegers(db.prepare(text));\n try {\n const bound = asBindings(serializeParamsRaw({ values }));\n if (statement.columnNames.length > 0) {\n const rows = statement.all(...bound) as Record<string, unknown>[];\n return { rows: normalizeRows({ rows }), affectedRows: rows.length };\n }\n const result = statement.run(...bound);\n return { rows: [], affectedRows: result.changes };\n } finally {\n statement.finalize();\n }\n });\n }\n\n /**\n * Adapter bound to an open transaction. It never re-enters the queue — the\n * enclosing `transaction()` call already owns the connection — and nested\n * `transaction()` calls become savepoints whose LIFETIMES are serialized by\n * the savepoint gate (F14): overlapping siblings would share one savepoint\n * stack, and the earlier one's rollback would erase the later one's work.\n */\n function createTransactionAdapter(params: {\n db: Database;\n scope: SavepointScope;\n budget: TransactionBudget;\n }): DatabaseAdapter {\n const { db, scope, budget } = params;\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n\n async execute(execParams) {\n const turn = scopeTurn({ scope });\n if (turn !== undefined) await turn;\n // After the turn, not before: waiting for a sibling savepoint spends\n // the same budget the statement is about to run against.\n budget.assertUsable({ stage: \"statement\" });\n return runExecute({ db, ...execParams });\n },\n\n async executeUnsafe(execParams) {\n const turn = scopeTurn({ scope });\n if (turn !== undefined) await turn;\n budget.assertUsable({ stage: \"statement\" });\n return runExecuteUnsafe({ db, ...execParams });\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n nestedOptions?: TransactionOptions,\n ): Promise<T> {\n refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });\n budget.assertUsable({ stage: \"nested\" });\n return runNestedSavepoint({\n scope,\n execute: (statement) => runMapped(() => db.run(statement)),\n // The child shares the ROOT budget by reference: a savepoint never\n // gets an independent clock or session setting.\n body: (child) => fn(createTransactionAdapter({ db, scope: child, budget })),\n });\n },\n\n async connect(): Promise<void> {\n // Already connected — this adapter is bound to an open transaction.\n },\n\n async disconnect(): Promise<void> {\n // The database outlives the transaction; closing here would be wrong.\n },\n\n formatArrayParam(values: unknown[]): unknown {\n throw arrayParamBug({ values });\n },\n };\n }\n\n /**\n * sqlite has no array parameters — the sqlite dialect declares\n * `scalarArrays: false` and expands `IN` lists to individual placeholders\n * (`inArrayStrategy: \"expand\"`), so this method being reached means an\n * upstream runtime bug routed an array here.\n */\n function arrayParamBug(params: { values: unknown[] }): VibeError {\n return internalError({\n message:\n \"formatArrayParam called on the sqlite adapter — sqlite has no array parameters; the dialect expands IN lists to individual placeholders, so this call is an upstream runtime bug\",\n meta: { provider: PROVIDER, valueCount: params.values.length },\n });\n }\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n\n async execute(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecute({ db: getDatabase(), ...params }));\n },\n\n async executeUnsafe(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecuteUnsafe({ db: getDatabase(), ...params }));\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n assertNotInsideTransaction();\n // Refuse unsupportable options BEFORE taking the queue or issuing BEGIN,\n // so a bad option fails fast and leaves no transaction open.\n validateTransactionOptions({ options: transactionOptions });\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n });\n\n return queue.run(async () => {\n const db = getDatabase();\n const scope = createSavepointScope();\n // The deadline clock starts here — after the queue turn, immediately\n // before BEGIN. Waiting for the connection is not charged to it.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n // Write-intent lock up front — see module doc on BEGIN IMMEDIATE.\n runMapped(() => db.run(\"BEGIN IMMEDIATE\"));\n try {\n const result = await txContext.run(true, () =>\n runRootScope({ scope, body: () => fn(createTransactionAdapter({ db, scope, budget })) }),\n );\n // An expired budget must never commit, however the callback ended.\n budget.assertUsable({ stage: \"commit\" });\n runMapped(() => db.run(\"COMMIT\"));\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n // Best effort: never mask the original failure (the user callback's\n // own error, or a failed COMMIT) with a rollback failure.\n try {\n db.run(\"ROLLBACK\");\n budget.close({ outcome: \"rolled-back\" });\n } catch {\n // The rollback that should have cleaned up failed: the commit state\n // is genuinely unknown, and saying \"rolled back\" would be a lie.\n budget.close({ outcome: \"unknown\" });\n if (budget.totalMs !== null && isTransactionDeadlineError(error)) {\n throw transactionOutcomeUnknownError({\n provider: PROVIDER,\n totalMs: budget.totalMs,\n cause: error,\n });\n }\n }\n throw error;\n }\n });\n },\n\n async connect(): Promise<void> {\n getDatabase();\n },\n\n /**\n * ONE ordered lifecycle (F15): the reference is cleared and the handle\n * closed INSIDE the queue, so work this adapter already accepted runs on\n * the database it was accepted against. Clearing `database` up front — as\n * this did — sent that queued work through `getDatabase()` with nothing in\n * hand, silently opening a FRESH (for `\":memory:\"`, empty) database while\n * the close went to the old handle.\n *\n * Refuses inside a transaction callback for the same reason the other entry\n * points do: the close would queue behind the transaction that is waiting\n * for it. Before this guard that call hung forever.\n */\n async disconnect(): Promise<void> {\n assertNotInsideTransaction();\n if (!ownsDatabase) return;\n await queue.run(async () => {\n if (database === null) return;\n const closing = database;\n database = null;\n configured = false;\n runMapped(() => closing.close());\n });\n },\n\n formatArrayParam(values: unknown[]): unknown {\n throw arrayParamBug({ values });\n },\n };\n}\n\n// ─── Re-exports (driver-error table, helpers) ─────────────────────\n\nexport {\n SQLITE_ERROR_CODES,\n SQLITE_ERROR_REASONS,\n mapSqliteDriverError,\n runMapped,\n sqliteErrorCode,\n} from \"./errors.ts\";\nexport { normalizeRow, normalizeRows, serializeParamsRaw } from \"./params.ts\";\nexport { createOperationQueue } from \"./queue.ts\";\nexport type { OperationQueue } from \"./queue.ts\";\nexport { createSavepointCounter, nextSavepointName } from \"./savepoints.ts\";\nexport type { SavepointCounter } from \"./savepoints.ts\";\nexport { refuseNestedTransactionOptions, validateTransactionOptions } from \"./transaction-options.ts\";\n// ─── EPIC 6: transaction budgets ──────────────────────────────────\nexport {\n DEFAULT_DEADLINE_CLOCK,\n engineStatementBudgetMs,\n isTransactionDeadlineError,\n startTransactionBudget,\n transactionClosedError,\n transactionDeadlineError,\n transactionOutcomeUnknownError,\n validateTransactionDeadline,\n} from \"./transaction-budget.ts\";\nexport type { BudgetStage, DeadlineClock, TransactionBudget, TransactionOutcome } from \"./transaction-budget.ts\";\n",
|
|
5
|
+
"/**\n * @vibeorm/adapter-sqlite — bun:sqlite (embedded SQLite) adapter for\n * VibeORM v2.\n *\n * bun:sqlite is a *synchronous* single-connection driver, so every call\n * completes without I/O waits and the adapter wraps results in\n * already-resolved promises — the cheapest possible `execute`. Like PGlite,\n * one connection means every operation runs through one serial queue:\n * a whole `transaction()` holds the queue until it commits or rolls back, so\n * concurrent client calls can never interleave into an open transaction.\n *\n * INTEGER WIDTH (the BigInt story): SQLite INTEGER columns are 64-bit, but JS\n * numbers are only exact to ±(2^53−1) — a plain read of a large BigInt column\n * silently corrupts (pinned on Bun 1.3.14: `SELECT 9007199254740993` returns\n * `9007199254740992` without safeIntegers). The adapter therefore ALWAYS reads\n * through bun:sqlite's `safeIntegers` (database-level for connections it\n * opens, statement-level `stmt.safeIntegers(true)` for every statement it\n * prepares, which also covers injected databases), then normalizes rows:\n * bigints inside the safe range become `number`, larger ones stay `bigint`.\n * That is exactly the shape the runtime's `SQLITE_CODECS` expect — Int and\n * Boolean columns arrive as numbers, and >2^53 BigInt columns round-trip\n * losslessly as bigint. See params.ts for the full reasoning.\n *\n * PRAGMAS applied on open (each documented on {@link SqliteAdapterOptions}):\n * `foreign_keys = ON` (mandatory — SQLite defaults OFF and the ORM's FK\n * semantics depend on it), `busy_timeout` (lock-wait bound), and\n * `journal_mode = WAL` for file databases (readers no longer block the\n * writer). Transactions open with `BEGIN IMMEDIATE`: the write lock is taken\n * up front, so a write after a read inside the transaction can never hit the\n * SQLITE_BUSY deadlock that lock *upgrades* are prone to.\n */\n\nimport { Database } from \"bun:sqlite\";\nimport type { SQLQueryBindings } from \"bun:sqlite\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { VibeError, internalError } from \"@vibeorm/schema\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport {\n createSavepointScope,\n isTransactionDeadlineError,\n runNestedSavepoint,\n runRootScope,\n type SavepointScope,\n scopeTurn,\n startTransactionBudget,\n type TransactionBudget,\n transactionOutcomeUnknownError,\n validateTransactionDeadline,\n} from \"@vibeorm/runtime/adapter-kit\";\nimport { mapSqliteDriverError, runMapped } from \"./errors.ts\";\nimport { normalizeRows, serializeParamsRaw } from \"./params.ts\";\nimport { createOperationQueue } from \"./queue.ts\";\nimport { refuseNestedTransactionOptions, validateTransactionOptions } from \"./transaction-options.ts\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/** Options for {@link sqliteAdapter}. (Factories are the documented exception allowed a plain options object.) */\nexport type SqliteAdapterOptions = {\n /**\n * Database file to open. Defaults to `\":memory:\"` — a fresh private\n * in-memory database, the usual choice for tests. Ignored when `database`\n * is injected.\n */\n readonly filename?: string;\n /**\n * Open the file read-only (`SQLITE_READONLY` errors on writes, mapped to\n * `VIBE_ADAPTER`). The WAL pragma is skipped on read-only connections —\n * switching journal modes is a write. Ignored when `database` is injected.\n */\n readonly readonly?: boolean;\n /**\n * `PRAGMA busy_timeout` in milliseconds — how long a statement blocks on a\n * competing lock before failing with SQLITE_BUSY (mapped to\n * `VIBE_TRANSACTION`, `meta.reason: \"busy\"`). Default 5000. On an injected\n * `database` the pragma is applied only when this option is passed\n * explicitly, so the adapter never silently overrides the owner's setting.\n */\n readonly busyTimeoutMs?: number;\n /**\n * Switch file databases to `PRAGMA journal_mode = WAL` on open (default\n * true): readers stop blocking the writer, the sane default for anything\n * long-lived. Skipped for `\":memory:\"` databases (they have no WAL),\n * read-only connections, and injected `database` instances (the owner\n * decides their journal mode).\n */\n readonly wal?: boolean;\n /**\n * An already-open `bun:sqlite` Database to run on — sharing one in-memory\n * database across adapters makes multi-adapter tests trivial. The adapter\n * never closes an injected instance (`disconnect()` is a no-op), never\n * changes its journal mode, and still applies `PRAGMA foreign_keys = ON`\n * (the ORM requires it). For lossless >2^53 BigInt reads the adapter sets\n * statement-level safeIntegers on every statement it prepares, so an\n * injected database needs no special construction flags.\n */\n readonly database?: Database;\n};\n\n// ─── Internals ────────────────────────────────────────────────────\n\nconst DIALECT: Dialect = \"sqlite\";\nconst PROVIDER: Provider = \"sqlite\";\n\n/**\n * bun:sqlite wire (board #30, live-verified): INTEGER/REAL → numbers (large\n * INTEGER → native `bigint`, which every decoder passes through); DateTime and\n * Json ride TEXT, so their decoders do real work and are declared \"text\"\n * (prunes nothing — the declaration is the honest record).\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"text\", json: \"text\", numbers: \"native\" } as const);\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: false` — bun:sqlite is ONE synchronous connection behind\n * an in-process queue. There is no pool to exhaust and no wait to bound.\n * - `statementTimeout: \"unsupported\"` — sqlite has no per-statement timeout;\n * `busy_timeout` bounds LOCK acquisition only, which is why the `timeout`\n * option is already refused outright (./transaction-options.ts).\n * - `transactionDeadline: \"between-statements\"` — an expired budget stops the\n * next statement and the commit, which is real enforcement. It is NOT\n * cancellation: bun:sqlite runs statements synchronously on the JS thread, so\n * a long statement blocks the event loop and no timer of any kind can fire\n * while it runs. Asking for `\"cancel-running-statements\"` is refused.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: false,\n statementTimeout: \"unsupported\",\n transactionDeadline: \"between-statements\",\n} as const);\nconst DEFAULT_BUSY_TIMEOUT_MS = 5000;\n\n/** `\":memory:\"` (also in URI form) and `\"\"` (anonymous temp) have no WAL. */\nfunction isMemoryFilename(params: { filename: string }): boolean {\n return params.filename === \"\" || params.filename.includes(\":memory:\");\n}\n\n/**\n * The one cast at the driver boundary. ORM-path values were encoded by the\n * runtime codec table into driver-native shapes; raw-path values went through\n * `serializeParamsRaw`. Verified by the param-passthrough unit tests.\n */\nfunction asBindings(values: readonly unknown[]): SQLQueryBindings[] {\n return values as SQLQueryBindings[];\n}\n\n/**\n * `Statement.safeIntegers()` exists at runtime since Bun 1.1.14 (pinned by\n * the integer-width unit tests: it returns the statement and applies to\n * cached statements too) but is missing from the installed @types/bun — this\n * minimal structural view bridges the gap without `any`.\n */\ntype SafeIntegersStatement = { safeIntegers(toggle: boolean): unknown };\n\n/** Flip a prepared statement to lossless 64-bit integer reads (see module doc). */\nfunction withSafeIntegers<S>(statement: S): S {\n (statement as unknown as SafeIntegersStatement).safeIntegers(true);\n return statement;\n}\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\n/**\n * Create a VibeORM adapter backed by bun:sqlite.\n *\n * @example In-memory database (tests)\n * ```ts\n * const adapter = sqliteAdapter();\n * await adapter.executeUnsafe({ text: `CREATE TABLE \"User\" (\"id\" INTEGER PRIMARY KEY)` });\n * ```\n *\n * @example File database / shared instance\n * ```ts\n * const file = sqliteAdapter({ filename: \"./app.db\" });\n * const shared = sqliteAdapter({ database: existingDatabase });\n * ```\n *\n * `disconnect()` closes a database the adapter opened; a later call opens a\n * fresh one (lazy reconnect, matching the pool-backed adapters). With the\n * default `\":memory:\"` filename that fresh database is a new, empty one.\n *\n * @throws VibeError `VIBE_VALIDATION` when `busyTimeoutMs` is not a\n * non-negative integer — validated eagerly, the value lands in PRAGMA text.\n */\nexport function sqliteAdapter(options?: SqliteAdapterOptions): DatabaseAdapter {\n const busyTimeoutMs = options?.busyTimeoutMs;\n if (busyTimeoutMs !== undefined && (!Number.isInteger(busyTimeoutMs) || busyTimeoutMs < 0)) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `busyTimeoutMs must be a non-negative integer of milliseconds, received ${String(busyTimeoutMs)}`,\n meta: { busyTimeoutMs },\n });\n }\n\n const queue = createOperationQueue();\n const ownsDatabase = options?.database === undefined;\n const filename = options?.filename ?? \":memory:\";\n\n /**\n * Deadlock guard (the pglite F6 lesson): a whole transaction holds the\n * operation queue, so a ROOT-adapter call issued from inside a\n * `transaction()` callback would queue behind the very transaction it is\n * running in and hang forever. Root entry points refuse instead; the\n * tx-bound adapter bypasses the queue and stays fully usable.\n */\n const txContext = new AsyncLocalStorage<true>();\n\n function assertNotInsideTransaction(): void {\n if (txContext.getStore() === true) {\n throw new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n \"root client used inside a transaction callback — use the tx client the callback receives; the root connection would deadlock behind the open transaction\",\n meta: { provider: PROVIDER },\n });\n }\n }\n\n let database: Database | null = options?.database ?? null;\n let configured = false;\n\n /**\n * Session pragmas, applied once per opened connection (see module doc).\n * On an injected database only `foreign_keys` is unconditional; the\n * busy timeout is applied only when explicitly configured, and the journal\n * mode is never touched.\n */\n function applyPragmas(params: { db: Database }): void {\n const { db } = params;\n // MANDATORY: sqlite ships with foreign-key enforcement OFF; every FK\n // semantic the ORM promises (RESTRICT errors, ON DELETE CASCADE) needs it.\n db.run(\"PRAGMA foreign_keys = ON\");\n if (ownsDatabase || busyTimeoutMs !== undefined) {\n db.run(`PRAGMA busy_timeout = ${busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS}`);\n }\n if (\n ownsDatabase &&\n options?.wal !== false &&\n options?.readonly !== true &&\n !isMemoryFilename({ filename })\n ) {\n db.run(\"PRAGMA journal_mode = WAL\");\n }\n }\n\n /** Open (or reuse) the connection lazily; sync because the driver is. */\n function getDatabase(): Database {\n if (database === null) {\n database = runMapped(\n () =>\n new Database(\n filename,\n options?.readonly === true\n ? { readonly: true, safeIntegers: true }\n : { create: true, readwrite: true, safeIntegers: true },\n ),\n );\n configured = false;\n }\n if (!configured) {\n runMapped(() => applyPragmas({ db: database as Database }));\n configured = true;\n }\n return database;\n }\n\n /**\n * ORM hot path: `db.query()` prepares through bun:sqlite's built-in\n * per-connection statement cache (pinned: same text ⇒ same Statement\n * object), so no adapter-side LRU is needed. Values bind verbatim — the\n * runtime codec table already encoded them (params.ts).\n */\n function runExecute(params: {\n db: Database;\n text: string;\n values: unknown[];\n }): Record<string, unknown>[] {\n return runMapped(() => {\n const statement = withSafeIntegers(params.db.query(params.text));\n const rows = statement.all(...asBindings(params.values)) as Record<string, unknown>[];\n return normalizeRows({ rows });\n });\n }\n\n /**\n * Raw path — uncached (`db.prepare`), finalized after use, raw-path\n * serialization for user-supplied values.\n *\n * With `values` present the text must be a SINGLE statement: bun:sqlite's\n * prepare SILENTLY DROPS everything after the first statement (pinned on\n * Bun 1.3.14), so multi-statement text with parameters would half-run.\n * Rows come from `all()` when the statement has result columns\n * (`columnNames`), with `affectedRows` = the row count (exact for\n * DML…RETURNING); otherwise from `run()`, whose driver-reported `changes`\n * is exact for UPDATE/DELETE/INSERT.\n *\n * With `values` absent the text runs as a multi-statement batch through\n * `db.run()` — the shape @vibeorm/migrate emits for DDL. The driver executes\n * every statement and reports cumulative changes; rows are always `[]` on\n * this path (the sync driver cannot both batch and read), so callers that\n * want rows from a single statement pass `values: []` — the runtime's\n * `$queryRawUnsafe` always does.\n */\n function runExecuteUnsafe(params: { db: Database; text: string; values?: unknown[] }): QueryResult {\n return runMapped(() => {\n const { db, text, values } = params;\n if (values === undefined) {\n const changes = db.run(text);\n return { rows: [], affectedRows: changes.changes };\n }\n const statement = withSafeIntegers(db.prepare(text));\n try {\n const bound = asBindings(serializeParamsRaw({ values }));\n if (statement.columnNames.length > 0) {\n const rows = statement.all(...bound) as Record<string, unknown>[];\n return { rows: normalizeRows({ rows }), affectedRows: rows.length };\n }\n const result = statement.run(...bound);\n return { rows: [], affectedRows: result.changes };\n } finally {\n statement.finalize();\n }\n });\n }\n\n /**\n * Adapter bound to an open transaction. It never re-enters the queue — the\n * enclosing `transaction()` call already owns the connection — and nested\n * `transaction()` calls become savepoints whose LIFETIMES are serialized by\n * the savepoint gate (F14): overlapping siblings would share one savepoint\n * stack, and the earlier one's rollback would erase the later one's work.\n */\n function createTransactionAdapter(params: {\n db: Database;\n scope: SavepointScope;\n budget: TransactionBudget;\n }): DatabaseAdapter {\n const { db, scope, budget } = params;\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n\n async execute(execParams) {\n const turn = scopeTurn({ scope });\n if (turn !== undefined) await turn;\n // After the turn, not before: waiting for a sibling savepoint spends\n // the same budget the statement is about to run against.\n budget.assertUsable({ stage: \"statement\" });\n return runExecute({ db, ...execParams });\n },\n\n async executeUnsafe(execParams) {\n const turn = scopeTurn({ scope });\n if (turn !== undefined) await turn;\n budget.assertUsable({ stage: \"statement\" });\n return runExecuteUnsafe({ db, ...execParams });\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n nestedOptions?: TransactionOptions,\n ): Promise<T> {\n refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });\n budget.assertUsable({ stage: \"nested\" });\n return runNestedSavepoint({\n scope,\n execute: (statement) => runMapped(() => db.run(statement)),\n // The child shares the ROOT budget by reference: a savepoint never\n // gets an independent clock or session setting.\n body: (child) => fn(createTransactionAdapter({ db, scope: child, budget })),\n });\n },\n\n async connect(): Promise<void> {\n // Already connected — this adapter is bound to an open transaction.\n },\n\n async disconnect(): Promise<void> {\n // The database outlives the transaction; closing here would be wrong.\n },\n\n formatArrayParam(values: unknown[]): unknown {\n throw arrayParamBug({ values });\n },\n };\n }\n\n /**\n * sqlite has no array parameters — the sqlite dialect declares\n * `scalarArrays: false` and expands `IN` lists to individual placeholders\n * (`inArrayStrategy: \"expand\"`), so this method being reached means an\n * upstream runtime bug routed an array here.\n */\n function arrayParamBug(params: { values: unknown[] }): VibeError {\n return internalError({\n message:\n \"formatArrayParam called on the sqlite adapter — sqlite has no array parameters; the dialect expands IN lists to individual placeholders, so this call is an upstream runtime bug\",\n meta: { provider: PROVIDER, valueCount: params.values.length },\n });\n }\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n\n async execute(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecute({ db: getDatabase(), ...params }));\n },\n\n async executeUnsafe(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecuteUnsafe({ db: getDatabase(), ...params }));\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n assertNotInsideTransaction();\n // Refuse unsupportable options BEFORE taking the queue or issuing BEGIN,\n // so a bad option fails fast and leaves no transaction open.\n validateTransactionOptions({ options: transactionOptions });\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n });\n\n return queue.run(async () => {\n const db = getDatabase();\n const scope = createSavepointScope();\n // The deadline clock starts here — after the queue turn, immediately\n // before BEGIN. Waiting for the connection is not charged to it.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n // Write-intent lock up front — see module doc on BEGIN IMMEDIATE.\n runMapped(() => db.run(\"BEGIN IMMEDIATE\"));\n try {\n const result = await txContext.run(true, () =>\n runRootScope({ scope, body: () => fn(createTransactionAdapter({ db, scope, budget })) }),\n );\n // An expired budget must never commit, however the callback ended.\n budget.assertUsable({ stage: \"commit\" });\n runMapped(() => db.run(\"COMMIT\"));\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n // Best effort: never mask the original failure (the user callback's\n // own error, or a failed COMMIT) with a rollback failure.\n try {\n db.run(\"ROLLBACK\");\n budget.close({ outcome: \"rolled-back\" });\n } catch {\n // The rollback that should have cleaned up failed: the commit state\n // is genuinely unknown, and saying \"rolled back\" would be a lie.\n budget.close({ outcome: \"unknown\" });\n if (budget.totalMs !== null && isTransactionDeadlineError(error)) {\n throw transactionOutcomeUnknownError({\n provider: PROVIDER,\n totalMs: budget.totalMs,\n cause: error,\n });\n }\n }\n throw error;\n }\n });\n },\n\n async connect(): Promise<void> {\n getDatabase();\n },\n\n /**\n * ONE ordered lifecycle (F15): the reference is cleared and the handle\n * closed INSIDE the queue, so work this adapter already accepted runs on\n * the database it was accepted against. Clearing `database` up front — as\n * this did — sent that queued work through `getDatabase()` with nothing in\n * hand, silently opening a FRESH (for `\":memory:\"`, empty) database while\n * the close went to the old handle.\n *\n * Refuses inside a transaction callback for the same reason the other entry\n * points do: the close would queue behind the transaction that is waiting\n * for it. Before this guard that call hung forever.\n */\n async disconnect(): Promise<void> {\n assertNotInsideTransaction();\n if (!ownsDatabase) return;\n await queue.run(async () => {\n if (database === null) return;\n const closing = database;\n database = null;\n configured = false;\n runMapped(() => closing.close());\n });\n },\n\n formatArrayParam(values: unknown[]): unknown {\n throw arrayParamBug({ values });\n },\n };\n}\n\n// ─── Re-exports (driver-error table, helpers) ─────────────────────\n\nexport {\n SQLITE_ERROR_CODES,\n SQLITE_ERROR_REASONS,\n mapSqliteDriverError,\n runMapped,\n sqliteErrorCode,\n} from \"./errors.ts\";\nexport { normalizeRow, normalizeRows, serializeParamsRaw } from \"./params.ts\";\nexport { createOperationQueue } from \"./queue.ts\";\nexport type { OperationQueue } from \"./queue.ts\";\nexport { createSavepointCounter, nextSavepointName } from \"@vibeorm/runtime/adapter-kit\";\nexport type { SavepointCounter } from \"@vibeorm/runtime/adapter-kit\";\nexport { refuseNestedTransactionOptions, validateTransactionOptions } from \"./transaction-options.ts\";\n// ─── EPIC 6: transaction budgets (shared, @vibeorm/runtime/adapter-kit) ───\nexport {\n DEFAULT_DEADLINE_CLOCK,\n engineStatementBudgetMs,\n isTransactionDeadlineError,\n startTransactionBudget,\n transactionClosedError,\n transactionDeadlineError,\n transactionOutcomeUnknownError,\n validateTransactionDeadline,\n} from \"@vibeorm/runtime/adapter-kit\";\nexport type { BudgetStage, DeadlineClock, TransactionBudget, TransactionOutcome } from \"@vibeorm/runtime/adapter-kit\";\n",
|
|
6
6
|
"/**\n * Driver-error mapping for `bun:sqlite`.\n *\n * bun:sqlite throws `SQLiteError` carrying the SQLite *extended* result-code\n * name in `code` (e.g. `\"SQLITE_CONSTRAINT_UNIQUE\"`) and the numeric extended\n * result code in `errno` (e.g. `2067`). Real shapes pinned against Bun 1.3.14:\n *\n * unique → code \"SQLITE_CONSTRAINT_UNIQUE\" errno 2067\n * pk → code \"SQLITE_CONSTRAINT_PRIMARYKEY\" errno 1555\n * notnull → code \"SQLITE_CONSTRAINT_NOTNULL\" errno 1299\n * check → code \"SQLITE_CONSTRAINT_CHECK\" errno 275\n * fk → code \"SQLITE_CONSTRAINT_FOREIGNKEY\" errno 787\n * busy → code \"SQLITE_BUSY\" errno 5\n *\n * Constitution rule 5: only `VibeError` escapes the adapter.\n */\n\nimport { VibeError, type VibeErrorCode } from \"@vibeorm/schema\";\n\n// ─── Result-code table ────────────────────────────────────────────\n\n/**\n * SQLite extended result-code name → stable VibeError code. Frozen and\n * mirrored by tests; anything absent from this table maps to `VIBE_ADAPTER`.\n */\nexport const SQLITE_ERROR_CODES: Readonly<Record<string, VibeErrorCode>> = Object.freeze({\n /** UNIQUE constraint failed on a declared unique index. */\n SQLITE_CONSTRAINT_UNIQUE: \"VIBE_UNIQUE_VIOLATION\",\n /** UNIQUE constraint failed on the primary key. */\n SQLITE_CONSTRAINT_PRIMARYKEY: \"VIBE_UNIQUE_VIOLATION\",\n /** rowid collision — the rowid alias flavour of a PK violation. */\n SQLITE_CONSTRAINT_ROWID: \"VIBE_UNIQUE_VIOLATION\",\n /** FOREIGN KEY constraint failed (requires `PRAGMA foreign_keys = ON`). */\n SQLITE_CONSTRAINT_FOREIGNKEY: \"VIBE_FK_VIOLATION\",\n /** NOT NULL constraint failed. */\n SQLITE_CONSTRAINT_NOTNULL: \"VIBE_VALIDATION\",\n /** CHECK constraint failed (enum emulation uses CHECK on sqlite). */\n SQLITE_CONSTRAINT_CHECK: \"VIBE_VALIDATION\",\n /** STRICT-table type mismatch. */\n SQLITE_CONSTRAINT_DATATYPE: \"VIBE_VALIDATION\",\n /** Another connection holds a conflicting lock. */\n SQLITE_BUSY: \"VIBE_TRANSACTION\",\n /** WAL snapshot invalidated by a concurrent writer. */\n SQLITE_BUSY_SNAPSHOT: \"VIBE_TRANSACTION\",\n /** Locked while another connection recovers the WAL. */\n SQLITE_BUSY_RECOVERY: \"VIBE_TRANSACTION\",\n /** busy_timeout elapsed while blocking (sqlite ≥3.41 setlk timeouts). */\n SQLITE_BUSY_TIMEOUT: \"VIBE_TRANSACTION\",\n /** Conflict on the same connection (e.g. dropping a table mid-read). */\n SQLITE_LOCKED: \"VIBE_TRANSACTION\",\n /** Shared-cache lock contention. */\n SQLITE_LOCKED_SHAREDCACHE: \"VIBE_TRANSACTION\",\n});\n\n/** Result codes that additionally explain themselves through `meta.reason`. */\nexport const SQLITE_ERROR_REASONS: Readonly<Record<string, string>> = Object.freeze({\n SQLITE_BUSY: \"busy\",\n SQLITE_BUSY_SNAPSHOT: \"busy\",\n SQLITE_BUSY_RECOVERY: \"busy\",\n SQLITE_BUSY_TIMEOUT: \"busy\",\n SQLITE_LOCKED: \"busy\",\n SQLITE_LOCKED_SHAREDCACHE: \"busy\",\n});\n\n// ─── Reading driver errors ────────────────────────────────────────\n\n/** bun:sqlite result-code names are SCREAMING_SNAKE with the SQLITE_ prefix. */\nconst RESULT_CODE_PATTERN = /^SQLITE_[A-Z0-9_]+$/;\n\nfunction readStringField(params: { source: Record<string, unknown>; key: string }): string | undefined {\n const value = params.source[params.key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/**\n * Extract the SQLite result-code name from a bun:sqlite error. Failures raised\n * outside the database engine (a closed handle, a bad filename) may carry no\n * `SQLITE_*` code and yield `undefined`.\n */\nexport function sqliteErrorCode(params: { error: unknown }): string | undefined {\n const { error } = params;\n if (error === null || typeof error !== \"object\") return undefined;\n const code = readStringField({ source: error as Record<string, unknown>, key: \"code\" });\n return code !== undefined && RESULT_CODE_PATTERN.test(code) ? code : undefined;\n}\n\n// ─── Mapping ──────────────────────────────────────────────────────\n\n/**\n * Map any error thrown by bun:sqlite onto a `VibeError`.\n *\n * A `VibeError` passes through unchanged, so an error raised deeper inside the\n * ORM (or by a nested adapter call) is never re-wrapped. Everything else keeps\n * the original error as `cause` and carries `{ code, errno }` in `meta`.\n */\nexport function mapSqliteDriverError(params: { error: unknown }): VibeError {\n const { error } = params;\n if (error instanceof VibeError) return error;\n\n const source: Record<string, unknown> =\n error !== null && typeof error === \"object\" ? (error as Record<string, unknown>) : {};\n const driverCode = sqliteErrorCode({ error });\n const code: VibeErrorCode =\n (driverCode === undefined ? undefined : SQLITE_ERROR_CODES[driverCode]) ?? \"VIBE_ADAPTER\";\n\n const meta: Record<string, unknown> = {};\n if (driverCode !== undefined) meta.code = driverCode;\n const errno = source.errno;\n if (typeof errno === \"number\") meta.errno = errno;\n const reason = driverCode === undefined ? undefined : SQLITE_ERROR_REASONS[driverCode];\n if (reason !== undefined) meta.reason = reason;\n\n const message =\n readStringField({ source, key: \"message\" }) ??\n (typeof error === \"string\" ? error : \"unknown bun:sqlite driver error\");\n\n return new VibeError({ code, message, meta, cause: error });\n}\n\n/**\n * Run a driver call, mapping anything it throws onto a `VibeError`. Synchronous\n * because bun:sqlite is a synchronous driver — the adapter's async surface\n * wraps results in already-resolved promises.\n */\nexport function runMapped<T>(fn: () => T): T {\n try {\n return fn();\n } catch (error) {\n throw mapSqliteDriverError({ error });\n }\n}\n",
|
|
7
7
|
"/**\n * Parameter serialization and row normalization for bun:sqlite.\n *\n * TWO SERIALIZATION PATHS (LEARNINGS.md — raw vs ORM differ):\n *\n * ORM hot path — NO conversion at all. The runtime codec table\n * (`SQLITE_CODECS` in @vibeorm/runtime) already encoded every parameter:\n * booleans are 0/1, Dates are ISO-8601 text, Json is stringified, BigInt is\n * decimal text. Converting again here would be exactly the double-coercion\n * v1's constitution rule 6 forbids, so `execute` binds values verbatim.\n *\n * Raw path (`$queryRaw*` / `$executeRaw*` / DDL) — user-supplied values that\n * never met a codec. bun:sqlite silently binds JS values it does not\n * understand as NULL (pinned on Bun 1.3.14: a `Date` and a plain object both\n * arrive as NULL), so {@link serializeParamsRaw} converts them explicitly\n * instead of letting data vanish.\n *\n * ROW NORMALIZATION: the adapter always reads INTEGER columns with\n * `safeIntegers` (see index.ts), so the driver hands back `bigint` for every\n * integer value. {@link normalizeRows} converts each bigint that fits the\n * IEEE-754 safe range back to `number`, and keeps the rest as `bigint`:\n *\n * - Int columns arrive as `number` (the Int codec passes numbers through),\n * - Boolean 0/1 arrive as `number` → decoded to boolean by the codec,\n * - BigInt columns ≤ 2^53−1 arrive as `number` → `BigInt(value)` (lossless),\n * - BigInt columns > 2^53−1 STAY `bigint` → codec passes them through — the\n * round-trip is lossless where plain number reads would silently corrupt.\n */\n\n// ─── Raw-path serialization ───────────────────────────────────────\n\n/**\n * Serialize raw-path parameters into values bun:sqlite binds faithfully:\n * `Date` → ISO-8601 text (matches the DateTime column encoding), `boolean` →\n * 1/0 (matches the Boolean column encoding), plain objects **and arrays** →\n * JSON text (sqlite has no array parameters — a JS array can only be a JSON\n * value here), `undefined` → NULL. Strings, numbers, bigints, NULL and binary\n * views pass through untouched.\n */\nexport function serializeParamsRaw(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value === undefined) return null;\n if (typeof value === \"boolean\") return value ? 1 : 0;\n if (value === null || typeof value !== \"object\") return value;\n if (value instanceof Date) return value.toISOString();\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;\n return JSON.stringify(value);\n });\n}\n\n// ─── Row normalization (safeIntegers → JS expectations) ───────────\n\nconst MAX_SAFE: bigint = BigInt(Number.MAX_SAFE_INTEGER);\nconst MIN_SAFE: bigint = -MAX_SAFE;\n\n/**\n * Downcast every lossless `bigint` in a row to `number`, in place. Values\n * outside ±(2^53−1) stay `bigint` so oversized BigInt columns round-trip\n * exactly (see the module doc for why this matches the runtime codec table).\n */\nexport function normalizeRow(params: { row: Record<string, unknown> }): Record<string, unknown> {\n const { row } = params;\n for (const key of Object.keys(row)) {\n const value = row[key];\n if (typeof value === \"bigint\" && value <= MAX_SAFE && value >= MIN_SAFE) {\n row[key] = Number(value);\n }\n }\n return row;\n}\n\n/** {@link normalizeRow} over a result set (mutates and returns the same array). */\nexport function normalizeRows(params: { rows: Record<string, unknown>[] }): Record<string, unknown>[] {\n for (const row of params.rows) normalizeRow({ row });\n return params.rows;\n}\n",
|
|
8
8
|
"/**\n * Operation queue for bun:sqlite.\n *\n * The driver itself is synchronous — a single statement can never interleave —\n * but a `transaction()` callback is async: while it awaits, a concurrent root\n * adapter call would run on the SAME connection and silently join the open\n * transaction. Every adapter entry point therefore runs through one serial\n * chain — including whole `transaction()` calls, which occupy the chain until\n * they commit or roll back (mirroring adapter-pglite).\n */\n\n/** A serial chain: jobs run one at a time, in the order they were queued. */\nexport type OperationQueue = {\n /** Queue `job`, resolving with its result once every earlier job has settled. */\n run<T>(job: () => Promise<T>): Promise<T>;\n};\n\n/** Create an empty operation queue. */\nexport function createOperationQueue(): OperationQueue {\n let tail: Promise<unknown> = Promise.resolve();\n\n return {\n run<T>(job: () => Promise<T>): Promise<T> {\n // Run after the previous job settles, whether it resolved or rejected.\n const result = tail.then(job, job);\n // Keep the chain alive: a rejected job must not reject every later job.\n tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n },\n };\n}\n",
|
|
9
|
-
"/**\n * Savepoint lifetimes inside one top-level transaction (F14).\n *\n * A savepoint stack lives on ONE connection: `ROLLBACK TO SAVEPOINT s` undoes\n * everything done after `s`, including work a LATER savepoint already released.\n * Two `transaction()` callbacks opened from the same handle and overlapping in\n * time therefore share that one stack, and the earlier one's rollback silently\n * erases the later one's already committed work. Unique savepoint names do not\n * help — the names never collided, the lifetimes did.\n *\n * The rule enforced here, on every handle of a transaction:\n *\n * > A handle runs when nothing deeper than itself is open. Otherwise it waits\n * > for the innermost open savepoint to settle — unless the call is being made\n * > from INSIDE that savepoint's own callback, which is genuine nesting and\n * > proceeds immediately.\n *\n * The \"from inside\" test is an {@link AsyncLocalStorage} lookup, so the common\n * nesting shapes never wait on themselves: a child opened from its parent's\n * callback, and the parent handle used from within an open child's callback,\n * both proceed. Only genuinely concurrent siblings serialize.\n *\n * Not covered: a callback that awaits a sibling it did not start. That\n * arrangement asks for two savepoints to be open at once, which is exactly what\n * loses work; it now blocks instead. Deliberate — see the notes for this fix.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { type SavepointCounter, createSavepointCounter, nextSavepointName } from \"./savepoints.ts\";\n\n// ─── Types ────────────────────────────────────────────────────────\n\n/**\n * One handle's position in the savepoint stack of a top-level transaction.\n * `state` is shared by reference across every handle of that transaction.\n */\nexport type SavepointScope = {\n /** Physical savepoint depth; 0 is the top-level transaction itself. */\n readonly depth: number;\n /** Shared state: name allocation plus the innermost open savepoint. */\n readonly state: SavepointState;\n};\n\n/** Mutable state shared by every scope of one top-level transaction. */\nexport type SavepointState = {\n /** Savepoint name counter (shared, so names never collide). */\n readonly counter: SavepointCounter;\n /** The innermost savepoint currently open, or `null` when none is. */\n open: OpenSavepoint | null;\n};\n\n/** An open savepoint: its handle, its name, and when it finished. */\ntype OpenSavepoint = {\n readonly scope: SavepointScope;\n readonly name: string;\n readonly parent: OpenSavepoint | null;\n readonly settled: Promise<void>;\n readonly settle: () => void;\n};\n\n/**\n * The savepoint whose callback the current async context is running inside.\n * This is the discriminator between genuine nesting (proceed) and an\n * overlapping sibling (wait).\n */\nconst CURRENT = new AsyncLocalStorage<SavepointScope>();\n\n// ─── Public API ───────────────────────────────────────────────────\n\n/** Fresh scope for one top-level transaction: depth 0, nothing open. */\nexport function createSavepointScope(): SavepointScope {\n return { depth: 0, state: { counter: createSavepointCounter(), open: null } };\n}\n\n/**\n * Run a top-level transaction callback inside its scope's async context, so\n * nested calls made from it can be told apart from calls made inside an open\n * child.\n */\nexport function runRootScope<T>(params: { scope: SavepointScope; body: () => Promise<T> }): Promise<T> {\n return CURRENT.run(params.scope, params.body);\n}\n\n/**\n * The wait a statement on `scope` owes before it may run, or `undefined` when\n * it may run now. Returning `undefined` keeps the uncontended path — nothing\n * deeper open — free of an extra promise and microtask.\n */\nexport function scopeTurn(params: { scope: SavepointScope }): Promise<void> | undefined {\n const open = params.scope.state.open;\n if (mayProceed({ scope: params.scope, open })) return undefined;\n return waitForTurn({ scope: params.scope });\n}\n\n/**\n * Open a nested savepoint under `scope`, run `body` inside it and release it —\n * rolling back to it if `body` throws, exactly as before this gate existed.\n *\n * `execute` runs the savepoint control SQL (already wrapped in the adapter's\n * own driver-error mapping). Omit it when the driver manages the savepoint\n * itself (bun:sql's `savepoint()`), in which case only the lifetime is gated.\n */\nexport async function runNestedSavepoint<T>(params: {\n readonly scope: SavepointScope;\n readonly execute?: (statement: string) => Promise<unknown> | unknown;\n readonly body: (scope: SavepointScope) => Promise<T>;\n}): Promise<T> {\n const { scope, execute, body } = params;\n const node = await acquire({ scope });\n try {\n if (execute !== undefined) await execute(`SAVEPOINT ${node.name}`);\n let result: T;\n try {\n result = await CURRENT.run(node.scope, () => body(node.scope));\n } catch (error) {\n // A descendant the callback started but never awaited must finish before\n // this savepoint may be rolled back — releasing out of order corrupts\n // the stack.\n await drainDescendants({ node });\n if (execute !== undefined) {\n // Best effort: never mask the original failure with a rollback failure.\n try {\n await execute(`ROLLBACK TO SAVEPOINT ${node.name}`);\n } catch {\n /* rollback is best-effort */\n }\n }\n throw error;\n }\n await drainDescendants({ node });\n if (execute !== undefined) await execute(`RELEASE SAVEPOINT ${node.name}`);\n return result;\n } finally {\n closeNode({ node });\n }\n}\n\n// ─── Internals ────────────────────────────────────────────────────\n\n/** Whether `scope` may act while `open` is the innermost open savepoint. */\nfunction mayProceed(params: { scope: SavepointScope; open: OpenSavepoint | null }): boolean {\n const { scope, open } = params;\n if (open === null) return true;\n // This handle IS the innermost open savepoint.\n if (open.scope === scope) return true;\n // This handle's own savepoint has already closed (a retained child handle):\n // it can only act at the connection's current depth, and waiting for a\n // shallower savepoint it does not own would never resolve.\n if (open.scope.depth < scope.depth) return true;\n // Genuine nesting: the call is being made from inside the open callback.\n return CURRENT.getStore() === open.scope;\n}\n\nasync function waitForTurn(params: { scope: SavepointScope }): Promise<void> {\n for (;;) {\n const open = params.scope.state.open;\n if (open === null || mayProceed({ scope: params.scope, open })) return;\n await open.settled;\n }\n}\n\n/**\n * Wait for this handle's turn and take it in the SAME synchronous step — the\n * check and the push must not be separated by an await, or two waiters woken\n * together would both open a savepoint.\n */\nasync function acquire(params: { scope: SavepointScope }): Promise<OpenSavepoint> {\n const { scope } = params;\n for (;;) {\n const open = scope.state.open;\n if (mayProceed({ scope, open })) return pushNode({ scope, parent: open });\n // `mayProceed` returns true for a null `open`, so this is never null here.\n await (open as OpenSavepoint).settled;\n }\n}\n\nfunction pushNode(params: { scope: SavepointScope; parent: OpenSavepoint | null }): OpenSavepoint {\n const { scope, parent } = params;\n const state = scope.state;\n let settle: () => void = () => undefined;\n const settled = new Promise<void>((resolve) => {\n settle = () => resolve();\n });\n // Physical depth follows the connection's stack, not the handle that opened\n // it: a savepoint opened from an outer handle inside a child's callback still\n // nests inside that child.\n const node: OpenSavepoint = {\n scope: { depth: (parent === null ? 0 : parent.scope.depth) + 1, state },\n name: nextSavepointName({ counter: state.counter }),\n parent,\n settled,\n settle,\n };\n state.open = node;\n return node;\n}\n\nfunction closeNode(params: { node: OpenSavepoint }): void {\n const { node } = params;\n const state = node.scope.state;\n if (state.open === node) state.open = node.parent;\n node.settle();\n}\n\n/** Wait out savepoints a callback opened below this node and never awaited. */\nasync function drainDescendants(params: { node: OpenSavepoint }): Promise<void> {\n const state = params.node.scope.state;\n for (;;) {\n const open = state.open;\n if (open === null || open === params.node || open.scope.depth <= params.node.scope.depth) return;\n await open.settled;\n }\n}\n",
|
|
10
|
-
"/**\n * Savepoint naming for nested transactions.\n *\n * v1 lesson (LEARNINGS.md): the counter is shared **by reference** across every\n * adapter created for one top-level transaction, so sibling and deeply nested\n * savepoints can never collide on a name.\n */\n\n/** Mutable counter shared by reference across one top-level transaction. */\nexport type SavepointCounter = { n: number };\n\n/** Fresh counter — one per top-level transaction. */\nexport function createSavepointCounter(): SavepointCounter {\n return { n: 0 };\n}\n\n/**\n * Allocate the next savepoint name (`vibeorm_sp_0`, `vibeorm_sp_1`, …) and\n * advance the shared counter. The name is an identifier by construction, so it\n * is safe to interpolate into `SAVEPOINT` / `RELEASE` / `ROLLBACK TO` SQL.\n */\nexport function nextSavepointName(params: { counter: SavepointCounter }): string {\n return `vibeorm_sp_${params.counter.n++}`;\n}\n",
|
|
11
|
-
"/**\n * Transaction budgets — the total-deadline clock and the handle-closure gate.\n *\n * BYTE-IDENTICAL in all six adapter packages, exactly like `savepoint-gate.ts`:\n * adapters depend on `@vibeorm/runtime` for TYPES only, so a shared value\n * module there would invert the dependency direction, and `@vibeorm/schema` is\n * the IR package, not a home for a transaction clock. Keep the six copies in\n * lockstep — a divergence here is a silent per-engine behaviour difference.\n *\n * WHAT A DEADLINE IS, AND IS NOT\n *\n * `TransactionOptions.timeout` is unchanged: a PER-STATEMENT engine-side bound.\n * `TransactionOptions.deadline` is new and different — a TOTAL wall-clock bound\n * on one top-level transaction. Its clock starts immediately before `BEGIN` is\n * sent, so connection acquisition (which has its own budget on the pooled\n * adapters) is deliberately NOT counted.\n *\n * Enforcement is never `Promise.race` over a statement that keeps running:\n *\n * - `\"between-statements\"` (the default, and all six adapters can deliver it):\n * before every statement, every nested `transaction()` and the `COMMIT`, an\n * expired budget refuses. The statement is never sent and the transaction is\n * rolled back. A statement ALREADY IN FLIGHT is not interrupted.\n * - `\"cancel-running-statements\"` (postgres servers only): additionally hands\n * the engine the remaining budget as `statement_timeout`, so the SERVER\n * cancels an over-long statement. An adapter that cannot do this refuses the\n * request outright rather than quietly delivering the weaker level.\n *\n * JavaScript cannot forcibly stop a running callback. A callback that keeps\n * going past the deadline finds its later database calls refused; work it does\n * outside the database (an HTTP call, an SMS) is not stopped and cannot be.\n * There is no automatic retry: a deadline failure is terminal for that\n * transaction, because external side effects may already have happened.\n */\n\nimport type {\n TransactionDeadlineEnforcement,\n TransactionDeadlineSupport,\n TransactionOptions,\n} from \"@vibeorm/runtime\";\nimport { VibeError } from \"@vibeorm/schema\";\n\n// ─── Types ────────────────────────────────────────────────────────\n\n/** Where a budget check happened, reported as `meta.stage`. */\nexport type BudgetStage = \"statement\" | \"nested\" | \"commit\";\n\n/**\n * What is known about a finished transaction. `\"unknown\"` is a first-class\n * outcome, not a failure to decide: when a rollback itself fails the commit\n * state genuinely cannot be asserted, and claiming \"rolled back\" would be a lie.\n */\nexport type TransactionOutcome = \"committed\" | \"rolled-back\" | \"unknown\";\n\n/** Wall clock, injectable so tests are deterministic instead of sleep-timed. */\nexport type DeadlineClock = () => number;\n\n/** The ambient clock, used whenever no seam is injected. */\nexport const DEFAULT_DEADLINE_CLOCK: DeadlineClock = (): number => Date.now();\n\n/**\n * One top-level transaction's budget. Nested savepoint handles share this\n * object BY REFERENCE — they never get an independent clock, connection or\n * session setting.\n */\nexport type TransactionBudget = {\n /** The enforcement level in force, or `null` when no deadline was requested. */\n readonly enforcement: TransactionDeadlineEnforcement | null;\n /** The requested total budget in milliseconds, or `null` when none was. */\n readonly totalMs: number | null;\n /** Existing per-statement limit, independent of the total deadline. Zero disables it. */\n readonly statementTimeoutMs: number | null;\n /** Milliseconds left (never negative), or `null` when no deadline is set. */\n remainingMs(): number | null;\n /** True only when a deadline is set and has passed. */\n expired(): boolean;\n /** Refuse a closed handle or an expired budget. Called BEFORE anything is sent. */\n assertUsable(params: { stage: BudgetStage }): void;\n /** Record the terminal outcome; the first call wins and later ones are ignored. */\n close(params: { outcome: TransactionOutcome }): void;\n /** The recorded outcome, or `null` while the transaction is still open. */\n readonly outcome: TransactionOutcome | null;\n};\n\n// ─── Validation (before BEGIN, before any write) ──────────────────\n\n/**\n * Check `options.deadline` against what this adapter can honestly deliver.\n * Called BEFORE `BEGIN` — a budget this engine cannot honour must never leave\n * a transaction open behind it.\n *\n * @throws VibeError `VIBE_VALIDATION` when `totalMs` is not a positive integer.\n * @throws VibeError `VIBE_UNSUPPORTED_CAPABILITY` when the requested\n * `enforcement` is stronger than `support` — refused, never silently degraded.\n */\nexport function validateTransactionDeadline(params: {\n options?: TransactionOptions;\n support: TransactionDeadlineSupport;\n provider: string;\n}): void {\n const { options, support, provider } = params;\n const deadline = options?.deadline;\n if (deadline === undefined) return;\n\n if (support === \"unsupported\") {\n throw new VibeError({\n code: \"VIBE_UNSUPPORTED_CAPABILITY\",\n message: `${provider} cannot enforce a transaction deadline — remove the \\`deadline\\` option`,\n meta: { provider, feature: \"transactionDeadline\", supported: support },\n });\n }\n\n if (!Number.isInteger(deadline.totalMs) || deadline.totalMs <= 0) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `transaction deadline \\`totalMs\\` must be a positive integer of milliseconds, received ${String(deadline.totalMs)}`,\n meta: { provider, feature: \"transactionDeadline\", totalMs: deadline.totalMs },\n });\n }\n\n const requested: TransactionDeadlineEnforcement = deadline.enforcement ?? \"between-statements\";\n if (requested === \"cancel-running-statements\" && support !== \"cancel-running-statements\") {\n throw new VibeError({\n code: \"VIBE_UNSUPPORTED_CAPABILITY\",\n message:\n `${provider} cannot cancel a statement that is already running, so the requested ` +\n \"`cancel-running-statements` deadline guarantee is refused rather than silently downgraded — \" +\n 'ask for \"between-statements\" if a budget checked between statements is enough',\n meta: { provider, feature: \"transactionDeadline\", requested, supported: support },\n });\n }\n}\n\n// ─── The budget ───────────────────────────────────────────────────\n\n/**\n * Start the clock for one top-level transaction. Call it immediately before\n * `BEGIN`; {@link validateTransactionDeadline} must already have run.\n */\nexport function startTransactionBudget(params: {\n options?: TransactionOptions;\n provider: string;\n clock?: DeadlineClock;\n}): TransactionBudget {\n const { options, provider } = params;\n const clock = params.clock ?? DEFAULT_DEADLINE_CLOCK;\n const deadline = options?.deadline;\n const totalMs = deadline === undefined ? null : deadline.totalMs;\n const enforcement: TransactionDeadlineEnforcement | null =\n deadline === undefined ? null : (deadline.enforcement ?? \"between-statements\");\n const expiresAt = totalMs === null ? null : clock() + totalMs;\n let outcome: TransactionOutcome | null = null;\n\n const budget: TransactionBudget = {\n enforcement,\n totalMs,\n statementTimeoutMs: options?.timeout ?? null,\n remainingMs(): number | null {\n return expiresAt === null ? null : Math.max(0, expiresAt - clock());\n },\n expired(): boolean {\n return expiresAt !== null && clock() >= expiresAt;\n },\n assertUsable(assertParams: { stage: BudgetStage }): void {\n if (outcome !== null) {\n throw transactionClosedError({ provider, outcome, stage: assertParams.stage });\n }\n if (expiresAt !== null && clock() >= expiresAt) {\n throw transactionDeadlineError({\n provider,\n stage: assertParams.stage,\n totalMs: totalMs as number,\n overdueMs: clock() - expiresAt,\n });\n }\n },\n close(closeParams: { outcome: TransactionOutcome }): void {\n if (outcome === null) outcome = closeParams.outcome;\n },\n get outcome(): TransactionOutcome | null {\n return outcome;\n },\n };\n return budget;\n}\n\n/**\n * The engine-side bound for the NEXT statement, or `null` when the caller did\n * not buy `\"cancel-running-statements\"`. Floored at 1 ms: on postgres `0` means\n * \"no limit\", so a spent budget must never be handed over as a zero.\n */\nexport function engineStatementBudgetMs(params: { budget: TransactionBudget }): number | null {\n const { budget } = params;\n if (budget.enforcement !== \"cancel-running-statements\") return null;\n const remaining = budget.remainingMs();\n if (remaining === null) return null;\n const totalBound = Math.max(1, Math.ceil(remaining));\n return budget.statementTimeoutMs !== null && budget.statementTimeoutMs > 0\n ? Math.min(totalBound, budget.statementTimeoutMs) : totalBound;\n}\n\n// ─── Errors ───────────────────────────────────────────────────────\n\n/**\n * The refusal an expired budget raises. `VIBE_TRANSACTION` with a stable\n * `meta.reason` — no SQL text, no parameter values, no credentials.\n */\nexport function transactionDeadlineError(params: {\n provider: string;\n stage: BudgetStage;\n totalMs: number;\n overdueMs: number;\n}): VibeError {\n const { provider, stage, totalMs, overdueMs } = params;\n const what =\n stage === \"commit\"\n ? \"the transaction was rolled back instead of committed\"\n : \"the statement was never sent and the transaction is being rolled back\";\n return new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n `transaction deadline of ${totalMs} ms expired ${Math.max(0, Math.round(overdueMs))} ms ago — ${what}. ` +\n \"Nothing is retried automatically: work the callback did outside the database may already have happened.\",\n meta: { provider, reason: \"transaction-deadline\", stage, totalMs, overdueMs: Math.max(0, Math.round(overdueMs)) },\n });\n}\n\n/**\n * The refusal an ESCAPED transaction handle raises — a transactional adapter\n * kept past the end of its transaction. Without this it would run on a\n * connection that is back in the pool, outside any transaction.\n */\nexport function transactionClosedError(params: {\n provider: string;\n outcome: TransactionOutcome;\n stage: BudgetStage;\n}): VibeError {\n const { provider, outcome, stage } = params;\n return new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n `this transaction has already finished (${outcome}), so its client can no longer be used — ` +\n \"a handle kept past the end of `$transaction` would run outside the transaction, on a connection \" +\n \"that now belongs to someone else\",\n meta: { provider, reason: \"transaction-closed\", outcome, stage },\n });\n}\n\n/**\n * The stable typed outcome for \"the deadline expired and the rollback that was\n * supposed to clean up failed too\". Promising a rollback here would be false;\n * promising a retry would be worse.\n *\n * `cause` is the CAUSAL error (the deadline refusal), so the reason the\n * transaction was being abandoned survives. The rollback failure itself is\n * reported only as `meta.rollbackFailed`: a raw driver failure can carry\n * statement text, and this error is meant to be logged.\n */\nexport function transactionOutcomeUnknownError(params: {\n provider: string;\n totalMs: number;\n cause: unknown;\n}): VibeError {\n const { provider, totalMs, cause } = params;\n return new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n `transaction deadline of ${totalMs} ms expired and the rollback that should have cleaned up failed — ` +\n \"the commit state of this transaction is UNKNOWN. It is not retried automatically; inspect the data \" +\n \"before deciding.\",\n meta: {\n provider,\n reason: \"transaction-outcome-unknown\",\n outcome: \"unknown\",\n totalMs,\n rollbackFailed: true,\n },\n cause,\n });\n}\n\n/** Whether `error` is this module's deadline refusal (used to pick the cleanup path). */\nexport function isTransactionDeadlineError(error: unknown): boolean {\n const reason = (error as { meta?: { reason?: unknown } } | null)?.meta?.reason;\n return reason === \"transaction-deadline\";\n}\n",
|
|
12
9
|
"/**\n * Transaction-option validation for sqlite — dialect honesty (constitution\n * rule 4): what sqlite cannot honor is refused loudly at the earliest knowable\n * moment, before `BEGIN` is ever issued, instead of being silently ignored.\n *\n * - SQLite transactions are always serializable. `Serializable` (and omitting\n * the option) is accepted; `ReadCommitted` / `RepeatableRead` would be a\n * silent upgrade the caller did not ask for, so they throw `VIBE_VALIDATION`.\n * - SQLite has no per-statement timeout. `busy_timeout` only bounds LOCK\n * ACQUISITION, not statement runtime, so it is NOT an honest implementation\n * of the contract's transaction-scoped `timeout` — the option throws\n * `VIBE_VALIDATION`.\n * - NESTED transactions are savepoints: no adapter can change the isolation\n * level mid-transaction or enforce a nested timeout, so nested options are\n * refused with `VIBE_VALIDATION` on EVERY adapter (the shared contract in\n * @vibeorm/runtime's adapter.ts).\n */\n\nimport type { TransactionOptions } from \"@vibeorm/runtime\";\nimport { VibeError } from \"@vibeorm/schema\";\n\n/**\n * Validate `$transaction` options against what sqlite can actually honor.\n *\n * @throws VibeError `VIBE_VALIDATION` for `ReadCommitted` / `RepeatableRead`\n * isolation or any `timeout` (see module doc).\n */\nexport function validateTransactionOptions(params: { options?: TransactionOptions }): void {\n const { options } = params;\n if (options === undefined) return;\n\n if (options.isolationLevel !== undefined && options.isolationLevel !== \"Serializable\") {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `sqlite transactions are always serializable — isolationLevel \"${options.isolationLevel}\" cannot be honored; pass \"Serializable\" or omit the option`,\n meta: { dialect: \"sqlite\", isolationLevel: options.isolationLevel },\n });\n }\n\n if (options.timeout !== undefined) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message:\n \"sqlite has no per-statement timeout, so a transaction `timeout` cannot be enforced database-side (busy_timeout only bounds lock acquisition, not statement runtime) — remove the option\",\n meta: { dialect: \"sqlite\", timeout: options.timeout },\n });\n }\n}\n\n/**\n * Refuse options on a NESTED `transaction()` call — the same contract on every\n * adapter (see the `DatabaseAdapter.transaction` JSDoc in @vibeorm/runtime):\n * a nested transaction is a SAVEPOINT, and a savepoint can neither change the\n * isolation level of the transaction it joins nor enforce its own timeout.\n *\n * @throws VibeError `VIBE_VALIDATION` when `isolationLevel` or `timeout` is\n * present (an `undefined` or empty options object is accepted).\n */\nexport function refuseNestedTransactionOptions(params: {\n options?: TransactionOptions;\n provider: string;\n}): void {\n const { options, provider } = params;\n if (options === undefined) return;\n\n const passed: string[] = [];\n if (options.isolationLevel !== undefined) passed.push(\"isolationLevel\");\n if (options.timeout !== undefined) passed.push(\"timeout\");\n if (options.deadline !== undefined) passed.push(\"deadline\");\n if (passed.length === 0) return;\n\n const meta: Record<string, unknown> = { provider, nested: true };\n if (options.isolationLevel !== undefined) meta.isolationLevel = options.isolationLevel;\n if (options.timeout !== undefined) meta.timeout = options.timeout;\n if (options.deadline !== undefined) meta.deadline = options.deadline;\n\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `nested transactions run as savepoints and cannot honor ${passed.join(\" or \")} — a savepoint cannot change the isolation level of the transaction it joins, a nested timeout is not enforceable, and a nested deadline would need its own session setting on a connection it does not own (it inherits the top-level one instead); pass options on the top-level $transaction`,\n meta,\n });\n}\n"
|
|
13
10
|
],
|
|
14
|
-
"mappings": "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",
|
|
15
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "B88E19439473D31164756E2164756E21",
|
|
16
13
|
"names": []
|
|
17
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeorm/adapter-sqlite",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "bun:sqlite adapter for VibeORM v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"orm",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"bun": ">=1.2.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@vibeorm/runtime": "2.0.
|
|
44
|
+
"@vibeorm/runtime": "2.0.2",
|
|
45
45
|
"@vibeorm/schema": "2.0.0"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
package/dist/savepoint-gate.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Savepoint lifetimes inside one top-level transaction (F14).
|
|
3
|
-
*
|
|
4
|
-
* A savepoint stack lives on ONE connection: `ROLLBACK TO SAVEPOINT s` undoes
|
|
5
|
-
* everything done after `s`, including work a LATER savepoint already released.
|
|
6
|
-
* Two `transaction()` callbacks opened from the same handle and overlapping in
|
|
7
|
-
* time therefore share that one stack, and the earlier one's rollback silently
|
|
8
|
-
* erases the later one's already committed work. Unique savepoint names do not
|
|
9
|
-
* help — the names never collided, the lifetimes did.
|
|
10
|
-
*
|
|
11
|
-
* The rule enforced here, on every handle of a transaction:
|
|
12
|
-
*
|
|
13
|
-
* > A handle runs when nothing deeper than itself is open. Otherwise it waits
|
|
14
|
-
* > for the innermost open savepoint to settle — unless the call is being made
|
|
15
|
-
* > from INSIDE that savepoint's own callback, which is genuine nesting and
|
|
16
|
-
* > proceeds immediately.
|
|
17
|
-
*
|
|
18
|
-
* The "from inside" test is an {@link AsyncLocalStorage} lookup, so the common
|
|
19
|
-
* nesting shapes never wait on themselves: a child opened from its parent's
|
|
20
|
-
* callback, and the parent handle used from within an open child's callback,
|
|
21
|
-
* both proceed. Only genuinely concurrent siblings serialize.
|
|
22
|
-
*
|
|
23
|
-
* Not covered: a callback that awaits a sibling it did not start. That
|
|
24
|
-
* arrangement asks for two savepoints to be open at once, which is exactly what
|
|
25
|
-
* loses work; it now blocks instead. Deliberate — see the notes for this fix.
|
|
26
|
-
*/
|
|
27
|
-
import { type SavepointCounter } from "./savepoints.ts";
|
|
28
|
-
/**
|
|
29
|
-
* One handle's position in the savepoint stack of a top-level transaction.
|
|
30
|
-
* `state` is shared by reference across every handle of that transaction.
|
|
31
|
-
*/
|
|
32
|
-
export type SavepointScope = {
|
|
33
|
-
/** Physical savepoint depth; 0 is the top-level transaction itself. */
|
|
34
|
-
readonly depth: number;
|
|
35
|
-
/** Shared state: name allocation plus the innermost open savepoint. */
|
|
36
|
-
readonly state: SavepointState;
|
|
37
|
-
};
|
|
38
|
-
/** Mutable state shared by every scope of one top-level transaction. */
|
|
39
|
-
export type SavepointState = {
|
|
40
|
-
/** Savepoint name counter (shared, so names never collide). */
|
|
41
|
-
readonly counter: SavepointCounter;
|
|
42
|
-
/** The innermost savepoint currently open, or `null` when none is. */
|
|
43
|
-
open: OpenSavepoint | null;
|
|
44
|
-
};
|
|
45
|
-
/** An open savepoint: its handle, its name, and when it finished. */
|
|
46
|
-
type OpenSavepoint = {
|
|
47
|
-
readonly scope: SavepointScope;
|
|
48
|
-
readonly name: string;
|
|
49
|
-
readonly parent: OpenSavepoint | null;
|
|
50
|
-
readonly settled: Promise<void>;
|
|
51
|
-
readonly settle: () => void;
|
|
52
|
-
};
|
|
53
|
-
/** Fresh scope for one top-level transaction: depth 0, nothing open. */
|
|
54
|
-
export declare function createSavepointScope(): SavepointScope;
|
|
55
|
-
/**
|
|
56
|
-
* Run a top-level transaction callback inside its scope's async context, so
|
|
57
|
-
* nested calls made from it can be told apart from calls made inside an open
|
|
58
|
-
* child.
|
|
59
|
-
*/
|
|
60
|
-
export declare function runRootScope<T>(params: {
|
|
61
|
-
scope: SavepointScope;
|
|
62
|
-
body: () => Promise<T>;
|
|
63
|
-
}): Promise<T>;
|
|
64
|
-
/**
|
|
65
|
-
* The wait a statement on `scope` owes before it may run, or `undefined` when
|
|
66
|
-
* it may run now. Returning `undefined` keeps the uncontended path — nothing
|
|
67
|
-
* deeper open — free of an extra promise and microtask.
|
|
68
|
-
*/
|
|
69
|
-
export declare function scopeTurn(params: {
|
|
70
|
-
scope: SavepointScope;
|
|
71
|
-
}): Promise<void> | undefined;
|
|
72
|
-
/**
|
|
73
|
-
* Open a nested savepoint under `scope`, run `body` inside it and release it —
|
|
74
|
-
* rolling back to it if `body` throws, exactly as before this gate existed.
|
|
75
|
-
*
|
|
76
|
-
* `execute` runs the savepoint control SQL (already wrapped in the adapter's
|
|
77
|
-
* own driver-error mapping). Omit it when the driver manages the savepoint
|
|
78
|
-
* itself (bun:sql's `savepoint()`), in which case only the lifetime is gated.
|
|
79
|
-
*/
|
|
80
|
-
export declare function runNestedSavepoint<T>(params: {
|
|
81
|
-
readonly scope: SavepointScope;
|
|
82
|
-
readonly execute?: (statement: string) => Promise<unknown> | unknown;
|
|
83
|
-
readonly body: (scope: SavepointScope) => Promise<T>;
|
|
84
|
-
}): Promise<T>;
|
|
85
|
-
export {};
|
|
86
|
-
//# sourceMappingURL=savepoint-gate.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"savepoint-gate.d.ts","sourceRoot":"","sources":["../src/savepoint-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,KAAK,gBAAgB,EAA6C,MAAM,iBAAiB,CAAC;AAInG;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,cAAc,GAAG;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,sEAAsE;IACtE,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,qEAAqE;AACrE,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC;AAWF,wEAAwE;AACxE,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE;IAAE,KAAK,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAErG;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAItF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE;IAClD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACtD,GAAG,OAAO,CAAC,CAAC,CAAC,CA6Bb"}
|
package/dist/savepoints.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Savepoint naming for nested transactions.
|
|
3
|
-
*
|
|
4
|
-
* v1 lesson (LEARNINGS.md): the counter is shared **by reference** across every
|
|
5
|
-
* adapter created for one top-level transaction, so sibling and deeply nested
|
|
6
|
-
* savepoints can never collide on a name.
|
|
7
|
-
*/
|
|
8
|
-
/** Mutable counter shared by reference across one top-level transaction. */
|
|
9
|
-
export type SavepointCounter = {
|
|
10
|
-
n: number;
|
|
11
|
-
};
|
|
12
|
-
/** Fresh counter — one per top-level transaction. */
|
|
13
|
-
export declare function createSavepointCounter(): SavepointCounter;
|
|
14
|
-
/**
|
|
15
|
-
* Allocate the next savepoint name (`vibeorm_sp_0`, `vibeorm_sp_1`, …) and
|
|
16
|
-
* advance the shared counter. The name is an identifier by construction, so it
|
|
17
|
-
* is safe to interpolate into `SAVEPOINT` / `RELEASE` / `ROLLBACK TO` SQL.
|
|
18
|
-
*/
|
|
19
|
-
export declare function nextSavepointName(params: {
|
|
20
|
-
counter: SavepointCounter;
|
|
21
|
-
}): string;
|
|
22
|
-
//# sourceMappingURL=savepoints.d.ts.map
|
package/dist/savepoints.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"savepoints.d.ts","sourceRoot":"","sources":["../src/savepoints.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,qDAAqD;AACrD,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,gBAAgB,CAAA;CAAE,GAAG,MAAM,CAE/E"}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction budgets — the total-deadline clock and the handle-closure gate.
|
|
3
|
-
*
|
|
4
|
-
* BYTE-IDENTICAL in all six adapter packages, exactly like `savepoint-gate.ts`:
|
|
5
|
-
* adapters depend on `@vibeorm/runtime` for TYPES only, so a shared value
|
|
6
|
-
* module there would invert the dependency direction, and `@vibeorm/schema` is
|
|
7
|
-
* the IR package, not a home for a transaction clock. Keep the six copies in
|
|
8
|
-
* lockstep — a divergence here is a silent per-engine behaviour difference.
|
|
9
|
-
*
|
|
10
|
-
* WHAT A DEADLINE IS, AND IS NOT
|
|
11
|
-
*
|
|
12
|
-
* `TransactionOptions.timeout` is unchanged: a PER-STATEMENT engine-side bound.
|
|
13
|
-
* `TransactionOptions.deadline` is new and different — a TOTAL wall-clock bound
|
|
14
|
-
* on one top-level transaction. Its clock starts immediately before `BEGIN` is
|
|
15
|
-
* sent, so connection acquisition (which has its own budget on the pooled
|
|
16
|
-
* adapters) is deliberately NOT counted.
|
|
17
|
-
*
|
|
18
|
-
* Enforcement is never `Promise.race` over a statement that keeps running:
|
|
19
|
-
*
|
|
20
|
-
* - `"between-statements"` (the default, and all six adapters can deliver it):
|
|
21
|
-
* before every statement, every nested `transaction()` and the `COMMIT`, an
|
|
22
|
-
* expired budget refuses. The statement is never sent and the transaction is
|
|
23
|
-
* rolled back. A statement ALREADY IN FLIGHT is not interrupted.
|
|
24
|
-
* - `"cancel-running-statements"` (postgres servers only): additionally hands
|
|
25
|
-
* the engine the remaining budget as `statement_timeout`, so the SERVER
|
|
26
|
-
* cancels an over-long statement. An adapter that cannot do this refuses the
|
|
27
|
-
* request outright rather than quietly delivering the weaker level.
|
|
28
|
-
*
|
|
29
|
-
* JavaScript cannot forcibly stop a running callback. A callback that keeps
|
|
30
|
-
* going past the deadline finds its later database calls refused; work it does
|
|
31
|
-
* outside the database (an HTTP call, an SMS) is not stopped and cannot be.
|
|
32
|
-
* There is no automatic retry: a deadline failure is terminal for that
|
|
33
|
-
* transaction, because external side effects may already have happened.
|
|
34
|
-
*/
|
|
35
|
-
import type { TransactionDeadlineEnforcement, TransactionDeadlineSupport, TransactionOptions } from "@vibeorm/runtime";
|
|
36
|
-
import { VibeError } from "@vibeorm/schema";
|
|
37
|
-
/** Where a budget check happened, reported as `meta.stage`. */
|
|
38
|
-
export type BudgetStage = "statement" | "nested" | "commit";
|
|
39
|
-
/**
|
|
40
|
-
* What is known about a finished transaction. `"unknown"` is a first-class
|
|
41
|
-
* outcome, not a failure to decide: when a rollback itself fails the commit
|
|
42
|
-
* state genuinely cannot be asserted, and claiming "rolled back" would be a lie.
|
|
43
|
-
*/
|
|
44
|
-
export type TransactionOutcome = "committed" | "rolled-back" | "unknown";
|
|
45
|
-
/** Wall clock, injectable so tests are deterministic instead of sleep-timed. */
|
|
46
|
-
export type DeadlineClock = () => number;
|
|
47
|
-
/** The ambient clock, used whenever no seam is injected. */
|
|
48
|
-
export declare const DEFAULT_DEADLINE_CLOCK: DeadlineClock;
|
|
49
|
-
/**
|
|
50
|
-
* One top-level transaction's budget. Nested savepoint handles share this
|
|
51
|
-
* object BY REFERENCE — they never get an independent clock, connection or
|
|
52
|
-
* session setting.
|
|
53
|
-
*/
|
|
54
|
-
export type TransactionBudget = {
|
|
55
|
-
/** The enforcement level in force, or `null` when no deadline was requested. */
|
|
56
|
-
readonly enforcement: TransactionDeadlineEnforcement | null;
|
|
57
|
-
/** The requested total budget in milliseconds, or `null` when none was. */
|
|
58
|
-
readonly totalMs: number | null;
|
|
59
|
-
/** Existing per-statement limit, independent of the total deadline. Zero disables it. */
|
|
60
|
-
readonly statementTimeoutMs: number | null;
|
|
61
|
-
/** Milliseconds left (never negative), or `null` when no deadline is set. */
|
|
62
|
-
remainingMs(): number | null;
|
|
63
|
-
/** True only when a deadline is set and has passed. */
|
|
64
|
-
expired(): boolean;
|
|
65
|
-
/** Refuse a closed handle or an expired budget. Called BEFORE anything is sent. */
|
|
66
|
-
assertUsable(params: {
|
|
67
|
-
stage: BudgetStage;
|
|
68
|
-
}): void;
|
|
69
|
-
/** Record the terminal outcome; the first call wins and later ones are ignored. */
|
|
70
|
-
close(params: {
|
|
71
|
-
outcome: TransactionOutcome;
|
|
72
|
-
}): void;
|
|
73
|
-
/** The recorded outcome, or `null` while the transaction is still open. */
|
|
74
|
-
readonly outcome: TransactionOutcome | null;
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Check `options.deadline` against what this adapter can honestly deliver.
|
|
78
|
-
* Called BEFORE `BEGIN` — a budget this engine cannot honour must never leave
|
|
79
|
-
* a transaction open behind it.
|
|
80
|
-
*
|
|
81
|
-
* @throws VibeError `VIBE_VALIDATION` when `totalMs` is not a positive integer.
|
|
82
|
-
* @throws VibeError `VIBE_UNSUPPORTED_CAPABILITY` when the requested
|
|
83
|
-
* `enforcement` is stronger than `support` — refused, never silently degraded.
|
|
84
|
-
*/
|
|
85
|
-
export declare function validateTransactionDeadline(params: {
|
|
86
|
-
options?: TransactionOptions;
|
|
87
|
-
support: TransactionDeadlineSupport;
|
|
88
|
-
provider: string;
|
|
89
|
-
}): void;
|
|
90
|
-
/**
|
|
91
|
-
* Start the clock for one top-level transaction. Call it immediately before
|
|
92
|
-
* `BEGIN`; {@link validateTransactionDeadline} must already have run.
|
|
93
|
-
*/
|
|
94
|
-
export declare function startTransactionBudget(params: {
|
|
95
|
-
options?: TransactionOptions;
|
|
96
|
-
provider: string;
|
|
97
|
-
clock?: DeadlineClock;
|
|
98
|
-
}): TransactionBudget;
|
|
99
|
-
/**
|
|
100
|
-
* The engine-side bound for the NEXT statement, or `null` when the caller did
|
|
101
|
-
* not buy `"cancel-running-statements"`. Floored at 1 ms: on postgres `0` means
|
|
102
|
-
* "no limit", so a spent budget must never be handed over as a zero.
|
|
103
|
-
*/
|
|
104
|
-
export declare function engineStatementBudgetMs(params: {
|
|
105
|
-
budget: TransactionBudget;
|
|
106
|
-
}): number | null;
|
|
107
|
-
/**
|
|
108
|
-
* The refusal an expired budget raises. `VIBE_TRANSACTION` with a stable
|
|
109
|
-
* `meta.reason` — no SQL text, no parameter values, no credentials.
|
|
110
|
-
*/
|
|
111
|
-
export declare function transactionDeadlineError(params: {
|
|
112
|
-
provider: string;
|
|
113
|
-
stage: BudgetStage;
|
|
114
|
-
totalMs: number;
|
|
115
|
-
overdueMs: number;
|
|
116
|
-
}): VibeError;
|
|
117
|
-
/**
|
|
118
|
-
* The refusal an ESCAPED transaction handle raises — a transactional adapter
|
|
119
|
-
* kept past the end of its transaction. Without this it would run on a
|
|
120
|
-
* connection that is back in the pool, outside any transaction.
|
|
121
|
-
*/
|
|
122
|
-
export declare function transactionClosedError(params: {
|
|
123
|
-
provider: string;
|
|
124
|
-
outcome: TransactionOutcome;
|
|
125
|
-
stage: BudgetStage;
|
|
126
|
-
}): VibeError;
|
|
127
|
-
/**
|
|
128
|
-
* The stable typed outcome for "the deadline expired and the rollback that was
|
|
129
|
-
* supposed to clean up failed too". Promising a rollback here would be false;
|
|
130
|
-
* promising a retry would be worse.
|
|
131
|
-
*
|
|
132
|
-
* `cause` is the CAUSAL error (the deadline refusal), so the reason the
|
|
133
|
-
* transaction was being abandoned survives. The rollback failure itself is
|
|
134
|
-
* reported only as `meta.rollbackFailed`: a raw driver failure can carry
|
|
135
|
-
* statement text, and this error is meant to be logged.
|
|
136
|
-
*/
|
|
137
|
-
export declare function transactionOutcomeUnknownError(params: {
|
|
138
|
-
provider: string;
|
|
139
|
-
totalMs: number;
|
|
140
|
-
cause: unknown;
|
|
141
|
-
}): VibeError;
|
|
142
|
-
/** Whether `error` is this module's deadline refusal (used to pick the cleanup path). */
|
|
143
|
-
export declare function isTransactionDeadlineError(error: unknown): boolean;
|
|
144
|
-
//# sourceMappingURL=transaction-budget.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-budget.d.ts","sourceRoot":"","sources":["../src/transaction-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EACV,8BAA8B,EAC9B,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,+DAA+D;AAC/D,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;AAEzE,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC;AAEzC,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,EAAE,aAAwC,CAAC;AAE9E;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,8BAA8B,GAAG,IAAI,CAAC;IAC5D,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,yFAAyF;IACzF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,6EAA6E;IAC7E,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;IAC7B,uDAAuD;IACvD,OAAO,IAAI,OAAO,CAAC;IACnB,mFAAmF;IACnF,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAAC;IACnD,mFAAmF;IACnF,KAAK,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,kBAAkB,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAC7C,CAAC;AAIF;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,0BAA0B,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAgCP;AAID;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,GAAG,iBAAiB,CAyCpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAAG,MAAM,GAAG,IAAI,CAQ5F;AAID;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,SAAS,CAaZ;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,WAAW,CAAC;CACpB,GAAG,SAAS,CAUZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,SAAS,CAiBZ;AAED,yFAAyF;AACzF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGlE"}
|