@vibeorm/adapter-pglite 4.2.0 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.js +297 -28
- package/dist/index.js.map +5 -4
- package/dist/protocol.d.ts +139 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -62,6 +62,8 @@ Two behaviours are specific to running in-process, and both are enforced rather
|
|
|
62
62
|
|
|
63
63
|
Nested transactions become savepoints on the open transaction, named from a counter shared across the whole top-level transaction.
|
|
64
64
|
|
|
65
|
+
On PGlite's in-process class the adapter sends a transaction's control statements itself (`BEGIN`, `COMMIT`, `ROLLBACK`, `SET TRANSACTION`, the savepoint statements): each is the same `Query` + `Sync` pair PGlite's `transaction()` and `exec()` send, in one engine call, inside PGlite's transaction lock, with PGlite's error objects. A transaction costs about 60 µs less, a savepoint pair about 60 µs less. Code that observes `execProtocolStream` sees these calls; other instances (`PGliteWorker`, another copy of the module) keep PGlite's `transaction()`.
|
|
66
|
+
|
|
65
67
|
## Caller cancellation of reads
|
|
66
68
|
|
|
67
69
|
This adapter declares `readCancellation: "before-dispatch"` on root, transaction and savepoint handles. Execution options are separate from query arguments and apply only to known reads. An abort observed before dispatch, including after waiting for the operation queue, prevents SQL. If a statement has already started, it settles before successful rows are discarded with `VIBE_CANCELLED` and `databaseCancelled: false`. Unrelated driver failures keep their mapped codes.
|
package/dist/index.js
CHANGED
|
@@ -12,11 +12,15 @@ import {
|
|
|
12
12
|
runPinnedSession,
|
|
13
13
|
transactionLifecycleRefusal,
|
|
14
14
|
createSavepointScope,
|
|
15
|
+
findRefusedSetting,
|
|
16
|
+
readTransactionSettings,
|
|
17
|
+
renderTransactionSettings,
|
|
15
18
|
runNestedSavepoint,
|
|
16
19
|
runRootScope,
|
|
17
20
|
scopeTurn,
|
|
18
21
|
settleFailedTransaction,
|
|
19
22
|
startTransactionBudget,
|
|
23
|
+
transactionSettingRefusedError,
|
|
20
24
|
validateTransactionDeadline,
|
|
21
25
|
readOnlyRequested
|
|
22
26
|
} from "@vibeorm/runtime/adapter-kit";
|
|
@@ -159,6 +163,225 @@ function serializeParamsRaw(params) {
|
|
|
159
163
|
});
|
|
160
164
|
}
|
|
161
165
|
|
|
166
|
+
// packages/adapter-pglite/src/protocol.ts
|
|
167
|
+
function createProtocolKit(params) {
|
|
168
|
+
const loose = params.module;
|
|
169
|
+
if (typeof loose.PGlite !== "function" || typeof loose.protocol?.serialize !== "object" || typeof loose.parse?.parseResults !== "function" || typeof loose.parse.parseDescribeStatementResults !== "function" || typeof loose.messages?.DatabaseError !== "function") {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const { module } = params;
|
|
173
|
+
return Object.freeze({
|
|
174
|
+
PGlite: module.PGlite,
|
|
175
|
+
serialize: module.protocol.serialize,
|
|
176
|
+
parseResults: module.parse.parseResults,
|
|
177
|
+
parseDescribeStatementResults: module.parse.parseDescribeStatementResults,
|
|
178
|
+
DatabaseError: module.messages.DatabaseError
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
var PROTOCOL_MEMBERS = Object.freeze([
|
|
182
|
+
"execProtocolStream",
|
|
183
|
+
"_checkReady",
|
|
184
|
+
"_runExclusiveTransaction",
|
|
185
|
+
"runExclusive",
|
|
186
|
+
"syncToFs",
|
|
187
|
+
"_handleBlob",
|
|
188
|
+
"_cleanupBlob",
|
|
189
|
+
"_getWrittenBlob"
|
|
190
|
+
]);
|
|
191
|
+
var PROTOCOL_INSTANCES = new WeakMap;
|
|
192
|
+
function isProtocolCapable(params) {
|
|
193
|
+
if (!(params.instance instanceof params.kit.PGlite))
|
|
194
|
+
return false;
|
|
195
|
+
const members = params.instance;
|
|
196
|
+
for (const name of PROTOCOL_MEMBERS) {
|
|
197
|
+
if (typeof members[name] !== "function")
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return typeof members.parsers === "object" && members.parsers !== null && typeof members.serializers === "object" && members.serializers !== null;
|
|
201
|
+
}
|
|
202
|
+
function protocolInstance(params) {
|
|
203
|
+
const known = PROTOCOL_INSTANCES.get(params.instance);
|
|
204
|
+
if (known !== undefined)
|
|
205
|
+
return known ?? undefined;
|
|
206
|
+
const verdict = isProtocolCapable(params) ? params.instance : null;
|
|
207
|
+
PROTOCOL_INSTANCES.set(params.instance, verdict);
|
|
208
|
+
return verdict ?? undefined;
|
|
209
|
+
}
|
|
210
|
+
var NO_FS_SYNC = Object.freeze({ syncToFs: false });
|
|
211
|
+
function joinMessages(parts) {
|
|
212
|
+
let size = 0;
|
|
213
|
+
for (const part of parts)
|
|
214
|
+
size += part.length;
|
|
215
|
+
const joined = new Uint8Array(size);
|
|
216
|
+
let offset = 0;
|
|
217
|
+
for (const part of parts) {
|
|
218
|
+
joined.set(part, offset);
|
|
219
|
+
offset += part.length;
|
|
220
|
+
}
|
|
221
|
+
return joined;
|
|
222
|
+
}
|
|
223
|
+
function serializeValue(params) {
|
|
224
|
+
const { value } = params;
|
|
225
|
+
if (value === null || value === undefined)
|
|
226
|
+
return null;
|
|
227
|
+
const serializer = params.serializers[params.oid];
|
|
228
|
+
if (serializer)
|
|
229
|
+
return serializer(value);
|
|
230
|
+
return value.toString();
|
|
231
|
+
}
|
|
232
|
+
function columnParsers(params) {
|
|
233
|
+
const subset = {};
|
|
234
|
+
for (const message of params.messages) {
|
|
235
|
+
if (message.name !== "rowDescription")
|
|
236
|
+
continue;
|
|
237
|
+
for (const field of message.fields) {
|
|
238
|
+
const parser = params.db.parsers[field.dataTypeID];
|
|
239
|
+
if (parser !== undefined)
|
|
240
|
+
subset[field.dataTypeID] = parser;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return subset;
|
|
244
|
+
}
|
|
245
|
+
async function runProtocolQuery(params) {
|
|
246
|
+
const { db, transaction } = params;
|
|
247
|
+
if (transaction === undefined) {
|
|
248
|
+
await db._checkReady();
|
|
249
|
+
return db._runExclusiveTransaction(() => runStatement({ ...params, root: true }));
|
|
250
|
+
}
|
|
251
|
+
if (transaction.closed)
|
|
252
|
+
throw new Error("Transaction is closed");
|
|
253
|
+
return runStatement({ ...params, root: false });
|
|
254
|
+
}
|
|
255
|
+
async function runStatement(params) {
|
|
256
|
+
const { kit, db, text, values } = params;
|
|
257
|
+
const options = params.callerOptions === undefined ? { parsers: params.parsers } : params.callerOptions.value;
|
|
258
|
+
return db.runExclusive(async () => {
|
|
259
|
+
if (db.debug > 0)
|
|
260
|
+
console.log("runQuery", text, values, options);
|
|
261
|
+
await db._handleBlob(undefined);
|
|
262
|
+
let messages = [];
|
|
263
|
+
let synced = false;
|
|
264
|
+
try {
|
|
265
|
+
const { serialize } = kit;
|
|
266
|
+
let head;
|
|
267
|
+
let serialized;
|
|
268
|
+
if (values.some((value) => value !== null && value !== undefined)) {
|
|
269
|
+
const described = await db.execProtocolStream(joinMessages([serialize.parse({ text }), serialize.describe({ type: "S" })]), NO_FS_SYNC);
|
|
270
|
+
const dataTypeIDs = kit.parseDescribeStatementResults(described);
|
|
271
|
+
serialized = values.map((value, index) => serializeValue({ serializers: db.serializers, value, oid: dataTypeIDs[index] }));
|
|
272
|
+
head = [];
|
|
273
|
+
} else {
|
|
274
|
+
serialized = values.map(() => null);
|
|
275
|
+
head = [serialize.parse({ text })];
|
|
276
|
+
}
|
|
277
|
+
const batch = joinMessages([...head, serialize.bind({ values: serialized }), serialize.describe({ type: "P" }), serialize.execute({}), serialize.sync()]);
|
|
278
|
+
try {
|
|
279
|
+
messages = await db.execProtocolStream(batch, NO_FS_SYNC);
|
|
280
|
+
synced = true;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
if (error instanceof kit.DatabaseError)
|
|
283
|
+
synced = true;
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (error instanceof kit.DatabaseError)
|
|
288
|
+
throw Object.assign(error, { query: text, params: values, queryOptions: options });
|
|
289
|
+
throw error;
|
|
290
|
+
} finally {
|
|
291
|
+
if (!synced)
|
|
292
|
+
await db.execProtocolStream(kit.serialize.sync(), NO_FS_SYNC);
|
|
293
|
+
}
|
|
294
|
+
await db._cleanupBlob();
|
|
295
|
+
if (params.root)
|
|
296
|
+
await db.syncToFs();
|
|
297
|
+
const blob = await db._getWrittenBlob();
|
|
298
|
+
const [result] = kit.parseResults(messages, columnParsers({ db, messages }), options, blob);
|
|
299
|
+
return result ?? { rows: [], fields: [], affectedRows: 0 };
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
async function runSimpleQuery(params) {
|
|
303
|
+
const { kit, db, text, options } = params;
|
|
304
|
+
return db.runExclusive(async () => {
|
|
305
|
+
if (db.debug > 0)
|
|
306
|
+
console.log("runExec", text, options);
|
|
307
|
+
await db._handleBlob(options?.blob);
|
|
308
|
+
let messages = [];
|
|
309
|
+
let synced = false;
|
|
310
|
+
try {
|
|
311
|
+
try {
|
|
312
|
+
messages = await db.execProtocolStream(joinMessages([kit.serialize.query(text), kit.serialize.sync()]), NO_FS_SYNC);
|
|
313
|
+
synced = true;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (error instanceof kit.DatabaseError)
|
|
316
|
+
synced = true;
|
|
317
|
+
throw error;
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (error instanceof kit.DatabaseError)
|
|
321
|
+
throw Object.assign(error, { query: text, params: undefined, queryOptions: options });
|
|
322
|
+
throw error;
|
|
323
|
+
} finally {
|
|
324
|
+
if (!synced)
|
|
325
|
+
await db.execProtocolStream(kit.serialize.sync(), NO_FS_SYNC);
|
|
326
|
+
}
|
|
327
|
+
await db._cleanupBlob();
|
|
328
|
+
if (params.root)
|
|
329
|
+
await db.syncToFs();
|
|
330
|
+
const blob = await db._getWrittenBlob();
|
|
331
|
+
return { messages, blob };
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
async function execStatements(params) {
|
|
335
|
+
const { messages, blob } = await runSimpleQuery(params);
|
|
336
|
+
return params.kit.parseResults(messages, columnParsers({ db: params.db, messages }), params.options, blob);
|
|
337
|
+
}
|
|
338
|
+
async function runProtocolExec(params) {
|
|
339
|
+
const { kit, db, text } = params;
|
|
340
|
+
await db._checkReady();
|
|
341
|
+
return db._runExclusiveTransaction(() => execStatements({ kit, db, text, options: undefined, root: true }));
|
|
342
|
+
}
|
|
343
|
+
async function runProtocolTransaction(params) {
|
|
344
|
+
const { kit, db, body } = params;
|
|
345
|
+
await db._checkReady();
|
|
346
|
+
return db._runExclusiveTransaction(async () => {
|
|
347
|
+
await runSimpleQuery({ kit, db, text: "BEGIN", options: undefined, root: true });
|
|
348
|
+
let inTransaction = true;
|
|
349
|
+
let closed = false;
|
|
350
|
+
const checkClosed = () => {
|
|
351
|
+
if (closed)
|
|
352
|
+
throw new Error("Transaction is closed");
|
|
353
|
+
};
|
|
354
|
+
const handle = {
|
|
355
|
+
query: async (text, values, options) => {
|
|
356
|
+
checkClosed();
|
|
357
|
+
const statement = { kit, db, text, values: values ?? [], parsers: options?.parsers ?? {}, root: !inTransaction, callerOptions: { value: options } };
|
|
358
|
+
return await runStatement(statement);
|
|
359
|
+
},
|
|
360
|
+
exec: async (text, options) => {
|
|
361
|
+
checkClosed();
|
|
362
|
+
return execStatements({ kit, db, text, options, root: !inTransaction });
|
|
363
|
+
},
|
|
364
|
+
get closed() {
|
|
365
|
+
return closed;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
try {
|
|
369
|
+
const result = await body(handle);
|
|
370
|
+
if (!closed) {
|
|
371
|
+
closed = true;
|
|
372
|
+
await runSimpleQuery({ kit, db, text: "COMMIT", options: undefined, root: false });
|
|
373
|
+
}
|
|
374
|
+
inTransaction = false;
|
|
375
|
+
return result;
|
|
376
|
+
} catch (error) {
|
|
377
|
+
if (!closed)
|
|
378
|
+
await runSimpleQuery({ kit, db, text: "ROLLBACK", options: undefined, root: false });
|
|
379
|
+
inTransaction = false;
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
162
385
|
// packages/adapter-pglite/src/queue.ts
|
|
163
386
|
import { AsyncLocalStorage } from "async_hooks";
|
|
164
387
|
var INJECTED_OWNERSHIP = new WeakMap;
|
|
@@ -236,16 +459,28 @@ function parseTimestampAsUtc(value) {
|
|
|
236
459
|
}
|
|
237
460
|
return new Date(value);
|
|
238
461
|
}
|
|
239
|
-
var
|
|
240
|
-
async function
|
|
241
|
-
if (
|
|
242
|
-
return
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
462
|
+
var driverKit = null;
|
|
463
|
+
async function getDriverKit() {
|
|
464
|
+
if (driverKit !== null)
|
|
465
|
+
return driverKit;
|
|
466
|
+
const pglite = await import("@electric-sql/pglite");
|
|
467
|
+
const { types } = pglite;
|
|
468
|
+
driverKit = {
|
|
469
|
+
parsers: {
|
|
470
|
+
[TIMESTAMP_OID]: parseTimestampAsUtc,
|
|
471
|
+
[TIMESTAMP_ARRAY_OID]: (text) => types.arrayParser(text, parseTimestampAsUtc, TIMESTAMP_ARRAY_OID)
|
|
472
|
+
},
|
|
473
|
+
protocol: createProtocolKit({ module: pglite })
|
|
247
474
|
};
|
|
248
|
-
return
|
|
475
|
+
return driverKit;
|
|
476
|
+
}
|
|
477
|
+
function runQuery(params) {
|
|
478
|
+
const { connection, kit, text, values } = params;
|
|
479
|
+
const protocol = kit.protocol;
|
|
480
|
+
const db = protocol === undefined ? undefined : protocolInstance({ kit: protocol, instance: connection.db });
|
|
481
|
+
if (protocol === undefined || db === undefined)
|
|
482
|
+
return (connection.tx ?? connection.db).query(text, values, { parsers: kit.parsers });
|
|
483
|
+
return runProtocolQuery({ kit: protocol, db, transaction: connection.tx, text, values, parsers: kit.parsers });
|
|
249
484
|
}
|
|
250
485
|
|
|
251
486
|
class CallbackError extends Error {
|
|
@@ -260,6 +495,8 @@ var DIALECT = "postgres";
|
|
|
260
495
|
var PROVIDER = "pglite";
|
|
261
496
|
var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
|
|
262
497
|
var ROUND_TRIPS = "in-process";
|
|
498
|
+
var READ_SNAPSHOT = Object.freeze({ isolationLevel: "RepeatableRead", accessMode: "readOnly" });
|
|
499
|
+
var PIPELINING = "ordered";
|
|
263
500
|
var BUDGETS = Object.freeze({
|
|
264
501
|
acquireTimeout: false,
|
|
265
502
|
statementTimeout: "unsupported",
|
|
@@ -311,17 +548,18 @@ function pgliteAdapter(options) {
|
|
|
311
548
|
}
|
|
312
549
|
async function runExecute(params) {
|
|
313
550
|
const values = serializeParams({ values: params.values });
|
|
314
|
-
const
|
|
551
|
+
const kit = driverKit ?? await getDriverKit();
|
|
315
552
|
assertReadExecution({ ...params, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
316
|
-
const result = await runMapped(() => params.
|
|
553
|
+
const result = await runMapped(() => runQuery({ connection: params.connection, kit, text: params.text, values }));
|
|
317
554
|
if (params.scope !== undefined)
|
|
318
555
|
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows, rows: result.rows.length, resultRows: result.rows }) });
|
|
319
556
|
return result.rows;
|
|
320
557
|
}
|
|
321
558
|
async function runExecuteUnsafe(params) {
|
|
322
|
-
const
|
|
559
|
+
const kit = driverKit ?? await getDriverKit();
|
|
323
560
|
if (params.values === undefined) {
|
|
324
|
-
const
|
|
561
|
+
const queryable = params.connection.tx ?? params.connection.db;
|
|
562
|
+
const results = await runMapped(() => queryable.exec(params.text, { parsers: kit.parsers }));
|
|
325
563
|
if (params.scope !== undefined) {
|
|
326
564
|
for (const result of results)
|
|
327
565
|
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });
|
|
@@ -332,13 +570,14 @@ function pgliteAdapter(options) {
|
|
|
332
570
|
return { rows: last?.rows ?? [], affectedRows: last?.affectedRows ?? 0 };
|
|
333
571
|
}
|
|
334
572
|
const values = serializeParamsRaw({ values: params.values });
|
|
335
|
-
const result = await runMapped(() => params.
|
|
573
|
+
const result = await runMapped(() => runQuery({ connection: params.connection, kit, text: params.text, values }));
|
|
336
574
|
if (params.scope !== undefined)
|
|
337
575
|
observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });
|
|
338
576
|
return { rows: result.rows, affectedRows: result.affectedRows ?? 0 };
|
|
339
577
|
}
|
|
340
578
|
function createTransactionAdapter(params) {
|
|
341
|
-
const { tx, scope, budget } = params;
|
|
579
|
+
const { db, tx, scope, budget } = params;
|
|
580
|
+
const connection = { db, tx };
|
|
342
581
|
return {
|
|
343
582
|
dialect: DIALECT,
|
|
344
583
|
provider: PROVIDER,
|
|
@@ -346,6 +585,8 @@ function pgliteAdapter(options) {
|
|
|
346
585
|
budgets: BUDGETS,
|
|
347
586
|
readCancellation: "before-dispatch",
|
|
348
587
|
roundTrips: ROUND_TRIPS,
|
|
588
|
+
readSnapshot: READ_SNAPSHOT,
|
|
589
|
+
pipelining: PIPELINING,
|
|
349
590
|
rowChanges: () => {
|
|
350
591
|
budget.assertUsable({ stage: "statement" });
|
|
351
592
|
return readRowChanges({ scope });
|
|
@@ -356,7 +597,7 @@ function pgliteAdapter(options) {
|
|
|
356
597
|
if (turn !== undefined)
|
|
357
598
|
await turn;
|
|
358
599
|
budget.assertUsable({ stage: "statement" });
|
|
359
|
-
return runExecute({
|
|
600
|
+
return runExecute({ connection, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
|
|
360
601
|
} });
|
|
361
602
|
},
|
|
362
603
|
async executeUnsafe(execParams) {
|
|
@@ -364,7 +605,7 @@ function pgliteAdapter(options) {
|
|
|
364
605
|
if (turn !== undefined)
|
|
365
606
|
await turn;
|
|
366
607
|
budget.assertUsable({ stage: "statement" });
|
|
367
|
-
return runExecuteUnsafe({
|
|
608
|
+
return runExecuteUnsafe({ connection, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
|
|
368
609
|
},
|
|
369
610
|
async transaction(fn, nestedOptions) {
|
|
370
611
|
refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
|
|
@@ -372,7 +613,7 @@ function pgliteAdapter(options) {
|
|
|
372
613
|
return runNestedSavepoint({
|
|
373
614
|
scope,
|
|
374
615
|
execute: (statement) => runMapped(() => tx.exec(statement)),
|
|
375
|
-
body: (child) => fn(createTransactionAdapter({ tx, scope: child, budget }))
|
|
616
|
+
body: (child) => fn(createTransactionAdapter({ db, tx, scope: child, budget }))
|
|
376
617
|
});
|
|
377
618
|
},
|
|
378
619
|
async connect() {
|
|
@@ -413,11 +654,14 @@ function pgliteAdapter(options) {
|
|
|
413
654
|
const db = await getInstance();
|
|
414
655
|
return runInCallback(() => runPinnedSession({
|
|
415
656
|
run,
|
|
416
|
-
execute: async (query) => (await runExecuteUnsafe({
|
|
657
|
+
execute: async (query) => (await runExecuteUnsafe({ connection: { db }, ...query })).rows,
|
|
417
658
|
isLost: () => db.closed,
|
|
418
659
|
release: async () => {
|
|
419
|
-
if (
|
|
420
|
-
|
|
660
|
+
if (db.closed)
|
|
661
|
+
return;
|
|
662
|
+
const kit = driverKit ?? await getDriverKit();
|
|
663
|
+
const protocolDb = kit.protocol === undefined ? undefined : protocolInstance({ kit: kit.protocol, instance: db });
|
|
664
|
+
await runMapped(() => kit.protocol === undefined || protocolDb === undefined ? db.exec("ROLLBACK") : runProtocolExec({ kit: kit.protocol, db: protocolDb, text: "ROLLBACK" }));
|
|
421
665
|
}
|
|
422
666
|
}));
|
|
423
667
|
});
|
|
@@ -427,12 +671,12 @@ function pgliteAdapter(options) {
|
|
|
427
671
|
assertReadExecution({ ...params, provider: PROVIDER });
|
|
428
672
|
return queue.run(async () => {
|
|
429
673
|
assertReadExecution({ ...params, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
430
|
-
return runExecute({
|
|
674
|
+
return runExecute({ connection: { db: await getInstance() }, ...params });
|
|
431
675
|
});
|
|
432
676
|
},
|
|
433
677
|
async executeUnsafe(params) {
|
|
434
678
|
assertNotInsideTransaction();
|
|
435
|
-
return queue.run(async () => runExecuteUnsafe({
|
|
679
|
+
return queue.run(async () => runExecuteUnsafe({ connection: { db: await getInstance() }, ...params }));
|
|
436
680
|
},
|
|
437
681
|
async transaction(fn, transactionOptions) {
|
|
438
682
|
assertNotInsideTransaction();
|
|
@@ -446,24 +690,39 @@ function pgliteAdapter(options) {
|
|
|
446
690
|
validateTransactionDeadline({
|
|
447
691
|
options: transactionOptions,
|
|
448
692
|
support: BUDGETS.transactionDeadline,
|
|
449
|
-
provider: PROVIDER
|
|
693
|
+
provider: PROVIDER,
|
|
694
|
+
settings: "transaction-local"
|
|
450
695
|
});
|
|
451
696
|
const modesText = setTransactionModesStatement({
|
|
452
697
|
isolationLevel: transactionOptions?.isolationLevel,
|
|
453
698
|
readOnly: readOnlyRequested({ options: transactionOptions, provider: PROVIDER })
|
|
454
699
|
});
|
|
700
|
+
const settings = readTransactionSettings({ settings: transactionOptions?.settings, provider: PROVIDER });
|
|
701
|
+
const settingsStatement = settings.length === 0 ? undefined : renderTransactionSettings({ settings });
|
|
455
702
|
const scope = createSavepointScope();
|
|
456
703
|
return queue.run(async () => {
|
|
457
704
|
const db = await getInstance();
|
|
705
|
+
const kit = driverKit ?? await getDriverKit();
|
|
706
|
+
const protocolDb = kit.protocol === undefined ? undefined : protocolInstance({ kit: kit.protocol, instance: db });
|
|
707
|
+
const protocolKit = kit.protocol;
|
|
708
|
+
const openTransaction = (body) => protocolKit === undefined || protocolDb === undefined ? db.transaction(body) : runProtocolTransaction({ kit: protocolKit, db: protocolDb, body });
|
|
458
709
|
const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });
|
|
459
710
|
const commit = { progress: "not-sent" };
|
|
460
711
|
const handed = {};
|
|
712
|
+
const refused = {};
|
|
461
713
|
try {
|
|
462
|
-
const result = await
|
|
714
|
+
const result = await openTransaction(async (tx) => {
|
|
463
715
|
try {
|
|
464
716
|
if (modesText !== undefined)
|
|
465
717
|
await runMapped(() => tx.exec(modesText));
|
|
466
|
-
|
|
718
|
+
if (settingsStatement !== undefined) {
|
|
719
|
+
await runMapped(() => tx.query(settingsStatement.text, [...settingsStatement.values])).then(undefined, (error) => {
|
|
720
|
+
if (error instanceof VibeError3 && typeof error.meta.sqlstate === "string")
|
|
721
|
+
refused.cause = error;
|
|
722
|
+
throw error;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
const value = await runInCallback(() => runRootScope({ scope, body: () => fn(createTransactionAdapter({ db, tx, scope, budget })) }));
|
|
467
726
|
budget.assertUsable({ stage: "commit" });
|
|
468
727
|
commit.progress = "sent";
|
|
469
728
|
return value;
|
|
@@ -479,8 +738,15 @@ function pgliteAdapter(options) {
|
|
|
479
738
|
if (commit.progress === "sent" && failure === undefined)
|
|
480
739
|
commit.progress = "answered";
|
|
481
740
|
const rollback = failure === undefined ? "not-needed" : error === failure ? "acknowledged" : "failed";
|
|
741
|
+
const settingsCause = refused.cause !== undefined && failure?.inner === refused.cause ? refused.cause : undefined;
|
|
742
|
+
const settingsError = settingsCause === undefined ? undefined : transactionSettingRefusedError({
|
|
743
|
+
provider: PROVIDER,
|
|
744
|
+
settings,
|
|
745
|
+
setting: findRefusedSetting({ settings, cause: settingsCause }),
|
|
746
|
+
cause: settingsCause
|
|
747
|
+
});
|
|
482
748
|
throw settleFailedTransaction({
|
|
483
|
-
error: failure === undefined ? mapPgliteDriverError({ error }) : failure.inner,
|
|
749
|
+
error: failure === undefined ? mapPgliteDriverError({ error }) : settingsError ?? failure.inner,
|
|
484
750
|
provider: PROVIDER,
|
|
485
751
|
budget,
|
|
486
752
|
scope,
|
|
@@ -505,7 +771,10 @@ function pgliteAdapter(options) {
|
|
|
505
771
|
budgets: BUDGETS,
|
|
506
772
|
readCancellation: "before-dispatch",
|
|
507
773
|
roundTrips: ROUND_TRIPS,
|
|
774
|
+
readSnapshot: READ_SNAPSHOT,
|
|
775
|
+
pipelining: PIPELINING,
|
|
508
776
|
transactionOutcome: "reported",
|
|
777
|
+
transactionSettings: "transaction-local",
|
|
509
778
|
withSession({ run }) {
|
|
510
779
|
return lifecycle.admit({ run: () => root.withSession({ run }) });
|
|
511
780
|
},
|
|
@@ -555,4 +824,4 @@ export {
|
|
|
555
824
|
validateTransactionDeadline2 as validateTransactionDeadline
|
|
556
825
|
};
|
|
557
826
|
|
|
558
|
-
//# debugId=
|
|
827
|
+
//# debugId=428C39C9B2B1FAF264756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/errors.ts", "../src/params.ts", "../src/queue.ts", "../src/transaction-sql.ts"],
|
|
3
|
+
"sources": ["../src/index.ts", "../src/errors.ts", "../src/params.ts", "../src/protocol.ts", "../src/queue.ts", "../src/transaction-sql.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @vibeorm/adapter-pglite — PGlite (in-process PostgreSQL, WASM) adapter for\n * VibeORM v2.\n *\n * PGlite is a *single* embedded connection, so this adapter serializes every\n * operation through one queue — including whole transactions, which hold the\n * queue until they commit or roll back. That makes concurrent client calls safe\n * without a pool, and makes an in-memory instance an honest postgres target for\n * tests (no Docker).\n */\n\nimport { VibeError } from \"@vibeorm/schema\";\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n RowChangeCount,\n StatementEffect,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport {\n assertReadExecution,\n runReadExecution,\n abortTransactionOnServerFailure,\n observeRowChanges,\n readRowChanges,\n statementRowChanges,\n createAdapterLifecycleGate,\n runPinnedSession,\n type CommitProgress,\n transactionLifecycleRefusal,\n createSavepointScope,\n type RollbackOutcome,\n runNestedSavepoint,\n runRootScope,\n type SavepointScope,\n scopeTurn,\n settleFailedTransaction,\n startTransactionBudget,\n type TransactionBudget,\n validateTransactionDeadline,\n readOnlyRequested,\n} from \"@vibeorm/runtime/adapter-kit\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport type { Extensions, PGliteInterface, ParserOptions, Transaction } from \"@electric-sql/pglite\";\nimport { mapPgliteDriverError, runMapped } from \"./errors.ts\";\nimport { PgliteArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nimport { connectionOwnership } from \"./queue.ts\";\nimport { refuseNestedTransactionOptions, setTransactionModesStatement } from \"./transaction-sql.ts\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/** Options for {@link pgliteAdapter}. */\nexport type PgliteAdapterOptions = {\n /**\n * Directory to persist the database in. Omit for a fresh in-memory database —\n * the usual choice for tests. Prefixes PGlite understands (`idb://`,\n * `memory://`) work here too.\n */\n readonly dataDir?: string;\n /**\n * An already-created PGlite instance to run on (sharing one database across\n * adapters, or reusing a warm instance between test files). The adapter never\n * closes an instance it did not create: `disconnect()` only ends this adapter.\n */\n readonly pglite?: PGliteInterface;\n /**\n * PGlite extension bundles to load into the instance the ADAPTER creates —\n * `{ vector }` from `@electric-sql/pglite-pgvector`, `pg_trgm` from\n * `@electric-sql/pglite/contrib/pg_trgm`, … The bundles still come from\n * their own packages; this option removes the need to construct PGlite\n * yourself just to attach them. Mutually exclusive with `pglite` (that\n * instance was already constructed with its extensions) — passing both\n * throws `VIBE_CONFIG`.\n */\n readonly extensions?: Extensions;\n};\n\n// ─── Internals ────────────────────────────────────────────────────\n\n/** Anything that runs SQL: the PGlite instance itself or an open transaction. */\ntype Queryable = Pick<Transaction, \"query\" | \"exec\">;\n\n// ─── UTC zone-less timestamps ─────────────────────────────────────\n\n/** `timestamp without time zone` and its array form. */\nconst TIMESTAMP_OID = 1114;\nconst TIMESTAMP_ARRAY_OID = 1115;\n\n/** PGlite's text output for a zone-less timestamp: `2026-03-08 19:38:08.538`. */\nconst ZONE_LESS_TEXT = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,6})?$/;\n\n/**\n * Zone-less timestamp text read as **UTC** — the codec table's\n * `parseDateTimeText` rule (@vibeorm/sql) applied at the driver level, because\n * PGlite hands these columns to the ORM as `Date` objects already\n * (`wire.dateTime: \"native\"` prunes the codec decode). PGlite's own parser is\n * `new Date(text)`, which ECMA-262 reads as host-LOCAL time — while PGlite\n * SERIALIZES a `Date` parameter as UTC (`toISOString()`), so a value did not\n * even round-trip on a non-UTC host. `date` is unaffected (date-only text is\n * UTC midnight under ECMA-262 already). Anything outside the plain form keeps\n * PGlite's parser.\n */\nfunction parseTimestampAsUtc(value: string): unknown {\n if (ZONE_LESS_TEXT.test(value)) {\n const parsed = new Date(`${value.replace(\" \", \"T\").replace(/(\\.\\d{3})\\d+$/, \"$1\")}Z`);\n if (!Number.isNaN(parsed.getTime())) return parsed;\n }\n return new Date(value);\n}\n\nlet utcParsers: ParserOptions | null = null;\n\n/**\n * Per-query parsers (`QueryOptions.parsers`) — never `instance.parsers`, which\n * a caller-supplied instance shares with everyone else. The array parser is\n * PGlite's own (`types.arrayParser`), resolved lazily from the same dynamic\n * import `getInstance` uses, so the WASM module is never loaded just to build\n * this table.\n */\nasync function getUtcParsers(): Promise<ParserOptions> {\n if (utcParsers !== null) return utcParsers;\n const { types } = await import(\"@electric-sql/pglite\");\n utcParsers = {\n [TIMESTAMP_OID]: parseTimestampAsUtc,\n [TIMESTAMP_ARRAY_OID]: (text: string): unknown => types.arrayParser(text, parseTimestampAsUtc, TIMESTAMP_ARRAY_OID),\n };\n return utcParsers;\n}\n\n/**\n * Wrapper that marks an error as \"thrown by the user's transaction callback\",\n * so it can be rethrown untouched instead of being mapped as a driver failure\n * after PGlite has passed it back out of `transaction()`.\n */\nclass CallbackError extends Error {\n readonly inner: unknown;\n\n constructor(inner: unknown) {\n super(\"vibeorm: transaction callback failed\");\n this.name = \"CallbackError\";\n this.inner = inner;\n }\n}\n\nconst DIALECT: Dialect = \"postgres\";\nconst PROVIDER: Provider = \"pglite\";\n\n/**\n * pglite wire (board #30, live-verified on both execute paths): timestamptz →\n * `Date`, json/jsonb → parsed values, int4/float8 → numbers. (int8 arrives as\n * a native `bigint` here — the BigInt codec passes it through; not declared.)\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"native\", json: \"parsed\", numbers: \"native\" } as const);\n\n/**\n * PGlite runs inside this process (WASM): a round trip crosses no network, so\n * the runtime keeps its multi-statement paths instead of the one-statement\n * folds. The transaction facades carry the same declaration.\n */\nconst ROUND_TRIPS: NonNullable<DatabaseAdapter[\"roundTrips\"]> = \"in-process\";\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: false` — one in-process WASM instance behind a queue.\n * There is no pool to exhaust.\n * - `statementTimeout: \"unsupported\"` — **PGlite ACCEPTING `statement_timeout`\n * is not support.** The single-threaded WASM build reports the setting back\n * via SHOW and never fires it (board #2, live-verified: `pg_sleep(2)` under a\n * 100 ms timeout runs to completion), which is why the `timeout` option is\n * refused outright below.\n * - `transactionDeadline: \"between-statements\"` — the budget is checked before\n * each statement and before the commit, which PGlite CAN honour because those\n * checks happen in JavaScript. It is not cancellation, for the same\n * single-threaded reason, so `\"cancel-running-statements\"` is refused.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: false,\n statementTimeout: \"unsupported\",\n transactionDeadline: \"between-statements\",\n accessModes: Object.freeze([\"readOnly\"] as const),\n} as const);\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\n/**\n * Create a VibeORM adapter backed by PGlite.\n *\n * @example In-memory database (tests)\n * ```ts\n * const adapter = pgliteAdapter();\n * await adapter.executeUnsafe({ text: `CREATE TABLE \"User\" (\"id\" serial primary key)` });\n * ```\n *\n * @example Persistent database, or a shared instance\n * ```ts\n * const adapter = pgliteAdapter({ dataDir: \"./.pglite\" });\n * const shared = pgliteAdapter({ pglite: existingInstance });\n * ```\n *\n * `disconnect()` ends the adapter for good: it waits for admitted work, closes\n * an instance the adapter created (never a provided `pglite`), and every later\n * call refuses with `VIBE_ADAPTER_CLOSED`. Create a new adapter to connect\n * again; with no `dataDir` that is a new, empty in-memory database.\n *\n * @example Bundled extensions (pgvector) without hand-constructing PGlite\n * ```ts\n * import { vector } from \"@electric-sql/pglite-pgvector\";\n * const adapter = pgliteAdapter({ dataDir: \"./.pglite\", extensions: { vector } });\n * ```\n */\nexport function pgliteAdapter(options?: PgliteAdapterOptions): DatabaseAdapter {\n if (options?.pglite !== undefined && options.extensions !== undefined) {\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"pgliteAdapter: `extensions` applies to an instance the adapter CREATES — a provided `pglite` instance was already constructed with its own extensions; pass one or the other\",\n meta: { provider: PROVIDER },\n });\n }\n const { queue, txContext, runInCallback } = connectionOwnership({ database: options?.pglite });\n const ownsInstance = options?.pglite === undefined;\n\n /**\n * Deadlock guard (review F6): PGlite is a single connection, and a whole\n * transaction holds the operation queue — so a ROOT-adapter call issued from\n * inside a `transaction()` callback would queue behind the very transaction\n * it is running in and hang forever. The callback runs inside this context;\n * root entry points refuse instead of deadlocking. The tx-bound adapter\n * bypasses the queue and stays fully usable.\n */\n function assertNotInsideTransaction(): void {\n if (txContext.getStore()?.active === 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 instance: PGliteInterface | null = options?.pglite ?? null;\n let pending: Promise<PGliteInterface> | null = null;\n\n async function getInstance(): Promise<PGliteInterface> {\n if (instance !== null) return instance;\n if (pending === null) {\n pending = runMapped(async () => {\n const { PGlite } = await import(\"@electric-sql/pglite\");\n const created = new PGlite({\n ...(options?.dataDir === undefined ? {} : { dataDir: options.dataDir }),\n ...(options?.extensions === undefined ? {} : { extensions: options.extensions }),\n });\n await created.waitReady;\n return created;\n });\n }\n try {\n instance = await pending;\n return instance;\n } catch (error) {\n // Let the next caller retry instead of caching the failure forever.\n pending = null;\n throw error;\n }\n }\n\n async function runExecute(params: {\n queryable: Queryable;\n text: string;\n values: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n execution?: Parameters<DatabaseAdapter[\"execute\"]>[0][\"execution\"];\n }): Promise<Record<string, unknown>[]> {\n const values = serializeParams({ values: params.values });\n const parsers = utcParsers ?? (await getUtcParsers());\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n const result = await runMapped(() =>\n params.queryable.query<Record<string, unknown>>(params.text, values, { parsers }),\n );\n if (params.scope !== undefined) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows, rows: result.rows.length, resultRows: result.rows }) });\n return result.rows;\n }\n\n /**\n * Raw path. Without parameters the statement goes through `exec()` (simple\n * protocol) so multi-statement DDL — what @vibeorm/migrate emits — runs as\n * one call; with parameters it goes through the extended protocol.\n */\n async function runExecuteUnsafe(params: {\n queryable: Queryable;\n text: string;\n values?: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n }): Promise<QueryResult> {\n const parsers = utcParsers ?? (await getUtcParsers());\n if (params.values === undefined) {\n const results = await runMapped(() => params.queryable.exec(params.text, { parsers }));\n if (params.scope !== undefined) {\n for (const result of results) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });\n if (results.length === 0) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect }) });\n }\n const last = results.at(-1);\n return { rows: last?.rows ?? [], affectedRows: last?.affectedRows ?? 0 };\n }\n const values = serializeParamsRaw({ values: params.values });\n const result = await runMapped(() =>\n params.queryable.query<Record<string, unknown>>(params.text, values, { parsers }),\n );\n if (params.scope !== undefined) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });\n return { rows: result.rows, affectedRows: result.affectedRows ?? 0 };\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 tx: Transaction;\n scope: SavepointScope;\n budget: TransactionBudget;\n }): DatabaseAdapter {\n const { tx, scope, budget } = params;\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation: \"before-dispatch\",\n roundTrips: ROUND_TRIPS,\n rowChanges: (): RowChangeCount => { budget.assertUsable({ stage: \"statement\" }); return readRowChanges({ scope }); },\n\n async execute(execParams) {\n return runReadExecution({ ...execParams, provider: PROVIDER, run: async () => {\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 // A server-reported failure aborts a postgres transaction: record it so\n // a callback that swallows it cannot commit nothing (PGlite's COMMIT\n // would be answered with a silent ROLLBACK).\n return runExecute({ queryable: tx, scope, ...execParams })\n .catch((error: unknown) => abortTransactionOnServerFailure({ scope, error }));\n } });\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({ queryable: tx, scope, ...execParams })\n .catch((error: unknown) => abortTransactionOnServerFailure({ scope, error }));\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(() => tx.exec(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({ tx, scope: child, budget })),\n });\n },\n\n // The open transaction owns this connection: neither call may act on it.\n // An expired handle keeps its own refusal (the budget check comes first).\n async connect(): Promise<void> {\n budget.assertUsable({ stage: \"statement\" });\n throw transactionLifecycleRefusal({ method: \"connect\" });\n },\n\n async disconnect(): Promise<void> {\n budget.assertUsable({ stage: \"statement\" });\n throw transactionLifecycleRefusal({ method: \"disconnect\" });\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n }\n\n /**\n * Terminal lifetime (EPIC A2). The cleanup runs ONCE, after every admitted\n * operation settled, and closes an instance the adapter created — never an\n * injected one. A running callback of the shared connection (this wrapper's\n * or another wrapper's over the same injected instance) cannot disconnect:\n * the close would wait behind the transaction that is waiting for it.\n */\n const lifecycle = createAdapterLifecycleGate({\n driver: \"pglite\",\n isConnectionReentrant: () => txContext.getStore()?.active === true,\n cleanup: async () => {\n if (!ownsInstance) return;\n await queue.run(async () => {\n if (instance === null) {\n pending = null;\n return;\n }\n const closing = instance;\n instance = null;\n pending = null;\n await runMapped(() => closing.close());\n });\n },\n });\n\n /** The root operations; the adapter below admits each one through {@link lifecycle}. */\n const root: Pick<Required<DatabaseAdapter>, \"withSession\" | \"execute\" | \"executeUnsafe\" | \"transaction\" | \"connect\"> = {\n async withSession({ run }) {\n assertNotInsideTransaction();\n return queue.run(async () => {\n const db = await getInstance();\n return runInCallback( () => runPinnedSession({\n run,\n execute: async (query) => (await runExecuteUnsafe({ queryable: db, ...query })).rows,\n isLost: () => db.closed,\n release: async () => {\n if (!db.closed) await runMapped(() => db.exec(\"ROLLBACK\"));\n },\n }));\n });\n },\n\n async execute(params) {\n assertNotInsideTransaction();\n // The instance is acquired INSIDE the queue (F15): capturing it first and\n // enqueueing after let a `disconnect()` issued in between overtake this\n // statement and close the instance out from under it.\n assertReadExecution({ ...params, provider: PROVIDER });\n return queue.run(async () => {\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n return runExecute({ queryable: await getInstance(), ...params });\n });\n },\n\n async executeUnsafe(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecuteUnsafe({ queryable: await getInstance(), ...params }));\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n assertNotInsideTransaction();\n // Dialect honesty (constitution rule 4, board #2): PGlite ACCEPTS\n // `SET LOCAL statement_timeout` and even reports it back via SHOW, but\n // never FIRES it — the single-threaded WASM build has no timer interrupt\n // to cancel a running statement (live-verified: `pg_sleep(2)` under a\n // 100ms timeout runs to completion). A timeout the engine cannot enforce\n // is refused at the earliest knowable moment instead of becoming a\n // silent no-op; the postgres SERVER adapters honor the same option for\n // real (SQLSTATE 57014 → VIBE_TRANSACTION, meta.reason \"timeout\").\n if (transactionOptions?.timeout !== undefined) {\n throw new VibeError({\n code: \"VIBE_UNSUPPORTED_CAPABILITY\",\n message:\n \"pglite cannot enforce a transaction timeout: the single-threaded WASM build accepts statement_timeout but never cancels a running statement — remove the option, or use a postgres server adapter where it is genuinely enforced\",\n meta: { provider: PROVIDER, feature: \"transactionTimeout\", timeout: transactionOptions.timeout },\n });\n }\n // Same earliest-moment rule for the deadline: a budget this engine\n // cannot honour is refused before the transaction is ever opened.\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n });\n const modesText = setTransactionModesStatement({\n isolationLevel: transactionOptions?.isolationLevel,\n readOnly: readOnlyRequested({ options: transactionOptions, provider: PROVIDER }),\n });\n const scope = createSavepointScope();\n\n return queue.run(async () => {\n const db = await getInstance();\n // The deadline clock starts here — after the queue turn, immediately\n // before PGlite opens the transaction.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n /**\n * EPIC T facts, in holders because PGlite's callback sets them out of\n * sight of the compiler's narrowing: how far COMMIT got (PGlite sends\n * it the moment the callback returns), and the failure the callback\n * handed PGlite to roll back.\n */\n const commit: { progress: CommitProgress } = { progress: \"not-sent\" };\n const handed: { failure?: CallbackError } = {};\n try {\n const result = await db.transaction(async (tx) => {\n try {\n // Isolation and READ ONLY (`accessMode`) in one in-process exec,\n // right after PGlite's own BEGIN.\n if (modesText !== undefined) await runMapped(() => tx.exec(modesText));\n const value = await runInCallback( () =>\n runRootScope({ scope, body: () => fn(createTransactionAdapter({ tx, scope, budget })) }),\n );\n // An expired budget must never commit. PGlite commits when this\n // callback returns, so the refusal has to be thrown from INSIDE\n // it — which makes PGlite roll back, exactly as intended.\n budget.assertUsable({ stage: \"commit\" });\n commit.progress = \"sent\";\n return value;\n } catch (error) {\n handed.failure = new CallbackError(error);\n throw handed.failure;\n }\n });\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n const failure = handed.failure;\n // No callback failure after COMMIT was sent: PGlite's COMMIT failed.\n // The engine runs in-process, so that failure IS its answer, and\n // PGlite sends no ROLLBACK after it.\n if (commit.progress === \"sent\" && failure === undefined) commit.progress = \"answered\";\n // PGlite rolls back a failed callback itself. When that ROLLBACK\n // fails, PGlite throws the ROLLBACK's error INSTEAD of the callback's:\n // the callback's own failure is rethrown anyway, as on every other\n // adapter, and the failed ROLLBACK is reported beside it.\n const rollback: RollbackOutcome = failure === undefined ? \"not-needed\" : error === failure ? \"acknowledged\" : \"failed\";\n // EPIC T: the same error, with the facts recorded beside it. The one\n // instance is kept — there is no pool to discard it from. A lost\n // savepoint stack keeps the unknown outcome (settleFailedTransaction).\n throw settleFailedTransaction({\n error: failure === undefined ? mapPgliteDriverError({ error }) : failure.inner,\n provider: PROVIDER,\n budget,\n scope,\n commit: commit.progress,\n rollback,\n connection: \"returned\",\n });\n }\n });\n },\n\n async connect(): Promise<void> {\n assertNotInsideTransaction();\n await queue.run(async () => { await getInstance(); });\n },\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation: \"before-dispatch\",\n roundTrips: ROUND_TRIPS,\n transactionOutcome: \"reported\",\n\n withSession({ run }) {\n return lifecycle.admit({ run: () => root.withSession({ run }) });\n },\n\n execute(params) {\n return lifecycle.admit({ run: () => params.execution === undefined ? root.execute(params) : runReadExecution({ ...params, provider: PROVIDER, run: () => root.execute(params) }) });\n },\n\n executeUnsafe(params) {\n return lifecycle.admit({ run: () => root.executeUnsafe(params) });\n },\n\n transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n return lifecycle.admit({ run: () => root.transaction(fn, transactionOptions) });\n },\n\n connect(): Promise<void> {\n return lifecycle.admit({ run: () => root.connect() });\n },\n\n /**\n * Terminal (EPIC A2): refuses new work at once, waits for admitted work —\n * queued statements and whole transaction/session callbacks — then closes\n * an instance the adapter created, once (inside the queue, F15). Repeated\n * calls share that completion. A new adapter is the only way to connect\n * again; with no `dataDir` that is a new, empty database.\n */\n disconnect(): Promise<void> {\n return lifecycle.disconnect();\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n}\n\n// ─── Re-exports (driver-error table, markers, helpers) ────────────\n\nexport {\n PGLITE_SQLSTATE_ERROR_CODES,\n PGLITE_SQLSTATE_REASONS,\n mapPgliteDriverError,\n pgliteErrorSqlstate,\n} from \"./errors.ts\";\nexport { PgliteArrayParam, serializeParams, serializeParamsRaw, toPgArrayLiteral } 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 {\n ISOLATION_LEVEL_SQL,\n refuseNestedTransactionOptions,\n setIsolationLevelStatement,\n} from \"./transaction-sql.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",
|
|
5
|
+
"/**\n * @vibeorm/adapter-pglite — PGlite (in-process PostgreSQL, WASM) adapter for\n * VibeORM v2.\n *\n * PGlite is a *single* embedded connection, so this adapter serializes every\n * operation through one queue — including whole transactions, which hold the\n * queue until they commit or roll back. That makes concurrent client calls safe\n * without a pool, and makes an in-memory instance an honest postgres target for\n * tests (no Docker).\n */\n\nimport { VibeError } from \"@vibeorm/schema\";\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n RowChangeCount,\n StatementEffect,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport {\n assertReadExecution,\n runReadExecution,\n abortTransactionOnServerFailure,\n observeRowChanges,\n readRowChanges,\n statementRowChanges,\n createAdapterLifecycleGate,\n runPinnedSession,\n type CommitProgress,\n transactionLifecycleRefusal,\n createSavepointScope,\n findRefusedSetting,\n readTransactionSettings,\n renderTransactionSettings,\n type RollbackOutcome,\n runNestedSavepoint,\n runRootScope,\n type SavepointScope,\n scopeTurn,\n settleFailedTransaction,\n startTransactionBudget,\n type TransactionBudget,\n transactionSettingRefusedError,\n validateTransactionDeadline,\n readOnlyRequested,\n} from \"@vibeorm/runtime/adapter-kit\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport type { Extensions, PGliteInterface, ParserOptions, Results, Transaction } from \"@electric-sql/pglite\";\nimport { mapPgliteDriverError, runMapped } from \"./errors.ts\";\nimport { PgliteArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nimport {\n createProtocolKit,\n protocolInstance,\n runProtocolExec,\n runProtocolQuery,\n runProtocolTransaction,\n type PgliteProtocolKit,\n type PgliteTransactionHandle,\n} from \"./protocol.ts\";\nimport { connectionOwnership } from \"./queue.ts\";\nimport { refuseNestedTransactionOptions, setTransactionModesStatement } from \"./transaction-sql.ts\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/** Options for {@link pgliteAdapter}. */\nexport type PgliteAdapterOptions = {\n /**\n * Directory to persist the database in. Omit for a fresh in-memory database —\n * the usual choice for tests. Prefixes PGlite understands (`idb://`,\n * `memory://`) work here too.\n */\n readonly dataDir?: string;\n /**\n * An already-created PGlite instance to run on (sharing one database across\n * adapters, or reusing a warm instance between test files). The adapter never\n * closes an instance it did not create: `disconnect()` only ends this adapter.\n */\n readonly pglite?: PGliteInterface;\n /**\n * PGlite extension bundles to load into the instance the ADAPTER creates —\n * `{ vector }` from `@electric-sql/pglite-pgvector`, `pg_trgm` from\n * `@electric-sql/pglite/contrib/pg_trgm`, … The bundles still come from\n * their own packages; this option removes the need to construct PGlite\n * yourself just to attach them. Mutually exclusive with `pglite` (that\n * instance was already constructed with its extensions) — passing both\n * throws `VIBE_CONFIG`.\n */\n readonly extensions?: Extensions;\n};\n\n// ─── Internals ────────────────────────────────────────────────────\n\n/** Anything that runs SQL: the PGlite instance itself or an open transaction. */\ntype Queryable = Pick<Transaction, \"query\" | \"exec\">;\n\n/**\n * Where a statement runs: the instance, and the open PGlite transaction when\n * there is one. The protocol path (B-03) needs the instance even inside a\n * transaction — the transaction object exposes only `query`/`exec`.\n */\ntype Connection = { readonly db: PGliteInterface; readonly tx?: PgliteTransactionHandle };\n\n// ─── UTC zone-less timestamps ─────────────────────────────────────\n\n/** `timestamp without time zone` and its array form. */\nconst TIMESTAMP_OID = 1114;\nconst TIMESTAMP_ARRAY_OID = 1115;\n\n/** PGlite's text output for a zone-less timestamp: `2026-03-08 19:38:08.538`. */\nconst ZONE_LESS_TEXT = /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,6})?$/;\n\n/**\n * Zone-less timestamp text read as **UTC** — the codec table's\n * `parseDateTimeText` rule (@vibeorm/sql) applied at the driver level, because\n * PGlite hands these columns to the ORM as `Date` objects already\n * (`wire.dateTime: \"native\"` prunes the codec decode). PGlite's own parser is\n * `new Date(text)`, which ECMA-262 reads as host-LOCAL time — while PGlite\n * SERIALIZES a `Date` parameter as UTC (`toISOString()`), so a value did not\n * even round-trip on a non-UTC host. `date` is unaffected (date-only text is\n * UTC midnight under ECMA-262 already). Anything outside the plain form keeps\n * PGlite's parser.\n */\nfunction parseTimestampAsUtc(value: string): unknown {\n if (ZONE_LESS_TEXT.test(value)) {\n const parsed = new Date(`${value.replace(\" \", \"T\").replace(/(\\.\\d{3})\\d+$/, \"$1\")}Z`);\n if (!Number.isNaN(parsed.getTime())) return parsed;\n }\n return new Date(value);\n}\n\n/** What every statement needs from the loaded module: the UTC parsers and the protocol kit (B-03; absent = `query()`). */\ntype DriverKit = { readonly parsers: ParserOptions; readonly protocol: PgliteProtocolKit | undefined };\n\nlet driverKit: DriverKit | null = null;\n\n/**\n * Per-query parsers (`QueryOptions.parsers`) — never `instance.parsers`, which\n * a caller-supplied instance shares with everyone else. The array parser is\n * PGlite's own (`types.arrayParser`), resolved lazily from the same dynamic\n * import `getInstance` uses, so the WASM module is never loaded just to build\n * this table. The protocol kit comes from the same module object.\n */\nasync function getDriverKit(): Promise<DriverKit> {\n if (driverKit !== null) return driverKit;\n const pglite = await import(\"@electric-sql/pglite\");\n const { types } = pglite;\n driverKit = {\n parsers: {\n [TIMESTAMP_OID]: parseTimestampAsUtc,\n [TIMESTAMP_ARRAY_OID]: (text: string): unknown => types.arrayParser(text, parseTimestampAsUtc, TIMESTAMP_ARRAY_OID),\n },\n protocol: createProtocolKit({ module: pglite }),\n };\n return driverKit;\n}\n\n/**\n * One extended-protocol statement: PGlite's in-process class goes through the\n * protocol path (B-03, `protocol.ts`) with `query()`'s results and errors;\n * any other instance keeps `query()`.\n */\nfunction runQuery(params: {\n connection: Connection;\n kit: DriverKit;\n text: string;\n values: unknown[];\n}): Promise<Results<Record<string, unknown>>> {\n const { connection, kit, text, values } = params;\n const protocol = kit.protocol;\n const db = protocol === undefined ? undefined : protocolInstance({ kit: protocol, instance: connection.db });\n if (protocol === undefined || db === undefined) return (connection.tx ?? connection.db).query<Record<string, unknown>>(text, values, { parsers: kit.parsers });\n return runProtocolQuery({ kit: protocol, db, transaction: connection.tx, text, values, parsers: kit.parsers });\n}\n\n/**\n * Wrapper that marks an error as \"thrown by the user's transaction callback\",\n * so it can be rethrown untouched instead of being mapped as a driver failure\n * after PGlite has passed it back out of `transaction()`.\n */\nclass CallbackError extends Error {\n readonly inner: unknown;\n\n constructor(inner: unknown) {\n super(\"vibeorm: transaction callback failed\");\n this.name = \"CallbackError\";\n this.inner = inner;\n }\n}\n\nconst DIALECT: Dialect = \"postgres\";\nconst PROVIDER: Provider = \"pglite\";\n\n/**\n * pglite wire (board #30, live-verified on both execute paths): timestamptz →\n * `Date`, json/jsonb → parsed values, int4/float8 → numbers. (int8 arrives as\n * a native `bigint` here — the BigInt codec passes it through; not declared.)\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"native\", json: \"parsed\", numbers: \"native\" } as const);\n\n/**\n * PGlite runs inside this process (WASM): a round trip crosses no network, so\n * the runtime keeps its multi-statement paths instead of the one-statement\n * folds. The transaction facades carry the same declaration.\n */\nconst ROUND_TRIPS: NonNullable<DatabaseAdapter[\"roundTrips\"]> = \"in-process\";\n\n/** `$all`'s snapshot: a read-only REPEATABLE READ transaction (`SET TRANSACTION …, READ ONLY` after PGlite's BEGIN). */\nconst READ_SNAPSHOT: NonNullable<DatabaseAdapter[\"readSnapshot\"]> = Object.freeze({ isolationLevel: \"RepeatableRead\", accessMode: \"readOnly\" } as const);\n\n/** One in-process engine behind a queue: statements run one after another, and a round trip costs nothing. */\nconst PIPELINING: NonNullable<DatabaseAdapter[\"pipelining\"]> = \"ordered\";\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: false` — one in-process WASM instance behind a queue.\n * There is no pool to exhaust.\n * - `statementTimeout: \"unsupported\"` — **PGlite ACCEPTING `statement_timeout`\n * is not support.** The single-threaded WASM build reports the setting back\n * via SHOW and never fires it (board #2, live-verified: `pg_sleep(2)` under a\n * 100 ms timeout runs to completion), which is why the `timeout` option is\n * refused outright below.\n * - `transactionDeadline: \"between-statements\"` — the budget is checked before\n * each statement and before the commit, which PGlite CAN honour because those\n * checks happen in JavaScript. It is not cancellation, for the same\n * single-threaded reason, so `\"cancel-running-statements\"` is refused.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: false,\n statementTimeout: \"unsupported\",\n transactionDeadline: \"between-statements\",\n accessModes: Object.freeze([\"readOnly\"] as const),\n} as const);\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\n/**\n * Create a VibeORM adapter backed by PGlite.\n *\n * @example In-memory database (tests)\n * ```ts\n * const adapter = pgliteAdapter();\n * await adapter.executeUnsafe({ text: `CREATE TABLE \"User\" (\"id\" serial primary key)` });\n * ```\n *\n * @example Persistent database, or a shared instance\n * ```ts\n * const adapter = pgliteAdapter({ dataDir: \"./.pglite\" });\n * const shared = pgliteAdapter({ pglite: existingInstance });\n * ```\n *\n * `disconnect()` ends the adapter for good: it waits for admitted work, closes\n * an instance the adapter created (never a provided `pglite`), and every later\n * call refuses with `VIBE_ADAPTER_CLOSED`. Create a new adapter to connect\n * again; with no `dataDir` that is a new, empty in-memory database.\n *\n * @example Bundled extensions (pgvector) without hand-constructing PGlite\n * ```ts\n * import { vector } from \"@electric-sql/pglite-pgvector\";\n * const adapter = pgliteAdapter({ dataDir: \"./.pglite\", extensions: { vector } });\n * ```\n */\nexport function pgliteAdapter(options?: PgliteAdapterOptions): DatabaseAdapter {\n if (options?.pglite !== undefined && options.extensions !== undefined) {\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"pgliteAdapter: `extensions` applies to an instance the adapter CREATES — a provided `pglite` instance was already constructed with its own extensions; pass one or the other\",\n meta: { provider: PROVIDER },\n });\n }\n const { queue, txContext, runInCallback } = connectionOwnership({ database: options?.pglite });\n const ownsInstance = options?.pglite === undefined;\n\n /**\n * Deadlock guard (review F6): PGlite is a single connection, and a whole\n * transaction holds the operation queue — so a ROOT-adapter call issued from\n * inside a `transaction()` callback would queue behind the very transaction\n * it is running in and hang forever. The callback runs inside this context;\n * root entry points refuse instead of deadlocking. The tx-bound adapter\n * bypasses the queue and stays fully usable.\n */\n function assertNotInsideTransaction(): void {\n if (txContext.getStore()?.active === 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 instance: PGliteInterface | null = options?.pglite ?? null;\n let pending: Promise<PGliteInterface> | null = null;\n\n async function getInstance(): Promise<PGliteInterface> {\n if (instance !== null) return instance;\n if (pending === null) {\n pending = runMapped(async () => {\n const { PGlite } = await import(\"@electric-sql/pglite\");\n const created = new PGlite({\n ...(options?.dataDir === undefined ? {} : { dataDir: options.dataDir }),\n ...(options?.extensions === undefined ? {} : { extensions: options.extensions }),\n });\n await created.waitReady;\n return created;\n });\n }\n try {\n instance = await pending;\n return instance;\n } catch (error) {\n // Let the next caller retry instead of caching the failure forever.\n pending = null;\n throw error;\n }\n }\n\n async function runExecute(params: {\n connection: Connection;\n text: string;\n values: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n execution?: Parameters<DatabaseAdapter[\"execute\"]>[0][\"execution\"];\n }): Promise<Record<string, unknown>[]> {\n const values = serializeParams({ values: params.values });\n const kit = driverKit ?? (await getDriverKit());\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n const result = await runMapped(() => runQuery({ connection: params.connection, kit, text: params.text, values }));\n if (params.scope !== undefined) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows, rows: result.rows.length, resultRows: result.rows }) });\n return result.rows;\n }\n\n /**\n * Raw path. Without parameters the statement goes through `exec()` (simple\n * protocol) so multi-statement DDL — what @vibeorm/migrate emits — runs as\n * one call; with parameters it goes through the extended protocol.\n */\n async function runExecuteUnsafe(params: {\n connection: Connection;\n text: string;\n values?: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n }): Promise<QueryResult> {\n const kit = driverKit ?? (await getDriverKit());\n if (params.values === undefined) {\n const queryable: Queryable = params.connection.tx ?? params.connection.db;\n const results = await runMapped(() => queryable.exec(params.text, { parsers: kit.parsers }));\n if (params.scope !== undefined) {\n for (const result of results) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });\n if (results.length === 0) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect }) });\n }\n const last = results.at(-1);\n return { rows: last?.rows ?? [], affectedRows: last?.affectedRows ?? 0 };\n }\n const values = serializeParamsRaw({ values: params.values });\n const result = await runMapped(() => runQuery({ connection: params.connection, kit, text: params.text, values }));\n if (params.scope !== undefined) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.affectedRows }) });\n return { rows: result.rows, affectedRows: result.affectedRows ?? 0 };\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: PGliteInterface;\n tx: PgliteTransactionHandle;\n scope: SavepointScope;\n budget: TransactionBudget;\n }): DatabaseAdapter {\n const { db, tx, scope, budget } = params;\n const connection: Connection = { db, tx };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation: \"before-dispatch\",\n roundTrips: ROUND_TRIPS,\n readSnapshot: READ_SNAPSHOT,\n pipelining: PIPELINING,\n rowChanges: (): RowChangeCount => { budget.assertUsable({ stage: \"statement\" }); return readRowChanges({ scope }); },\n\n async execute(execParams) {\n return runReadExecution({ ...execParams, provider: PROVIDER, run: async () => {\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 // A server-reported failure aborts a postgres transaction: record it so\n // a callback that swallows it cannot commit nothing (PGlite's COMMIT\n // would be answered with a silent ROLLBACK).\n return runExecute({ connection, scope, ...execParams })\n .catch((error: unknown) => abortTransactionOnServerFailure({ scope, error }));\n } });\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({ connection, scope, ...execParams })\n .catch((error: unknown) => abortTransactionOnServerFailure({ scope, error }));\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(() => tx.exec(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, tx, scope: child, budget })),\n });\n },\n\n // The open transaction owns this connection: neither call may act on it.\n // An expired handle keeps its own refusal (the budget check comes first).\n async connect(): Promise<void> {\n budget.assertUsable({ stage: \"statement\" });\n throw transactionLifecycleRefusal({ method: \"connect\" });\n },\n\n async disconnect(): Promise<void> {\n budget.assertUsable({ stage: \"statement\" });\n throw transactionLifecycleRefusal({ method: \"disconnect\" });\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n }\n\n /**\n * Terminal lifetime (EPIC A2). The cleanup runs ONCE, after every admitted\n * operation settled, and closes an instance the adapter created — never an\n * injected one. A running callback of the shared connection (this wrapper's\n * or another wrapper's over the same injected instance) cannot disconnect:\n * the close would wait behind the transaction that is waiting for it.\n */\n const lifecycle = createAdapterLifecycleGate({\n driver: \"pglite\",\n isConnectionReentrant: () => txContext.getStore()?.active === true,\n cleanup: async () => {\n if (!ownsInstance) return;\n await queue.run(async () => {\n if (instance === null) {\n pending = null;\n return;\n }\n const closing = instance;\n instance = null;\n pending = null;\n await runMapped(() => closing.close());\n });\n },\n });\n\n /** The root operations; the adapter below admits each one through {@link lifecycle}. */\n const root: Pick<Required<DatabaseAdapter>, \"withSession\" | \"execute\" | \"executeUnsafe\" | \"transaction\" | \"connect\"> = {\n async withSession({ run }) {\n assertNotInsideTransaction();\n return queue.run(async () => {\n const db = await getInstance();\n return runInCallback( () => runPinnedSession({\n run,\n execute: async (query) => (await runExecuteUnsafe({ connection: { db }, ...query })).rows,\n isLost: () => db.closed,\n release: async () => {\n if (db.closed) return;\n // AW-02: through the protocol seam when the instance passes B-03's gate, else PGlite's `exec()`.\n const kit = driverKit ?? (await getDriverKit());\n const protocolDb = kit.protocol === undefined ? undefined : protocolInstance({ kit: kit.protocol, instance: db });\n await runMapped(() => (kit.protocol === undefined || protocolDb === undefined ? db.exec(\"ROLLBACK\") : runProtocolExec({ kit: kit.protocol, db: protocolDb, text: \"ROLLBACK\" })));\n },\n }));\n });\n },\n\n async execute(params) {\n assertNotInsideTransaction();\n // The instance is acquired INSIDE the queue (F15): capturing it first and\n // enqueueing after let a `disconnect()` issued in between overtake this\n // statement and close the instance out from under it.\n assertReadExecution({ ...params, provider: PROVIDER });\n return queue.run(async () => {\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n return runExecute({ connection: { db: await getInstance() }, ...params });\n });\n },\n\n async executeUnsafe(params) {\n assertNotInsideTransaction();\n return queue.run(async () => runExecuteUnsafe({ connection: { db: await getInstance() }, ...params }));\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n assertNotInsideTransaction();\n // Dialect honesty (constitution rule 4, board #2): PGlite ACCEPTS\n // `SET LOCAL statement_timeout` and even reports it back via SHOW, but\n // never FIRES it — the single-threaded WASM build has no timer interrupt\n // to cancel a running statement (live-verified: `pg_sleep(2)` under a\n // 100ms timeout runs to completion). A timeout the engine cannot enforce\n // is refused at the earliest knowable moment instead of becoming a\n // silent no-op; the postgres SERVER adapters honor the same option for\n // real (SQLSTATE 57014 → VIBE_TRANSACTION, meta.reason \"timeout\").\n if (transactionOptions?.timeout !== undefined) {\n throw new VibeError({\n code: \"VIBE_UNSUPPORTED_CAPABILITY\",\n message:\n \"pglite cannot enforce a transaction timeout: the single-threaded WASM build accepts statement_timeout but never cancels a running statement — remove the option, or use a postgres server adapter where it is genuinely enforced\",\n meta: { provider: PROVIDER, feature: \"transactionTimeout\", timeout: transactionOptions.timeout },\n });\n }\n // Same earliest-moment rule for the deadline: a budget this engine\n // cannot honour is refused before the transaction is ever opened.\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n settings: \"transaction-local\",\n });\n const modesText = setTransactionModesStatement({\n isolationLevel: transactionOptions?.isolationLevel,\n readOnly: readOnlyRequested({ options: transactionOptions, provider: PROVIDER }),\n });\n // TU-02: transaction settings, validated and rendered before the queue\n // turn. None = today's path, byte for byte.\n const settings = readTransactionSettings({ settings: transactionOptions?.settings, provider: PROVIDER });\n const settingsStatement = settings.length === 0 ? undefined : renderTransactionSettings({ settings });\n const scope = createSavepointScope();\n\n return queue.run(async () => {\n const db = await getInstance();\n // AW-02: an instance that passes B-03's protocol gate runs the\n // transaction's control statements through the protocol seam\n // (`runProtocolTransaction`); any other keeps PGlite's `transaction()`.\n const kit = driverKit ?? (await getDriverKit());\n const protocolDb = kit.protocol === undefined ? undefined : protocolInstance({ kit: kit.protocol, instance: db });\n const protocolKit = kit.protocol;\n const openTransaction = <R>(body: (tx: PgliteTransactionHandle) => Promise<R>): Promise<R> =>\n protocolKit === undefined || protocolDb === undefined ? db.transaction(body) : runProtocolTransaction({ kit: protocolKit, db: protocolDb, body });\n // The deadline clock starts here — after the queue turn, immediately\n // before the transaction opens.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n /**\n * EPIC T facts, in holders because PGlite's callback sets them out of\n * sight of the compiler's narrowing: how far COMMIT got (PGlite sends\n * it the moment the callback returns), and the failure the callback\n * handed PGlite to roll back.\n */\n const commit: { progress: CommitProgress } = { progress: \"not-sent\" };\n const handed: { failure?: CallbackError } = {};\n /** TU-02: the mapped error of a settings statement the engine refused (it is also the callback failure). */\n const refused: { cause?: VibeError } = {};\n try {\n const result = await openTransaction(async (tx) => {\n try {\n // Isolation and READ ONLY (`accessMode`) in one in-process exec,\n // right after BEGIN.\n if (modesText !== undefined) await runMapped(() => tx.exec(modesText));\n // TU-02: the ONE bound settings statement, before the callback: a\n // refusal rolls back and the callback never runs.\n if (settingsStatement !== undefined) {\n await runMapped(() => tx.query(settingsStatement.text, [...settingsStatement.values])).then(undefined, (error: unknown) => {\n if (error instanceof VibeError && typeof error.meta.sqlstate === \"string\") refused.cause = error;\n throw error;\n });\n }\n const value = await runInCallback( () =>\n runRootScope({ scope, body: () => fn(createTransactionAdapter({ db, tx, scope, budget })) }),\n );\n // An expired budget must never commit. PGlite commits when this\n // callback returns, so the refusal has to be thrown from INSIDE\n // it — which makes PGlite roll back, exactly as intended.\n budget.assertUsable({ stage: \"commit\" });\n commit.progress = \"sent\";\n return value;\n } catch (error) {\n handed.failure = new CallbackError(error);\n throw handed.failure;\n }\n });\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n const failure = handed.failure;\n // No callback failure after COMMIT was sent: PGlite's COMMIT failed.\n // The engine runs in-process, so that failure IS its answer, and\n // PGlite sends no ROLLBACK after it.\n if (commit.progress === \"sent\" && failure === undefined) commit.progress = \"answered\";\n // PGlite rolls back a failed callback itself. When that ROLLBACK\n // fails, PGlite throws the ROLLBACK's error INSTEAD of the callback's:\n // the callback's own failure is rethrown anyway, as on every other\n // adapter, and the failed ROLLBACK is reported beside it.\n const rollback: RollbackOutcome = failure === undefined ? \"not-needed\" : error === failure ? \"acknowledged\" : \"failed\";\n // TU-02: attribute the opening failure from its original diagnostic,\n // never by replaying settings after rollback.\n const settingsCause = refused.cause !== undefined && failure?.inner === refused.cause ? refused.cause : undefined;\n const settingsError = settingsCause === undefined ? undefined : transactionSettingRefusedError({\n provider: PROVIDER,\n settings,\n setting: findRefusedSetting({ settings, cause: settingsCause }),\n cause: settingsCause,\n });\n // EPIC T: the same error, with the facts recorded beside it. The one\n // instance is kept — there is no pool to discard it from. A lost\n // savepoint stack keeps the unknown outcome (settleFailedTransaction).\n throw settleFailedTransaction({\n error: failure === undefined ? mapPgliteDriverError({ error }) : settingsError ?? failure.inner,\n provider: PROVIDER,\n budget,\n scope,\n commit: commit.progress,\n rollback,\n connection: \"returned\",\n });\n }\n });\n },\n\n async connect(): Promise<void> {\n assertNotInsideTransaction();\n await queue.run(async () => { await getInstance(); });\n },\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation: \"before-dispatch\",\n roundTrips: ROUND_TRIPS,\n readSnapshot: READ_SNAPSHOT,\n pipelining: PIPELINING,\n transactionOutcome: \"reported\",\n transactionSettings: \"transaction-local\",\n\n withSession({ run }) {\n return lifecycle.admit({ run: () => root.withSession({ run }) });\n },\n\n execute(params) {\n return lifecycle.admit({ run: () => params.execution === undefined ? root.execute(params) : runReadExecution({ ...params, provider: PROVIDER, run: () => root.execute(params) }) });\n },\n\n executeUnsafe(params) {\n return lifecycle.admit({ run: () => root.executeUnsafe(params) });\n },\n\n transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n return lifecycle.admit({ run: () => root.transaction(fn, transactionOptions) });\n },\n\n connect(): Promise<void> {\n return lifecycle.admit({ run: () => root.connect() });\n },\n\n /**\n * Terminal (EPIC A2): refuses new work at once, waits for admitted work —\n * queued statements and whole transaction/session callbacks — then closes\n * an instance the adapter created, once (inside the queue, F15). Repeated\n * calls share that completion. A new adapter is the only way to connect\n * again; with no `dataDir` that is a new, empty database.\n */\n disconnect(): Promise<void> {\n return lifecycle.disconnect();\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n}\n\n// ─── Re-exports (driver-error table, markers, helpers) ────────────\n\nexport {\n PGLITE_SQLSTATE_ERROR_CODES,\n PGLITE_SQLSTATE_REASONS,\n mapPgliteDriverError,\n pgliteErrorSqlstate,\n} from \"./errors.ts\";\nexport { PgliteArrayParam, serializeParams, serializeParamsRaw, toPgArrayLiteral } 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 {\n ISOLATION_LEVEL_SQL,\n refuseNestedTransactionOptions,\n setIsolationLevelStatement,\n} from \"./transaction-sql.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 PGlite.\n *\n * PGlite runs the real PostgreSQL error protocol in-process, so its errors\n * carry the same SQLSTATE (`code`) plus `constraint` / `table` / `column` as a\n * server would. Constitution rule 5: only `VibeError` escapes the adapter.\n */\n\nimport { VibeError, type VibeErrorCode } from \"@vibeorm/schema\";\n\n// ─── SQLSTATE table ───────────────────────────────────────────────\n\n/**\n * PostgreSQL SQLSTATE → stable VibeError code. Frozen and mirrored by tests;\n * anything absent from this table maps to `VIBE_ADAPTER`.\n */\nexport const PGLITE_SQLSTATE_ERROR_CODES: Readonly<Record<string, VibeErrorCode>> = Object.freeze({\n /** exclusion_violation */\n \"23P01\": \"VIBE_EXCLUSION_VIOLATION\",\n /** unique_violation */\n \"23505\": \"VIBE_UNIQUE_VIOLATION\",\n /** foreign_key_violation */\n \"23503\": \"VIBE_FK_VIOLATION\",\n /** restrict_violation — PostgreSQL 18+ ON DELETE/UPDATE RESTRICT */\n \"23001\": \"VIBE_FK_VIOLATION\",\n /** not_null_violation */\n \"23502\": \"VIBE_VALIDATION\",\n /** check_violation */\n \"23514\": \"VIBE_VALIDATION\",\n /**\n * invalid_text_representation — a value the server cannot parse into the\n * column's type (out-of-range enum value, `\"abc\"` for an integer). Bad input,\n * so the same code sqlite reports for its enum CHECK violation.\n */\n \"22P02\": \"VIBE_VALIDATION\",\n /** Data exceptions the same bad input raises (mirrors adapter-pg): too long, out of range, bad datetime, datetime overflow. */\n \"22001\": \"VIBE_VALIDATION\",\n \"22003\": \"VIBE_VALIDATION\",\n \"22007\": \"VIBE_VALIDATION\",\n \"22008\": \"VIBE_VALIDATION\",\n /**\n * insufficient_privilege — a denied table/column privilege, and the SQLSTATE a\n * native row-level-security policy raises when its WITH CHECK refuses a write.\n * Callers identify it by this code, never by message text.\n */\n \"42501\": \"VIBE_PERMISSION_DENIED\",\n /** serialization_failure */\n \"40001\": \"VIBE_TRANSACTION\",\n /**\n * lock_not_available. PGlite has ONE session, so nothing can contend with the\n * caller — the mapping exists for parity with the server adapters (identical\n * PostgreSQL SQLSTATEs), not because this engine is expected to raise it.\n */\n \"55P03\": \"VIBE_LOCK_NOT_AVAILABLE\",\n /** deadlock_detected */\n \"40P01\": \"VIBE_TRANSACTION\",\n /** query_canceled — statement_timeout fired */\n \"57014\": \"VIBE_TRANSACTION\",\n /** read_only_sql_transaction — a write inside a READ ONLY transaction (mirrors adapter-pg). */\n \"25006\": \"VIBE_TRANSACTION\",\n});\n\n/** SQLSTATEs that additionally explain themselves through `meta.reason`. */\nexport const PGLITE_SQLSTATE_REASONS: Readonly<Record<string, string>> = Object.freeze({\n \"23P01\": \"exclusion-violation\",\n \"57014\": \"timeout\",\n \"55P03\": \"lock-not-available\",\n \"40P01\": \"deadlock\",\n \"40001\": \"serialization-failure\",\n \"25006\": \"read-only-transaction\",\n});\n\n// ─── Reading driver errors ────────────────────────────────────────\n\n/** A PostgreSQL SQLSTATE is exactly five digits/uppercase letters (e.g. `40P01`). */\nconst SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;\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 SQLSTATE from a PGlite error. Failures raised by PGlite itself\n * (closed database, WASM init) carry no SQLSTATE and yield `undefined`.\n */\nexport function pgliteErrorSqlstate(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 && SQLSTATE_PATTERN.test(code) ? code : undefined;\n}\n\n// ─── Mapping ──────────────────────────────────────────────────────\n\n/**\n * Map any error thrown by PGlite 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.\n */\nexport function mapPgliteDriverError(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 sqlstate = pgliteErrorSqlstate({ error });\n const code: VibeErrorCode =\n (sqlstate === undefined ? undefined : PGLITE_SQLSTATE_ERROR_CODES[sqlstate]) ?? \"VIBE_ADAPTER\";\n\n const meta: Record<string, unknown> = {};\n if (sqlstate !== undefined) meta.sqlstate = sqlstate;\n const constraint = readStringField({ source, key: \"constraint\" });\n if (constraint !== undefined) meta.constraint = constraint;\n const table = readStringField({ source, key: \"table\" });\n if (table !== undefined) meta.table = table;\n const column = readStringField({ source, key: \"column\" });\n if (column !== undefined) meta.column = column;\n const reason = sqlstate === undefined ? undefined : PGLITE_SQLSTATE_REASONS[sqlstate];\n if (reason !== undefined) meta.reason = reason;\n\n const message =\n readStringField({ source, key: \"message\" }) ??\n (typeof error === \"string\" ? error : \"unknown pglite driver error\");\n\n return new VibeError({ code, message, meta, cause: error });\n}\n\n/** Run a driver call, mapping anything it throws onto a `VibeError`. */\nexport async function runMapped<T>(fn: () => Promise<T>): Promise<T> {\n try {\n return await fn();\n } catch (error) {\n throw mapPgliteDriverError({ error });\n }\n}\n",
|
|
7
7
|
"/**\n * Parameter serialization for PGlite.\n *\n * PGlite takes JS arrays natively for the array types it knows, but it picks a\n * parameter serializer from the BACKEND-INFERRED type OID and falls back to\n * `String(value)` for anything else — so a scalar list bound against a column\n * of a custom type (a native `enum`, i.e. every `Role[]` / `role IN (…)` query)\n * reached the server as `ADMIN,MODERATOR` and was rejected with `malformed\n * array literal`. ORM-path scalar lists are therefore rendered as PostgreSQL\n * array literals here, exactly like the bun:sql adapter does\n * (`@vibeorm/adapter-bun`'s `toPgArrayLiteral`); the server then coerces the\n * literal to whichever array type the statement needs (`text[]`, `int[]`,\n * `timestamptz[]`, `\"Role\"[]`).\n *\n * The ORM path and the raw path still split the same way (LEARNINGS.md): the\n * ORM path cannot tell a `jsonb` value that happens to be an array from a\n * scalar-list parameter, so it JSON-stringifies plain arrays and only treats\n * values tagged with {@link PgliteArrayParam} as scalar lists; the raw path\n * passes plain JS arrays straight through to the driver.\n */\n\nimport { Buffer } from \"node:buffer\";\nimport { VibeError } from \"@vibeorm/schema\";\n\n// ─── Scalar-list marker ───────────────────────────────────────────\n\n/**\n * Opaque token returned by `pgliteAdapter().formatArrayParam` marking a value\n * as a PostgreSQL array parameter (rather than a JSON value that happens to be\n * an array). The wrapped array is rendered as an array literal on the way out.\n */\nexport class PgliteArrayParam {\n /** The untouched scalar-list values. */\n readonly values: unknown[];\n\n constructor(values: unknown[]) {\n this.values = values;\n }\n}\n\n// ─── PostgreSQL array literals ────────────────────────────────────\n\n/** Quote and escape one array element: `\"` and `\\` are the special characters. */\nfunction quoteElement(value: string): string {\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * Render a JS array as a PostgreSQL array literal string — the same element\n * rules as `@vibeorm/adapter-bun`'s `toPgArrayLiteral` (kept per-adapter: an\n * array literal is driver transport, not SQL text):\n * - `null` / `undefined` → unquoted `NULL`\n * - `number` / `bigint` / `boolean` → unquoted primitive\n * - `Date` → quoted ISO-8601 (PostgreSQL cannot parse `String(date)`)\n * - `string` → quoted, with `\"` and `\\` escaped (commas, braces and quotes\n * inside an element are safe)\n * - any other object, **including a nested array** → quoted `JSON.stringify`,\n * which is what a `Json[]` scalar list needs\n * - `Uint8Array` (including `Buffer`) → quoted PostgreSQL bytea hex\n * - other binary views / `ArrayBuffer` → unsupported, throws\n *\n * @throws VibeError `VIBE_VALIDATION` for unsupported binary elements.\n */\nexport function toPgArrayLiteral(values: readonly unknown[]): string {\n const elements = values.map((value) => {\n if (value === null || value === undefined) return \"NULL\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return quoteElement(value.toISOString());\n if (value instanceof Uint8Array) {\n return quoteElement(`\\\\x${Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString(\"hex\")}`);\n }\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"binary values are not supported inside a postgres array literal\",\n meta: { driver: \"pglite\" },\n });\n }\n if (typeof value === \"string\") return quoteElement(value);\n if (typeof value === \"object\") return quoteElement(JSON.stringify(value));\n return quoteElement(String(value));\n });\n return `{${elements.join(\",\")}}`;\n}\n\n// ─── Serialization ────────────────────────────────────────────────\n\n/** True for values PGlite must receive verbatim (dates, binary, primitives, null). */\nfunction isDriverNativeValue(value: unknown): boolean {\n return (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n value instanceof Date ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n );\n}\n\n/**\n * ORM hot path: render scalar-list markers as array literals, JSON-stringify\n * plain objects **and** plain arrays (they can only be `json`/`jsonb` values at\n * this point).\n */\nexport function serializeParams(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgliteArrayParam) return toPgArrayLiteral(value.values);\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\n}\n\n/**\n * Raw path (`$queryRaw*` / `$executeRaw*` / DDL): identical to\n * {@link serializeParams} except that plain JS arrays pass straight through, so\n * `db.$queryRawUnsafe('… WHERE \"id\" = ANY($1)', [1, 2, 3])` matches rows.\n */\nexport function serializeParamsRaw(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgliteArrayParam) return toPgArrayLiteral(value.values);\n if (Array.isArray(value)) return value;\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\n}\n",
|
|
8
|
+
"/**\n * PGlite statements on the wire protocol directly (performance audit round 2,\n * finding B-03).\n *\n * `PGlite.query()` cost ~44 µs for a one-row primary-key read, and most of it\n * is not the engine. Its result step (`parse.parseResults`) copies the\n * instance's WHOLE parser table — every array type PGlite registered at start,\n * `{ ...db.parsers, ...options.parsers }` — on every call (~23 µs), and it\n * crosses into the engine six times (Parse, Describe statement, Bind, Describe\n * portal, Execute, Sync). This module runs the SAME protocol messages through\n * the transport `query()` itself uses, `execProtocolStream` (one\n * `execProtocolRaw` call into the engine each, with PGlite's own message\n * parser, error, notice and notification handling):\n *\n * - no value needs a server-described type (no values, or only null/undefined):\n * ONE call `[Parse, Bind, Describe portal, Execute, Sync]`;\n * - otherwise TWO calls `[Parse, Describe statement]` →\n * `[Bind, Describe portal, Execute, Sync]`, because PGlite serializes every\n * value by the type the server infers for its parameter (`db.serializers`\n * keyed by that OID, `toString()` without one). Serializing by JavaScript\n * type instead would differ for a string bound to a `boolean` or `bytea`\n * parameter, a `Date` bound to `time`/`jsonb`, a `bigint` bound to `jsonb`, …\n *\n * and hands the result messages to PGlite's own `parse.parseResults` with only\n * the parsers of the columns that came back — the lookup `parseType` performs\n * reads no other key, so rows, values and `affectedRows` are those of the\n * installed PGlite version by construction. Everything else `query()` does is\n * kept: the ready check, the transaction mutex (root statements never\n * interleave into another caller's open transaction on a shared instance) and\n * the query mutex, the debug log line, the blob bookkeeping, the Sync after a\n * failure, the error decoration (`query` / `params` / `queryOptions`) and the\n * file-system sync after a root statement. Measured (one-row read, median of 7\n * interleaved batches of 400): `query()` 44.0 µs → this path 18.7 µs with\n * values, 16.0 µs without.\n *\n * Gate: by capability, not by version — PGlite's in-process class from the\n * module the adapter loaded, when the module and the class expose every member\n * this path calls. `query()`'s `#runQuery` source this mirrors is the same in\n * 0.3.16, 0.4.0, 0.5.4 and 0.5.9 (0.3.16 already has `execProtocolStream`, so\n * it takes this path too; review 2 of the round-2 fix campaign read its source\n * map). Anything else — `PGliteWorker`, another copy of the module, an early\n * 0.3.x without `execProtocolStream`, a module without the protocol helpers —\n * keeps `query()`.\n *\n * Transactions (performance round 3, AW-02): under the same gate the adapter\n * also owns BEGIN / COMMIT / ROLLBACK, SAVEPOINT / RELEASE / ROLLBACK TO and\n * `SET TRANSACTION` ({@link runProtocolTransaction}). PGlite's `transaction()`\n * and `exec()` sent each as a simple-query `Q` message plus a `Sync`, then ran\n * the result step over the whole parser table (≈ 33 µs per control statement);\n * the same two messages now go to the engine in ONE `execProtocolStream` call,\n * with `transaction()`'s mutex, closed-handle refusal, commit/rollback rule,\n * error decoration and file-system sync points kept.\n */\n\nimport type { PGlite, ParserOptions, QueryOptions, Results, Transaction } from \"@electric-sql/pglite\";\n\n// ─── The module surface the path uses ─────────────────────────────\n\n/** The `@electric-sql/pglite` module, as the adapter loads it. */\nexport type PgliteModule = typeof import(\"@electric-sql/pglite\");\n\n/** One wire message, as PGlite's protocol parser hands it back. */\ntype BackendMessage = Awaited<ReturnType<PGlite[\"execProtocolStream\"]>>[number];\n\n/** A PGlite result-value parser (`types.Parser`). */\ntype ValueParser = PGlite[\"parsers\"][number];\n\n/**\n * What the protocol path needs from the module the adapter loaded — taken from\n * that SAME module object, so the class check and the message helpers belong\n * to the PGlite copy that created the instance.\n */\nexport type PgliteProtocolKit = {\n readonly PGlite: PgliteModule[\"PGlite\"];\n readonly serialize: PgliteModule[\"protocol\"][\"serialize\"];\n readonly parseResults: PgliteModule[\"parse\"][\"parseResults\"];\n readonly parseDescribeStatementResults: PgliteModule[\"parse\"][\"parseDescribeStatementResults\"];\n readonly DatabaseError: PgliteModule[\"messages\"][\"DatabaseError\"];\n};\n\n/**\n * Build the kit from the loaded module; `undefined` (every statement keeps\n * `query()`) when the module lacks one of the helpers — an older PGlite, or a\n * test double standing in for the module.\n */\nexport function createProtocolKit(params: { module: PgliteModule }): PgliteProtocolKit | undefined {\n const loose = params.module as unknown as {\n readonly PGlite?: unknown;\n readonly protocol?: { readonly serialize?: unknown };\n readonly parse?: { readonly parseResults?: unknown; readonly parseDescribeStatementResults?: unknown };\n readonly messages?: { readonly DatabaseError?: unknown };\n };\n if (\n typeof loose.PGlite !== \"function\" ||\n typeof loose.protocol?.serialize !== \"object\" ||\n typeof loose.parse?.parseResults !== \"function\" ||\n typeof loose.parse.parseDescribeStatementResults !== \"function\" ||\n typeof loose.messages?.DatabaseError !== \"function\"\n ) {\n return undefined;\n }\n const { module } = params;\n return Object.freeze({\n PGlite: module.PGlite,\n serialize: module.protocol.serialize,\n parseResults: module.parse.parseResults,\n parseDescribeStatementResults: module.parse.parseDescribeStatementResults,\n DatabaseError: module.messages.DatabaseError,\n });\n}\n\n// ─── Which instances the path drives ──────────────────────────────\n\n/** The members of PGlite's in-process class the path calls — the same ones `query()` uses internally. */\nconst PROTOCOL_MEMBERS = Object.freeze([\n \"execProtocolStream\",\n \"_checkReady\",\n \"_runExclusiveTransaction\",\n \"runExclusive\",\n \"syncToFs\",\n \"_handleBlob\",\n \"_cleanupBlob\",\n \"_getWrittenBlob\",\n] as const);\n\n/** Per-instance verdict, computed once (`null` = keep `query()`). */\nconst PROTOCOL_INSTANCES: WeakMap<object, PGlite | null> = new WeakMap();\n\nfunction isProtocolCapable(params: { kit: PgliteProtocolKit; instance: object }): boolean {\n if (!(params.instance instanceof params.kit.PGlite)) return false;\n const members = params.instance as unknown as Record<string, unknown>;\n for (const name of PROTOCOL_MEMBERS) {\n if (typeof members[name] !== \"function\") return false;\n }\n return typeof members.parsers === \"object\" && members.parsers !== null && typeof members.serializers === \"object\" && members.serializers !== null;\n}\n\n/**\n * The instance as PGlite's in-process class when the protocol path can drive\n * it; `undefined` keeps `query()` (see the module comment for the gate).\n */\nexport function protocolInstance(params: { kit: PgliteProtocolKit; instance: object }): PGlite | undefined {\n const known = PROTOCOL_INSTANCES.get(params.instance);\n if (known !== undefined) return known ?? undefined;\n const verdict = isProtocolCapable(params) ? (params.instance as PGlite) : null;\n PROTOCOL_INSTANCES.set(params.instance, verdict);\n return verdict ?? undefined;\n}\n\n// ─── Messages ─────────────────────────────────────────────────────\n\n/** Execution never syncs the file system mid-statement — `query()` syncs once, at the end. */\nconst NO_FS_SYNC = Object.freeze({ syncToFs: false });\n\n/** Concatenate protocol messages into one engine call. */\nfunction joinMessages(parts: readonly Uint8Array[]): Uint8Array {\n let size = 0;\n for (const part of parts) size += part.length;\n const joined = new Uint8Array(size);\n let offset = 0;\n for (const part of parts) {\n joined.set(part, offset);\n offset += part.length;\n }\n return joined;\n}\n\n/** `query()`'s value rule: null stays null; otherwise the serializer of the described type, or `toString()`. */\nfunction serializeValue(params: { serializers: PGlite[\"serializers\"]; value: unknown; oid: number | undefined }): string | null {\n const { value } = params;\n if (value === null || value === undefined) return null;\n const serializer = params.serializers[params.oid as number];\n if (serializer) return serializer(value);\n return (value as { toString(): string }).toString();\n}\n\n/**\n * The parsers of the columns that came back, keyed like the instance table.\n * `parseResults` merges this with the per-query parsers and `parseType` looks\n * up only a column's own type OID (falling back to PGlite's default table), so\n * the subset resolves every cell exactly as the whole table would.\n */\nfunction columnParsers(params: { db: PGlite; messages: readonly BackendMessage[] }): Record<number, ValueParser> {\n const subset: Record<number, ValueParser> = {};\n for (const message of params.messages) {\n if (message.name !== \"rowDescription\") continue;\n for (const field of (message as BackendMessage & { fields: readonly { dataTypeID: number }[] }).fields) {\n const parser = params.db.parsers[field.dataTypeID];\n if (parser !== undefined) subset[field.dataTypeID] = parser;\n }\n }\n return subset;\n}\n\n// ─── The statement ────────────────────────────────────────────────\n\n/**\n * What the adapter uses of an open transaction: PGlite's own `Transaction`\n * (from `db.transaction()`), or the handle {@link runProtocolTransaction}\n * passes its body.\n */\nexport type PgliteTransactionHandle = Pick<Transaction, \"query\" | \"exec\" | \"closed\">;\n\n/** Arguments of one statement on the protocol path. */\nexport type ProtocolQueryParams = {\n readonly kit: PgliteProtocolKit;\n /** The in-process instance ({@link protocolInstance}). */\n readonly db: PGlite;\n /** The open transaction the statement runs in; absent for a root statement. */\n readonly transaction?: PgliteTransactionHandle;\n readonly text: string;\n /** Values exactly as `query()` would receive them (the adapter's serialized parameters). */\n readonly values: readonly unknown[];\n readonly parsers: ParserOptions;\n};\n\n/**\n * Run one statement with `query()`'s results and errors (see the module\n * comment). Root: ready check, transaction mutex, query mutex, file-system\n * sync. Inside a PGlite transaction: the closed check and the query mutex only,\n * as `Transaction.query()` does.\n */\nexport async function runProtocolQuery(params: ProtocolQueryParams): Promise<Results<Record<string, unknown>>> {\n const { db, transaction } = params;\n if (transaction === undefined) {\n await db._checkReady();\n return db._runExclusiveTransaction(() => runStatement({ ...params, root: true }));\n }\n if (transaction.closed) throw new Error(\"Transaction is closed\");\n return runStatement({ ...params, root: false });\n}\n\nasync function runStatement(\n params: ProtocolQueryParams & {\n readonly root: boolean;\n /** AW-02's handle: the caller's own options object (possibly absent), logged, parsed with and decorated as `Transaction.query()` does. */\n readonly callerOptions?: { readonly value: QueryOptions | undefined };\n },\n): Promise<Results<Record<string, unknown>>> {\n const { kit, db, text, values } = params;\n const options: QueryOptions | undefined = params.callerOptions === undefined ? { parsers: params.parsers } : params.callerOptions.value;\n return db.runExclusive(async () => {\n if (db.debug > 0) console.log(\"runQuery\", text, values, options);\n await db._handleBlob(undefined);\n let messages: BackendMessage[] = [];\n // `query()` sends Sync after the statement whatever happened; the final\n // batch carries it, so only a failure before that batch sends it alone.\n let synced = false;\n try {\n const { serialize } = kit;\n let head: Uint8Array[];\n let serialized: (string | null)[];\n if (values.some((value) => value !== null && value !== undefined)) {\n const described = await db.execProtocolStream(joinMessages([serialize.parse({ text }), serialize.describe({ type: \"S\" })]), NO_FS_SYNC);\n const dataTypeIDs = kit.parseDescribeStatementResults(described);\n serialized = values.map((value, index) => serializeValue({ serializers: db.serializers, value, oid: dataTypeIDs[index] }));\n head = [];\n } else {\n serialized = values.map(() => null);\n head = [serialize.parse({ text })];\n }\n const batch = joinMessages([...head, serialize.bind({ values: serialized }), serialize.describe({ type: \"P\" }), serialize.execute({}), serialize.sync()]);\n try {\n messages = await db.execProtocolStream(batch, NO_FS_SYNC);\n synced = true;\n } catch (error) {\n // A server error ends the batch at its Sync; anything else may not have.\n if (error instanceof kit.DatabaseError) synced = true;\n throw error;\n }\n } catch (error) {\n if (error instanceof kit.DatabaseError) throw Object.assign(error, { query: text, params: values, queryOptions: options });\n throw error;\n } finally {\n if (!synced) await db.execProtocolStream(kit.serialize.sync(), NO_FS_SYNC);\n }\n await db._cleanupBlob();\n if (params.root) await db.syncToFs();\n const blob = await db._getWrittenBlob();\n const [result] = kit.parseResults(messages, columnParsers({ db, messages }), options, blob);\n return (result ?? { rows: [], fields: [], affectedRows: 0 }) as Results<Record<string, unknown>>;\n });\n}\n\n// ─── Simple-query statements and transactions (AW-02) ──────────────\n\n/** Arguments of one simple-query call ({@link runSimpleQuery}). */\ntype SimpleQueryParams = {\n readonly kit: PgliteProtocolKit;\n readonly db: PGlite;\n /** One or more statements, as `exec()` takes them. */\n readonly text: string;\n readonly options: QueryOptions | undefined;\n /** Outside any transaction PGlite syncs the file system after the statement; inside one it does not. */\n readonly root: boolean;\n};\n\n/**\n * PGlite's `#runExec` (the body of `exec()` and of every control statement of\n * `transaction()`), step for step: the query mutex, the debug log line, the\n * blob bookkeeping, the `Q` message followed by `Sync` (here in ONE engine\n * call; a failure that did not reach the `Sync` still sends it alone), the\n * error decoration (`query` / `params` / `queryOptions`) and the file-system\n * sync outside a transaction. Returns the backend messages and the written\n * blob; {@link execStatements} parses them, a control statement needs neither.\n */\nasync function runSimpleQuery(params: SimpleQueryParams): Promise<{ messages: BackendMessage[]; blob: Blob | undefined }> {\n const { kit, db, text, options } = params;\n return db.runExclusive(async () => {\n if (db.debug > 0) console.log(\"runExec\", text, options);\n await db._handleBlob(options?.blob);\n let messages: BackendMessage[] = [];\n let synced = false;\n try {\n try {\n messages = await db.execProtocolStream(joinMessages([kit.serialize.query(text), kit.serialize.sync()]), NO_FS_SYNC);\n synced = true;\n } catch (error) {\n // A server error is collected while the engine runs the whole buffer, Sync included.\n if (error instanceof kit.DatabaseError) synced = true;\n throw error;\n }\n } catch (error) {\n if (error instanceof kit.DatabaseError) throw Object.assign(error, { query: text, params: undefined, queryOptions: options });\n throw error;\n } finally {\n if (!synced) await db.execProtocolStream(kit.serialize.sync(), NO_FS_SYNC);\n }\n await db._cleanupBlob();\n if (params.root) await db.syncToFs();\n const blob = await db._getWrittenBlob();\n return { messages, blob };\n });\n}\n\n/** `exec()`'s result: every statement's rows, parsed by PGlite's own `parseResults` with the parsers of the columns that came back. */\nasync function execStatements(params: SimpleQueryParams): Promise<Results[]> {\n const { messages, blob } = await runSimpleQuery(params);\n return params.kit.parseResults(messages, columnParsers({ db: params.db, messages }), params.options, blob);\n}\n\n/**\n * PGlite's `exec()` at the ROOT (no transaction open): the ready check and the\n * transaction mutex, then {@link runSimpleQuery} with the file-system sync.\n * The adapter's session release (`ROLLBACK`) uses it.\n */\nexport async function runProtocolExec(params: { kit: PgliteProtocolKit; db: PGlite; text: string }): Promise<Results[]> {\n const { kit, db, text } = params;\n await db._checkReady();\n return db._runExclusiveTransaction(() => execStatements({ kit, db, text, options: undefined, root: true }));\n}\n\n/**\n * PGlite's `transaction()`, with the adapter sending the control statements:\n * the ready check; the transaction mutex for the whole body (a shared\n * instance's other callers wait, as before); `BEGIN` (synced like any root\n * statement — PGlite's flag is still \"not in a transaction\" then); the body\n * with a handle whose `query` / `exec` run inside the transaction and refuse\n * once COMMIT was sent (`Transaction is closed`, PGlite's message); `COMMIT` when the\n * body returns, `ROLLBACK` when it throws — neither synced, as in PGlite — and\n * a failed `COMMIT` is not followed by a `ROLLBACK` (the handle is closed\n * before it is sent). A failed `ROLLBACK` replaces the body's error, as in\n * PGlite; the adapter reports that case. `inTransaction` mirrors PGlite's\n * private flag for this handle: a statement through it syncs the file system\n * only once the transaction ended (a handle used after its ROLLBACK).\n */\nexport async function runProtocolTransaction<T>(params: {\n kit: PgliteProtocolKit;\n db: PGlite;\n body: (transaction: PgliteTransactionHandle) => Promise<T>;\n}): Promise<T> {\n const { kit, db, body } = params;\n await db._checkReady();\n return db._runExclusiveTransaction(async () => {\n await runSimpleQuery({ kit, db, text: \"BEGIN\", options: undefined, root: true });\n let inTransaction = true;\n let closed = false;\n const checkClosed = (): void => {\n if (closed) throw new Error(\"Transaction is closed\");\n };\n const handle: PgliteTransactionHandle = {\n query: async <R>(text: string, values?: unknown[], options?: QueryOptions): Promise<Results<R>> => {\n checkClosed();\n // The adapter never calls this (its statements take `runProtocolQuery`); it honours `parsers`, B-03's scope.\n const statement = { kit, db, text, values: values ?? [], parsers: options?.parsers ?? {}, root: !inTransaction, callerOptions: { value: options } };\n return (await runStatement(statement)) as Results<R>;\n },\n exec: async (text: string, options?: QueryOptions): Promise<Results[]> => {\n checkClosed();\n return execStatements({ kit, db, text, options, root: !inTransaction });\n },\n get closed(): boolean {\n return closed;\n },\n };\n try {\n const result = await body(handle);\n if (!closed) {\n closed = true;\n await runSimpleQuery({ kit, db, text: \"COMMIT\", options: undefined, root: false });\n }\n inTransaction = false;\n return result;\n } catch (error) {\n // PGlite leaves its handle's `closed` flag as it was after this ROLLBACK; so does this one.\n if (!closed) await runSimpleQuery({ kit, db, text: \"ROLLBACK\", options: undefined, root: false });\n inTransaction = false;\n throw error;\n }\n });\n}\n",
|
|
8
9
|
"/**\n * Operation queue for PGlite.\n *\n * PGlite is a single embedded PostgreSQL connection: two overlapping\n * operations would interleave on the same wire, and an interleaved statement\n * inside a transaction would silently join that transaction. Every adapter\n * entry point therefore runs through one serial chain — including whole\n * `transaction()` calls, which occupy the chain until they commit or roll back.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n/** Connection-wide exclusion shared by wrappers of an injected instance. */\nexport type ConnectionOwnership = {\n readonly queue: OperationQueue;\n readonly txContext: AsyncLocalStorage<{ active: boolean }>;\n readonly runInCallback: <T>(run: () => Promise<T>) => Promise<T>;\n};\n\nconst INJECTED_OWNERSHIP: WeakMap<object, ConnectionOwnership> = new WeakMap();\n\n/** Reuse the same operation queue for the same caller-owned PGlite instance. */\nexport function connectionOwnership(params: { database?: object }): ConnectionOwnership {\n const existing = params.database === undefined ? undefined : INJECTED_OWNERSHIP.get(params.database);\n if (existing !== undefined) return existing;\n const txContext = new AsyncLocalStorage<{ active: boolean }>();\n const ownership: ConnectionOwnership = { queue: createOperationQueue(), txContext,\n runInCallback<T>(run: () => Promise<T>): Promise<T> {\n const token = { active: true };\n return txContext.run(token, run).finally(() => { token.active = false; });\n },\n };\n if (params.database !== undefined) INJECTED_OWNERSHIP.set(params.database, ownership);\n return ownership;\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
10
|
"/**\n * Transaction-control SQL text (postgres dialect, PGlite flavour).\n *\n * PGlite issues its own `BEGIN` inside `pglite.transaction(cb)`, so isolation\n * is set with `SET TRANSACTION …` as the first statement of the callback rather\n * than as a `BEGIN` clause.\n */\n\nimport type { IsolationLevel } from \"@vibeorm/runtime\";\n\n/** VibeORM isolation level → PostgreSQL isolation level keyword. */\nexport const ISOLATION_LEVEL_SQL: Readonly<Record<IsolationLevel, string>> = Object.freeze({\n ReadCommitted: \"READ COMMITTED\",\n RepeatableRead: \"REPEATABLE READ\",\n Serializable: \"SERIALIZABLE\",\n});\n\n/**\n * `SET TRANSACTION ISOLATION LEVEL <level>` — valid only as the first statement\n * of an open transaction, which is where the adapter issues it.\n */\nexport function setIsolationLevelStatement(params: { isolationLevel: IsolationLevel }): string {\n return `SET TRANSACTION ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[params.isolationLevel]}`;\n}\n\n/**\n * `SET TRANSACTION [ISOLATION LEVEL <level>][, ]READ ONLY` — the transaction\n * modes PGlite cannot put in its own `BEGIN`, sent as the first exec inside\n * `pglite.transaction(cb)`. Returns `undefined` when nothing was asked for.\n * The isolation-only text is exactly {@link setIsolationLevelStatement}.\n */\nexport function setTransactionModesStatement(params: { isolationLevel?: IsolationLevel; readOnly?: boolean }): string | undefined {\n const { isolationLevel, readOnly } = params;\n if (readOnly !== true) return isolationLevel === undefined ? undefined : setIsolationLevelStatement({ isolationLevel });\n return isolationLevel === undefined\n ? \"SET TRANSACTION READ ONLY\"\n : `SET TRANSACTION ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[isolationLevel]}, READ ONLY`;\n}\n\n// NOTE (board #2): this module deliberately has NO statementTimeoutStatement.\n// PGlite accepts `SET LOCAL statement_timeout` and reports it back via SHOW,\n// but the single-threaded WASM build never fires it (live-verified:\n// `pg_sleep(2)` under a 100ms timeout runs to completion) — so the adapter\n// refuses the `timeout` option outright with VIBE_UNSUPPORTED_CAPABILITY\n// instead of rendering SQL that silently does nothing.\n\n// ─── Nested transaction options (the cross-adapter contract) ──────\n\n// ONE copy, shared by every adapter through the runtime's adapter-kit;\n// re-exported here so this adapter's public surface is unchanged.\nexport { refuseNestedTransactionOptions } from \"@vibeorm/runtime/adapter-kit\";\n"
|
|
10
11
|
],
|
|
11
|
-
"mappings": "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",
|
|
12
|
-
"debugId": "
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "428C39C9B2B1FAF264756E2164756E21",
|
|
13
14
|
"names": []
|
|
14
15
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PGlite statements on the wire protocol directly (performance audit round 2,
|
|
3
|
+
* finding B-03).
|
|
4
|
+
*
|
|
5
|
+
* `PGlite.query()` cost ~44 µs for a one-row primary-key read, and most of it
|
|
6
|
+
* is not the engine. Its result step (`parse.parseResults`) copies the
|
|
7
|
+
* instance's WHOLE parser table — every array type PGlite registered at start,
|
|
8
|
+
* `{ ...db.parsers, ...options.parsers }` — on every call (~23 µs), and it
|
|
9
|
+
* crosses into the engine six times (Parse, Describe statement, Bind, Describe
|
|
10
|
+
* portal, Execute, Sync). This module runs the SAME protocol messages through
|
|
11
|
+
* the transport `query()` itself uses, `execProtocolStream` (one
|
|
12
|
+
* `execProtocolRaw` call into the engine each, with PGlite's own message
|
|
13
|
+
* parser, error, notice and notification handling):
|
|
14
|
+
*
|
|
15
|
+
* - no value needs a server-described type (no values, or only null/undefined):
|
|
16
|
+
* ONE call `[Parse, Bind, Describe portal, Execute, Sync]`;
|
|
17
|
+
* - otherwise TWO calls `[Parse, Describe statement]` →
|
|
18
|
+
* `[Bind, Describe portal, Execute, Sync]`, because PGlite serializes every
|
|
19
|
+
* value by the type the server infers for its parameter (`db.serializers`
|
|
20
|
+
* keyed by that OID, `toString()` without one). Serializing by JavaScript
|
|
21
|
+
* type instead would differ for a string bound to a `boolean` or `bytea`
|
|
22
|
+
* parameter, a `Date` bound to `time`/`jsonb`, a `bigint` bound to `jsonb`, …
|
|
23
|
+
*
|
|
24
|
+
* and hands the result messages to PGlite's own `parse.parseResults` with only
|
|
25
|
+
* the parsers of the columns that came back — the lookup `parseType` performs
|
|
26
|
+
* reads no other key, so rows, values and `affectedRows` are those of the
|
|
27
|
+
* installed PGlite version by construction. Everything else `query()` does is
|
|
28
|
+
* kept: the ready check, the transaction mutex (root statements never
|
|
29
|
+
* interleave into another caller's open transaction on a shared instance) and
|
|
30
|
+
* the query mutex, the debug log line, the blob bookkeeping, the Sync after a
|
|
31
|
+
* failure, the error decoration (`query` / `params` / `queryOptions`) and the
|
|
32
|
+
* file-system sync after a root statement. Measured (one-row read, median of 7
|
|
33
|
+
* interleaved batches of 400): `query()` 44.0 µs → this path 18.7 µs with
|
|
34
|
+
* values, 16.0 µs without.
|
|
35
|
+
*
|
|
36
|
+
* Gate: by capability, not by version — PGlite's in-process class from the
|
|
37
|
+
* module the adapter loaded, when the module and the class expose every member
|
|
38
|
+
* this path calls. `query()`'s `#runQuery` source this mirrors is the same in
|
|
39
|
+
* 0.3.16, 0.4.0, 0.5.4 and 0.5.9 (0.3.16 already has `execProtocolStream`, so
|
|
40
|
+
* it takes this path too; review 2 of the round-2 fix campaign read its source
|
|
41
|
+
* map). Anything else — `PGliteWorker`, another copy of the module, an early
|
|
42
|
+
* 0.3.x without `execProtocolStream`, a module without the protocol helpers —
|
|
43
|
+
* keeps `query()`.
|
|
44
|
+
*
|
|
45
|
+
* Transactions (performance round 3, AW-02): under the same gate the adapter
|
|
46
|
+
* also owns BEGIN / COMMIT / ROLLBACK, SAVEPOINT / RELEASE / ROLLBACK TO and
|
|
47
|
+
* `SET TRANSACTION` ({@link runProtocolTransaction}). PGlite's `transaction()`
|
|
48
|
+
* and `exec()` sent each as a simple-query `Q` message plus a `Sync`, then ran
|
|
49
|
+
* the result step over the whole parser table (≈ 33 µs per control statement);
|
|
50
|
+
* the same two messages now go to the engine in ONE `execProtocolStream` call,
|
|
51
|
+
* with `transaction()`'s mutex, closed-handle refusal, commit/rollback rule,
|
|
52
|
+
* error decoration and file-system sync points kept.
|
|
53
|
+
*/
|
|
54
|
+
import type { PGlite, ParserOptions, Results, Transaction } from "@electric-sql/pglite";
|
|
55
|
+
/** The `@electric-sql/pglite` module, as the adapter loads it. */
|
|
56
|
+
export type PgliteModule = typeof import("@electric-sql/pglite");
|
|
57
|
+
/**
|
|
58
|
+
* What the protocol path needs from the module the adapter loaded — taken from
|
|
59
|
+
* that SAME module object, so the class check and the message helpers belong
|
|
60
|
+
* to the PGlite copy that created the instance.
|
|
61
|
+
*/
|
|
62
|
+
export type PgliteProtocolKit = {
|
|
63
|
+
readonly PGlite: PgliteModule["PGlite"];
|
|
64
|
+
readonly serialize: PgliteModule["protocol"]["serialize"];
|
|
65
|
+
readonly parseResults: PgliteModule["parse"]["parseResults"];
|
|
66
|
+
readonly parseDescribeStatementResults: PgliteModule["parse"]["parseDescribeStatementResults"];
|
|
67
|
+
readonly DatabaseError: PgliteModule["messages"]["DatabaseError"];
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Build the kit from the loaded module; `undefined` (every statement keeps
|
|
71
|
+
* `query()`) when the module lacks one of the helpers — an older PGlite, or a
|
|
72
|
+
* test double standing in for the module.
|
|
73
|
+
*/
|
|
74
|
+
export declare function createProtocolKit(params: {
|
|
75
|
+
module: PgliteModule;
|
|
76
|
+
}): PgliteProtocolKit | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* The instance as PGlite's in-process class when the protocol path can drive
|
|
79
|
+
* it; `undefined` keeps `query()` (see the module comment for the gate).
|
|
80
|
+
*/
|
|
81
|
+
export declare function protocolInstance(params: {
|
|
82
|
+
kit: PgliteProtocolKit;
|
|
83
|
+
instance: object;
|
|
84
|
+
}): PGlite | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* What the adapter uses of an open transaction: PGlite's own `Transaction`
|
|
87
|
+
* (from `db.transaction()`), or the handle {@link runProtocolTransaction}
|
|
88
|
+
* passes its body.
|
|
89
|
+
*/
|
|
90
|
+
export type PgliteTransactionHandle = Pick<Transaction, "query" | "exec" | "closed">;
|
|
91
|
+
/** Arguments of one statement on the protocol path. */
|
|
92
|
+
export type ProtocolQueryParams = {
|
|
93
|
+
readonly kit: PgliteProtocolKit;
|
|
94
|
+
/** The in-process instance ({@link protocolInstance}). */
|
|
95
|
+
readonly db: PGlite;
|
|
96
|
+
/** The open transaction the statement runs in; absent for a root statement. */
|
|
97
|
+
readonly transaction?: PgliteTransactionHandle;
|
|
98
|
+
readonly text: string;
|
|
99
|
+
/** Values exactly as `query()` would receive them (the adapter's serialized parameters). */
|
|
100
|
+
readonly values: readonly unknown[];
|
|
101
|
+
readonly parsers: ParserOptions;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Run one statement with `query()`'s results and errors (see the module
|
|
105
|
+
* comment). Root: ready check, transaction mutex, query mutex, file-system
|
|
106
|
+
* sync. Inside a PGlite transaction: the closed check and the query mutex only,
|
|
107
|
+
* as `Transaction.query()` does.
|
|
108
|
+
*/
|
|
109
|
+
export declare function runProtocolQuery(params: ProtocolQueryParams): Promise<Results<Record<string, unknown>>>;
|
|
110
|
+
/**
|
|
111
|
+
* PGlite's `exec()` at the ROOT (no transaction open): the ready check and the
|
|
112
|
+
* transaction mutex, then {@link runSimpleQuery} with the file-system sync.
|
|
113
|
+
* The adapter's session release (`ROLLBACK`) uses it.
|
|
114
|
+
*/
|
|
115
|
+
export declare function runProtocolExec(params: {
|
|
116
|
+
kit: PgliteProtocolKit;
|
|
117
|
+
db: PGlite;
|
|
118
|
+
text: string;
|
|
119
|
+
}): Promise<Results[]>;
|
|
120
|
+
/**
|
|
121
|
+
* PGlite's `transaction()`, with the adapter sending the control statements:
|
|
122
|
+
* the ready check; the transaction mutex for the whole body (a shared
|
|
123
|
+
* instance's other callers wait, as before); `BEGIN` (synced like any root
|
|
124
|
+
* statement — PGlite's flag is still "not in a transaction" then); the body
|
|
125
|
+
* with a handle whose `query` / `exec` run inside the transaction and refuse
|
|
126
|
+
* once COMMIT was sent (`Transaction is closed`, PGlite's message); `COMMIT` when the
|
|
127
|
+
* body returns, `ROLLBACK` when it throws — neither synced, as in PGlite — and
|
|
128
|
+
* a failed `COMMIT` is not followed by a `ROLLBACK` (the handle is closed
|
|
129
|
+
* before it is sent). A failed `ROLLBACK` replaces the body's error, as in
|
|
130
|
+
* PGlite; the adapter reports that case. `inTransaction` mirrors PGlite's
|
|
131
|
+
* private flag for this handle: a statement through it syncs the file system
|
|
132
|
+
* only once the transaction ended (a handle used after its ROLLBACK).
|
|
133
|
+
*/
|
|
134
|
+
export declare function runProtocolTransaction<T>(params: {
|
|
135
|
+
kit: PgliteProtocolKit;
|
|
136
|
+
db: PGlite;
|
|
137
|
+
body: (transaction: PgliteTransactionHandle) => Promise<T>;
|
|
138
|
+
}): Promise<T>;
|
|
139
|
+
//# sourceMappingURL=protocol.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeorm/adapter-pglite",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "PGlite (in-process Postgres) adapter for VibeORM v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"orm",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@electric-sql/pglite": "^0.3.0 || ^0.4.0 || ^0.5.0",
|
|
49
|
-
"@vibeorm/runtime": "4.
|
|
50
|
-
"@vibeorm/schema": "4.
|
|
49
|
+
"@vibeorm/runtime": "4.3.0",
|
|
50
|
+
"@vibeorm/schema": "4.3.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|