@vibeorm/adapter-pg 3.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/config.d.ts +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +250 -34
- package/dist/index.js.map +6 -4
- package/dist/read-cancellation.d.ts +38 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -48,6 +48,7 @@ const db = VibeClient({ adapter: pgAdapter({ pool }) });
|
|
|
48
48
|
| `max` | `number` | `10` | Maximum pooled connections; ignored when `pool` is given |
|
|
49
49
|
| `statementTimeout` | `number` (ms) | — | Pool-wide `statement_timeout`; a per-transaction `timeout` overrides it |
|
|
50
50
|
| `connectionTimeout` | `number` (ms) | — | Wait for a pool checkout before failing (pg's `connectionTimeoutMillis`) |
|
|
51
|
+
| `cancellationConnectionString` | `string` | Owned `connectionString`; none for an injected `pool` | Separate control endpoint for running read cancellation; same PostgreSQL server/database, with permission to signal the target role |
|
|
51
52
|
| `preparedStatements` | `boolean` | `false` | Send ORM-path queries as named prepared statements. Measured −17…−25 % on reads and −6 % on a by-key update (see below). Off by default because of transaction-mode poolers — turn it on for direct connections and session pooling |
|
|
52
53
|
| `stmtCacheMax` | `number` | `1000` | Budget of prepared-statement names per pool. Names never evict (an evicted name stays prepared on the backend), so this bounds server-side retention per connection; a text arriving after the budget is spent runs unnamed |
|
|
53
54
|
| `planCacheMode` | `"auto" \| "force_custom_plan" \| "force_generic_plan"` | `"auto"` | PostgreSQL `plan_cache_mode` for every connection of the pool the adapter builds; sent in the `options` startup parameter, merged with an `options` the URL already carries (a URL that sets `plan_cache_mode` itself wins). Refused with `VIBE_CONFIG` next to a caller-supplied `pool` |
|
|
@@ -107,6 +108,36 @@ A string bound to a `::jsonb` slot on the raw path is sent as text and parsed by
|
|
|
107
108
|
|
|
108
109
|
On both paths a `Date` is spelled as UTC ISO text (`2026-03-08T19:38:08.538Z`) rather than node-postgres' host-local form, and the result side attaches per-query parsers (never `pg.types` globally) that read `timestamp without time zone`, `date` and their arrays as **UTC** — pg-types reads them as host-local time. Together they give zone-less columns (Prisma's `@db.Timestamp(n)` / `@db.Date`) the UTC wall-clock contract shared with bun:sql, PGlite and Prisma, whatever the process `TZ` is; `timestamptz` is unaffected. See `docs/dialect-notes.md` "DateTime without time zone on postgres".
|
|
109
110
|
|
|
111
|
+
## Caller cancellation of reads
|
|
112
|
+
|
|
113
|
+
Read execution options are separate from query arguments: `signal?: AbortSignal` and `cancellation?: "before-dispatch" | "running"`. A signal defaults to `"before-dispatch"`. The adapter accepts them only on `execute` with `effect: "read"`; raw SQL, writes and unknown effects are outside this contract. A caller cancellation raises `VIBE_CANCELLED` with `meta.reason: "caller-aborted"`, a stage and `databaseCancelled` when its contract is fulfilled. Running requests can instead fail with the control-path refusal described below. Unrelated driver failures keep their mapped codes.
|
|
114
|
+
|
|
115
|
+
**Running support is qualified in this source tree for the configured PostgreSQL scope below.** Guarded live witnesses cover running sleep and lock-wait interruption, transaction arrival ordering, swallowed and nested abort cleanup, and response-wins reuse without signalling the next borrower. These witnesses do not claim publication of this feature or a performance improvement. An adapter-owned URL provides its control address. An injected pool requires an explicit address:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
const owned = pgAdapter({ connectionString });
|
|
119
|
+
const shared = pgAdapter({
|
|
120
|
+
pool,
|
|
121
|
+
cancellationConnectionString: connectionString,
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Without that explicit configuration, an injected pool declares `readCancellation: "before-dispatch"` and refuses `"running"` with `VIBE_UNSUPPORTED_CAPABILITY` before checkout or SQL. Its credentials are never inferred from pool internals or environment. The adapter creates and closes its own control connection without ending the injected pool. Give complete URLs. Use a direct or session-pinned control endpoint reaching the same PostgreSQL server/database, with the same login role or permission to signal the target role. A wrong endpoint that cannot match the exact target refuses; it does not cancel a backend with a coincidentally matching process ID.
|
|
126
|
+
|
|
127
|
+
The control URL removes local `query_timeout`, replaces any existing `options` with startup `statement_timeout` and `lock_timeout` settings, and adds a five-second `connect_timeout` and `statement_timeout`. The endpoint must accept these startup parameters. A transaction-mode pooler that refuses them fails the strong read before its user SQL is dispatched, even though the configured adapter declares `readCancellation: "running"`. If the main URL goes through such a pooler, give an explicit suitable `cancellationConnectionString`; the main connection remains transaction-pinned.
|
|
128
|
+
|
|
129
|
+
**A strong root read runs in a READ ONLY transaction.** This pins the backend through transaction-mode poolers, but it also changes what PostgreSQL permits: locking reads such as `SELECT FOR UPDATE`, or functions that write, can be rejected even if the default path would run them. Reads inside an existing transaction use that transaction's session and access mode. Running mode is refused on a pipelined target or one configured with local `query_timeout`, because its promise/cleanup lifetime cannot prove SQL settlement. Ordinary reads without execution options keep their existing query path.
|
|
130
|
+
|
|
131
|
+
The cancellation path uses the official `pg.Client` query API and PostgreSQL's documented `pg_cancel_backend`, never deprecated `Client.cancel`. A separate control connection avoids waiting behind an exhausted main pool. Each strong statement opens one extra control connection, including statements that are never aborted, and closes it after settlement. Five-second time bounds apply to control acquisition/query and post-abort settlement; they are not a concurrency cap. Concurrent strong reads can therefore add one connection each beyond the main pool's `max`.
|
|
132
|
+
|
|
133
|
+
A random transaction-local session tag, backend start time, database and role bind the target. The target remains held until the original read and cancellation control settle. Any attempt discards that session and prevents further user SQL or commit in its transaction, even if a callback catches the cancellation. A failed control operation or an unsettled read destroys only the held target; cleanup releases it once.
|
|
134
|
+
|
|
135
|
+
`databaseCancelled: true` requires the original server failure `57014` plus this read's own successful control signal. A control boolean alone does not prove that SQL stopped. Forced connection closure does not set that marker.
|
|
136
|
+
|
|
137
|
+
An abort can race statement start or end: the exact target may be visible but not yet active, or the server may have finished before the client receives its response. The active-target guard cannot distinguish those races from a mismatched control endpoint. If it cannot signal the target, the adapter deliberately reports `VIBE_UNSUPPORTED_CAPABILITY` with `meta.reason: "target-not-signalled"` and `databaseCancelled: false`, discards the exact held session, and does not claim cancellation. The requested stronger guarantee was not fulfilled. Consumers must not treat this refusal as proof that the server statement was cancelled.
|
|
138
|
+
|
|
139
|
+
Strong root reads still execute transaction lifecycle SQL and a target-witness query; an abort adds cancellation control SQL. Individual graph receipts exclude `BEGIN`, `COMMIT`, `ROLLBACK`, `SAVEPOINT` (including rollback to a savepoint) and `RELEASE` on both automatic native-RLS and strong PostgreSQL root paths. Interactive and array transaction lifecycle is also outside the individual graph receipt. The internal `onReadControl` hook continues to account for the real target-witness, cancellation and read-setup/control statements, including failed dispatched controls with zero returned rows. `rows` counts physical result records, not affected or scanned rows; control rows do not count as materialized graph records.
|
|
140
|
+
|
|
110
141
|
## Error mapping
|
|
111
142
|
|
|
112
143
|
Every driver failure becomes a `VibeError` with the driver error as `cause` and `meta` carrying `sqlstate` plus `constraint` / `table` / `column` when the server reported them. Table (`PG_SQLSTATE_ERROR_CODES`, frozen and mirrored by tests):
|
package/dist/config.d.ts
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export type PgQueryResultsLike = PgQueryResultLike | PgQueryResultLike[];
|
|
|
40
40
|
/** The parts of a `pg` PoolClient this adapter uses. */
|
|
41
41
|
export type PgPoolClientLike = {
|
|
42
42
|
query(config: PgQueryConfig): Promise<PgQueryResultsLike>;
|
|
43
|
+
/** Installed pg pipelined end waits for drain; strong cancellation refuses that lifetime. */
|
|
44
|
+
readonly pipeline?: boolean;
|
|
45
|
+
/** Local rejection timeouts cannot prove original statement settlement. */
|
|
46
|
+
readonly connectionParameters?: {
|
|
47
|
+
readonly query_timeout?: number;
|
|
48
|
+
};
|
|
43
49
|
/**
|
|
44
50
|
* Hand the client back. A TRUTHY argument tells node-postgres to DESTROY it
|
|
45
51
|
* instead of pooling it — used when a rollback failed and the session state
|
|
@@ -90,6 +96,11 @@ export type PgAdapterSettings = {
|
|
|
90
96
|
readonly statementTimeout?: number;
|
|
91
97
|
/** Milliseconds to wait for a pool checkout before failing (pg's `connectionTimeoutMillis`). */
|
|
92
98
|
readonly connectionTimeout?: number;
|
|
99
|
+
/** Separate direct/session-pinned PostgreSQL control endpoint for running reads.
|
|
100
|
+
* Required with an injected pool; never inferred from that pool or ambient env.
|
|
101
|
+
* The same role must be allowed to signal its target. Server support needs live acceptance.
|
|
102
|
+
*/
|
|
103
|
+
readonly cancellationConnectionString?: string;
|
|
93
104
|
/**
|
|
94
105
|
* Send ORM-path queries as named prepared statements (default false).
|
|
95
106
|
*
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/adapter-pg/src/index.ts
|
|
3
3
|
import {
|
|
4
|
+
assertReadExecution as assertReadExecution2,
|
|
5
|
+
readCancelledError as readCancelledError2,
|
|
6
|
+
markTransactionAborted,
|
|
7
|
+
runReadExecution,
|
|
4
8
|
abortTransactionOnServerFailure,
|
|
5
9
|
createDeferredControl,
|
|
6
10
|
observeRowChanges,
|
|
@@ -22,8 +26,8 @@ import {
|
|
|
22
26
|
transactionAbortedError,
|
|
23
27
|
validateTransactionDeadline
|
|
24
28
|
} from "@vibeorm/runtime/adapter-kit";
|
|
25
|
-
import { VibeError as
|
|
26
|
-
import
|
|
29
|
+
import { VibeError as VibeError5 } from "@vibeorm/schema";
|
|
30
|
+
import pg3 from "pg";
|
|
27
31
|
|
|
28
32
|
// packages/adapter-pg/src/batch.ts
|
|
29
33
|
import pg from "pg";
|
|
@@ -178,6 +182,16 @@ function firstBatchFailure(params) {
|
|
|
178
182
|
return;
|
|
179
183
|
}
|
|
180
184
|
|
|
185
|
+
// packages/adapter-pg/src/read-cancellation.ts
|
|
186
|
+
import pg2 from "pg";
|
|
187
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
188
|
+
import { assertReadExecution, readCancelledError } from "@vibeorm/runtime/adapter-kit";
|
|
189
|
+
|
|
190
|
+
// packages/adapter-pg/src/config.ts
|
|
191
|
+
var PG_READ_CANCELLATION = Object.freeze({
|
|
192
|
+
controlTimeoutMs: 5000
|
|
193
|
+
});
|
|
194
|
+
|
|
181
195
|
// packages/adapter-pg/src/errors.ts
|
|
182
196
|
import { VibeError } from "@vibeorm/schema";
|
|
183
197
|
var PG_SQLSTATE_ERROR_CODES = Object.freeze({
|
|
@@ -252,6 +266,170 @@ async function runMapped(fn) {
|
|
|
252
266
|
}
|
|
253
267
|
}
|
|
254
268
|
|
|
269
|
+
// packages/adapter-pg/src/read-cancellation.ts
|
|
270
|
+
function rowsOf(result) {
|
|
271
|
+
return (Array.isArray(result) ? result.at(-1)?.rows : result.rows) ?? [];
|
|
272
|
+
}
|
|
273
|
+
function createPgReadCancellation(params) {
|
|
274
|
+
const support = params.connectionString === undefined ? "before-dispatch" : "running";
|
|
275
|
+
return {
|
|
276
|
+
support,
|
|
277
|
+
async run(query) {
|
|
278
|
+
assertReadExecution({ execution: query.execution, effect: "read", support, provider: "postgresql", stage: "waiting-for-connection" });
|
|
279
|
+
if (query.execution?.cancellation !== "running")
|
|
280
|
+
return query.send();
|
|
281
|
+
if (params.target.pipeline === true || (params.target.connectionParameters?.query_timeout ?? 0) > 0) {
|
|
282
|
+
throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Running cancellation requires a non-pipelined target without local query_timeout", meta: { capability: "readCancellation", reason: "target-lifetime" } });
|
|
283
|
+
}
|
|
284
|
+
const address = params.connectionString;
|
|
285
|
+
if (address === undefined)
|
|
286
|
+
throw new VibeError2({ code: "VIBE_INTERNAL", message: "Running cancellation requires a control address" });
|
|
287
|
+
const token = `vibe_cancel_${crypto.randomUUID()}`;
|
|
288
|
+
if (!URL.canParse(address))
|
|
289
|
+
throw new VibeError2({ code: "VIBE_CONFIG", message: "The PostgreSQL cancellation control address must be a URL" });
|
|
290
|
+
const url = new URL(address);
|
|
291
|
+
url.searchParams.delete("query_timeout");
|
|
292
|
+
url.searchParams.set("connect_timeout", String(PG_READ_CANCELLATION.controlTimeoutMs / 1000));
|
|
293
|
+
url.searchParams.set("statement_timeout", String(PG_READ_CANCELLATION.controlTimeoutMs));
|
|
294
|
+
url.searchParams.set("options", `-c statement_timeout=${PG_READ_CANCELLATION.controlTimeoutMs} -c lock_timeout=${PG_READ_CANCELLATION.controlTimeoutMs}`);
|
|
295
|
+
const control = await runMapped(async () => params.createControl?.({ connectionString: url.toString() }) ?? new pg2.Client({ connectionString: url.toString(), connectionTimeoutMillis: PG_READ_CANCELLATION.controlTimeoutMs, query_timeout: 0 }));
|
|
296
|
+
control.on("error", () => {
|
|
297
|
+
return;
|
|
298
|
+
});
|
|
299
|
+
const signal = query.execution.signal;
|
|
300
|
+
let cancel;
|
|
301
|
+
let attempted = false;
|
|
302
|
+
let signalSent = false;
|
|
303
|
+
let finished = false;
|
|
304
|
+
let controlFailure;
|
|
305
|
+
let terminated = false;
|
|
306
|
+
let settlementTimer;
|
|
307
|
+
let preflightAborted = false;
|
|
308
|
+
const abortPreflight = () => {
|
|
309
|
+
preflightAborted = true;
|
|
310
|
+
params.discard();
|
|
311
|
+
if (!terminated) {
|
|
312
|
+
terminated = true;
|
|
313
|
+
params.terminate();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
let original;
|
|
317
|
+
const account = (stage, send) => runMapped(send).then((result) => {
|
|
318
|
+
query.onReadControl?.({ stage, rows: rowsOf(result).length });
|
|
319
|
+
return result;
|
|
320
|
+
}, (error) => {
|
|
321
|
+
query.onReadControl?.({ stage, rows: 0 });
|
|
322
|
+
throw error;
|
|
323
|
+
});
|
|
324
|
+
let listener;
|
|
325
|
+
const operation = runMapped(() => control.connect()).then(async () => {
|
|
326
|
+
assertReadExecution({ execution: query.execution, effect: "read", support, provider: "postgresql", stage: "waiting-for-connection" });
|
|
327
|
+
signal?.addEventListener("abort", abortPreflight, { once: true });
|
|
328
|
+
const witness = rowsOf(await account("preflight", () => params.target.query({
|
|
329
|
+
text: "SELECT pg_backend_pid() AS pid, extract(epoch FROM backend_start)::text AS backend_start, datname, usename, set_config('application_name', $1, true) AS tag FROM pg_stat_activity WHERE pid = pg_backend_pid()",
|
|
330
|
+
values: [token]
|
|
331
|
+
})))[0];
|
|
332
|
+
signal?.removeEventListener("abort", abortPreflight);
|
|
333
|
+
if (preflightAborted)
|
|
334
|
+
throw readCancelledError({ stage: "waiting-for-connection" });
|
|
335
|
+
if (witness === undefined || typeof witness.pid !== "number" || typeof witness.backend_start !== "string" || typeof witness.datname !== "string" || typeof witness.usename !== "string" || witness.tag !== token) {
|
|
336
|
+
throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Cannot prove the exact PostgreSQL cancellation target", meta: { capability: "readCancellation", reason: "target-witness" } });
|
|
337
|
+
}
|
|
338
|
+
assertReadExecution({ execution: query.execution, effect: "read", support, provider: "postgresql", stage: "waiting-for-connection" });
|
|
339
|
+
listener = () => {
|
|
340
|
+
if (finished || attempted)
|
|
341
|
+
return;
|
|
342
|
+
attempted = true;
|
|
343
|
+
params.discard();
|
|
344
|
+
const failControl = (error) => {
|
|
345
|
+
controlFailure = error;
|
|
346
|
+
if (!terminated) {
|
|
347
|
+
terminated = true;
|
|
348
|
+
params.terminate();
|
|
349
|
+
}
|
|
350
|
+
throw error;
|
|
351
|
+
};
|
|
352
|
+
settlementTimer = setTimeout(() => {
|
|
353
|
+
controlFailure = new VibeError2({ code: "VIBE_ADAPTER", message: "PostgreSQL read did not settle after cancellation; its exact session was discarded", meta: { reason: "read-cancellation-unsettled", databaseCancelled: false } });
|
|
354
|
+
if (!terminated) {
|
|
355
|
+
terminated = true;
|
|
356
|
+
params.terminate();
|
|
357
|
+
}
|
|
358
|
+
}, PG_READ_CANCELLATION.controlTimeoutMs);
|
|
359
|
+
cancel = account("cancel", () => control.query({
|
|
360
|
+
text: "SELECT pg_cancel_backend(pid) AS signalled FROM pg_stat_activity WHERE pid = $1 AND extract(epoch FROM backend_start)::text = $2 AND datname = $3 AND usename = $4 AND application_name = $5 AND state = 'active' AND pid <> pg_backend_pid()",
|
|
361
|
+
values: [witness.pid, witness.backend_start, witness.datname, witness.usename, token]
|
|
362
|
+
})).then((result) => {
|
|
363
|
+
signalSent = rowsOf(result)[0]?.signalled === true;
|
|
364
|
+
if (!signalSent)
|
|
365
|
+
failControl(new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "The control endpoint did not signal the exact PostgreSQL target", meta: { capability: "readCancellation", reason: "target-not-signalled", databaseCancelled: false } }));
|
|
366
|
+
}, failControl);
|
|
367
|
+
cancel.then(() => {
|
|
368
|
+
return;
|
|
369
|
+
}, () => {
|
|
370
|
+
return;
|
|
371
|
+
});
|
|
372
|
+
};
|
|
373
|
+
original = runMapped(query.send);
|
|
374
|
+
signal?.addEventListener("abort", listener, { once: true });
|
|
375
|
+
if (signal?.aborted === true)
|
|
376
|
+
listener();
|
|
377
|
+
return original.then(async (result) => {
|
|
378
|
+
finished = true;
|
|
379
|
+
signal?.removeEventListener("abort", listener);
|
|
380
|
+
if (cancel !== undefined)
|
|
381
|
+
await cancel;
|
|
382
|
+
if (controlFailure !== undefined)
|
|
383
|
+
throw controlFailure;
|
|
384
|
+
if (signal?.aborted === true)
|
|
385
|
+
throw readCancelledError({ stage: "running" });
|
|
386
|
+
return result;
|
|
387
|
+
}, async (error) => {
|
|
388
|
+
finished = true;
|
|
389
|
+
signal?.removeEventListener("abort", listener);
|
|
390
|
+
if (cancel !== undefined)
|
|
391
|
+
await cancel.then(() => {
|
|
392
|
+
return;
|
|
393
|
+
}, () => {
|
|
394
|
+
return;
|
|
395
|
+
});
|
|
396
|
+
const sqlstate = error instanceof VibeError2 ? error.meta.sqlstate : pgErrorSqlstate({ error });
|
|
397
|
+
if (terminated && controlFailure !== undefined && sqlstate === undefined)
|
|
398
|
+
throw controlFailure;
|
|
399
|
+
if (attempted && signalSent && sqlstate === "57014") {
|
|
400
|
+
const cancelled = readCancelledError({ stage: "running", databaseCancelled: true, cause: error });
|
|
401
|
+
throw new VibeError2({ code: cancelled.code, message: cancelled.message, meta: { ...cancelled.meta, sqlstate: "57014" }, cause: error });
|
|
402
|
+
}
|
|
403
|
+
throw error;
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
return operation.then(async (result) => {
|
|
407
|
+
signal?.removeEventListener("abort", abortPreflight);
|
|
408
|
+
if (settlementTimer !== undefined)
|
|
409
|
+
clearTimeout(settlementTimer);
|
|
410
|
+
if (listener !== undefined)
|
|
411
|
+
signal?.removeEventListener("abort", listener);
|
|
412
|
+
await runMapped(() => control.end());
|
|
413
|
+
return result;
|
|
414
|
+
}, async (error) => {
|
|
415
|
+
signal?.removeEventListener("abort", abortPreflight);
|
|
416
|
+
if (settlementTimer !== undefined)
|
|
417
|
+
clearTimeout(settlementTimer);
|
|
418
|
+
if (listener !== undefined)
|
|
419
|
+
signal?.removeEventListener("abort", listener);
|
|
420
|
+
await runMapped(() => control.end()).then(() => {
|
|
421
|
+
return;
|
|
422
|
+
}, () => {
|
|
423
|
+
return;
|
|
424
|
+
});
|
|
425
|
+
if (preflightAborted)
|
|
426
|
+
throw readCancelledError({ stage: "waiting-for-connection", cause: error });
|
|
427
|
+
throw error;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
255
433
|
// packages/adapter-pg/src/params.ts
|
|
256
434
|
class PgArrayParam {
|
|
257
435
|
values;
|
|
@@ -302,7 +480,7 @@ function serializeParamsRaw(params) {
|
|
|
302
480
|
}
|
|
303
481
|
|
|
304
482
|
// packages/adapter-pg/src/pool-config.ts
|
|
305
|
-
import { VibeError as
|
|
483
|
+
import { VibeError as VibeError3 } from "@vibeorm/schema";
|
|
306
484
|
var PLAN_CACHE_MODES = ["auto", "force_custom_plan", "force_generic_plan"];
|
|
307
485
|
function isPlanCacheMode(value) {
|
|
308
486
|
return typeof value === "string" && PLAN_CACHE_MODES.includes(value);
|
|
@@ -311,7 +489,7 @@ function assertPlanCacheMode(params) {
|
|
|
311
489
|
const { value } = params;
|
|
312
490
|
if (isPlanCacheMode(value))
|
|
313
491
|
return value;
|
|
314
|
-
throw new
|
|
492
|
+
throw new VibeError3({
|
|
315
493
|
code: "VIBE_CONFIG",
|
|
316
494
|
message: `planCacheMode must be one of ${PLAN_CACHE_MODES.map((mode) => `"${mode}"`).join(", ")}, received ${JSON.stringify(value)}`,
|
|
317
495
|
meta: { planCacheMode: value, allowed: PLAN_CACHE_MODES }
|
|
@@ -398,7 +576,7 @@ function createStatementNameCache(params) {
|
|
|
398
576
|
}
|
|
399
577
|
|
|
400
578
|
// packages/adapter-pg/src/transaction-sql.ts
|
|
401
|
-
import { VibeError as
|
|
579
|
+
import { VibeError as VibeError4 } from "@vibeorm/schema";
|
|
402
580
|
import { refuseNestedTransactionOptions } from "@vibeorm/runtime/adapter-kit";
|
|
403
581
|
var ISOLATION_LEVEL_SQL = Object.freeze({
|
|
404
582
|
ReadCommitted: "READ COMMITTED",
|
|
@@ -421,7 +599,7 @@ function openTransactionStatement(params) {
|
|
|
421
599
|
function statementTimeoutStatement(params) {
|
|
422
600
|
const { timeout } = params;
|
|
423
601
|
if (!Number.isInteger(timeout) || timeout < 0) {
|
|
424
|
-
throw new
|
|
602
|
+
throw new VibeError4({
|
|
425
603
|
code: "VIBE_VALIDATION",
|
|
426
604
|
message: `transaction timeout must be a non-negative integer of milliseconds, received ${String(timeout)}`,
|
|
427
605
|
meta: { timeout }
|
|
@@ -461,13 +639,13 @@ function finalResult(params) {
|
|
|
461
639
|
var POOL_STATEMENT_NAMES = new WeakMap;
|
|
462
640
|
function isConnectionLoss(params) {
|
|
463
641
|
const { error } = params;
|
|
464
|
-
if (!(error instanceof
|
|
642
|
+
if (!(error instanceof VibeError5))
|
|
465
643
|
return true;
|
|
466
644
|
const sqlstate = error.meta.sqlstate;
|
|
467
645
|
return typeof sqlstate !== "string" || sqlstate.startsWith("08") || ["57P01", "57P02", "57P03"].includes(sqlstate);
|
|
468
646
|
}
|
|
469
647
|
function commitOutcomeUnknownError(params) {
|
|
470
|
-
return new
|
|
648
|
+
return new VibeError5({
|
|
471
649
|
code: "VIBE_TRANSACTION",
|
|
472
650
|
message: "the connection was lost while COMMIT was in flight, so the transaction's outcome is unknown: the write may have committed. The connection was discarded; inspect the data before retrying.",
|
|
473
651
|
meta: { provider: PROVIDER, reason: "transaction-outcome-unknown", outcome: "unknown", stage: "commit" },
|
|
@@ -496,7 +674,7 @@ function observeReadyForQuery(params) {
|
|
|
496
674
|
client.on("end", onEnd);
|
|
497
675
|
return {
|
|
498
676
|
reusableAfter: async ({ error, named }) => {
|
|
499
|
-
if (!(error instanceof
|
|
677
|
+
if (!(error instanceof VibeError5))
|
|
500
678
|
return false;
|
|
501
679
|
const sqlstate = error.meta.sqlstate;
|
|
502
680
|
if (typeof sqlstate !== "string" || NEVER_REUSE_SQLSTATE_CLASSES.has(sqlstate.slice(0, 2)))
|
|
@@ -547,7 +725,7 @@ var TIMESTAMP_OID = 1114;
|
|
|
547
725
|
var TIMESTAMP_ARRAY_OID = 1115;
|
|
548
726
|
var DATE_OID = 1082;
|
|
549
727
|
var DATE_ARRAY_OID = 1182;
|
|
550
|
-
var driverTypeParser =
|
|
728
|
+
var driverTypeParser = pg3.types.getTypeParser;
|
|
551
729
|
var ZONE_LESS_TEXT = /^\d{4}-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?)?$/;
|
|
552
730
|
function parseZoneLessAsUtc(value) {
|
|
553
731
|
if (ZONE_LESS_TEXT.test(value)) {
|
|
@@ -588,7 +766,7 @@ function takeAddress(params) {
|
|
|
588
766
|
const connectionString = options?.connectionString;
|
|
589
767
|
if (typeof connectionString === "string" && connectionString.trim() !== "")
|
|
590
768
|
return { connectionString };
|
|
591
|
-
throw new
|
|
769
|
+
throw new VibeError5({
|
|
592
770
|
code: "VIBE_CONFIG",
|
|
593
771
|
message: "pgAdapter needs an explicit address: pass `connectionString` (a PostgreSQL URL) or an existing `pool`. " + "The adapter does not read DATABASE_URL or the PG* environment variables.",
|
|
594
772
|
meta: { adapter: "pg", reason: "missing-address" }
|
|
@@ -596,11 +774,13 @@ function takeAddress(params) {
|
|
|
596
774
|
}
|
|
597
775
|
function pgAdapter(options) {
|
|
598
776
|
const address = takeAddress({ options });
|
|
777
|
+
const cancellationAddress = options.cancellationConnectionString ?? ("connectionString" in address ? address.connectionString : undefined);
|
|
778
|
+
const readCancellation = cancellationAddress === undefined ? "before-dispatch" : "running";
|
|
599
779
|
const usePrepared = options?.preparedStatements ?? false;
|
|
600
780
|
const stmtCacheMax = options?.stmtCacheMax ?? 1000;
|
|
601
781
|
const planCacheMode = options?.planCacheMode === undefined ? "auto" : assertPlanCacheMode({ value: options.planCacheMode });
|
|
602
782
|
if (planCacheMode !== "auto" && "pool" in address) {
|
|
603
|
-
throw new
|
|
783
|
+
throw new VibeError5({
|
|
604
784
|
code: "VIBE_CONFIG",
|
|
605
785
|
message: "planCacheMode configures the pool the adapter builds \u2014 it cannot be applied to a caller-supplied `pool`; " + 'set `options: "-c plan_cache_mode=\u2026"` on that pool instead',
|
|
606
786
|
meta: { planCacheMode }
|
|
@@ -626,7 +806,7 @@ function pgAdapter(options) {
|
|
|
626
806
|
...options?.connectionTimeout === undefined ? {} : { connectionTimeout: options.connectionTimeout },
|
|
627
807
|
planCacheMode
|
|
628
808
|
});
|
|
629
|
-
const PoolCtor =
|
|
809
|
+
const PoolCtor = pg3.Pool;
|
|
630
810
|
pool = new PoolCtor(config);
|
|
631
811
|
pool.on?.("error", () => {});
|
|
632
812
|
ownsPool = true;
|
|
@@ -670,8 +850,7 @@ function pgAdapter(options) {
|
|
|
670
850
|
return { rows: final.rows, affectedRows: final.rowCount ?? 0 };
|
|
671
851
|
}
|
|
672
852
|
function createClientAdapter(params) {
|
|
673
|
-
const { dispatch, scope, budget, deferred } = params;
|
|
674
|
-
const client = { query: (config) => dispatch({ config, batchable: true }) };
|
|
853
|
+
const { dispatch, scope, budget, deferred, cancellation } = params;
|
|
675
854
|
const rawClient = { query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 }) };
|
|
676
855
|
const savepointControl = (statement, cleanup) => {
|
|
677
856
|
if (deferred !== null && deferred.routeSavepoint({ text: statement }) !== "send")
|
|
@@ -688,17 +867,22 @@ function pgAdapter(options) {
|
|
|
688
867
|
provider: PROVIDER,
|
|
689
868
|
wire: WIRE,
|
|
690
869
|
budgets: BUDGETS,
|
|
870
|
+
readCancellation: cancellation.support,
|
|
691
871
|
roundTrips: ROUND_TRIPS,
|
|
692
872
|
rowChanges: () => {
|
|
693
873
|
budget.assertUsable({ stage: "statement" });
|
|
694
874
|
return readRowChanges({ scope });
|
|
695
875
|
},
|
|
696
876
|
async execute(execParams) {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
877
|
+
return runReadExecution({ ...execParams, support: cancellation.support, provider: PROVIDER, run: async () => {
|
|
878
|
+
const turn = scopeTurn({ scope });
|
|
879
|
+
if (turn !== undefined)
|
|
880
|
+
await turn;
|
|
881
|
+
budget.assertUsable({ stage: "statement" });
|
|
882
|
+
assertReadExecution2({ ...execParams, support: cancellation.support, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
883
|
+
const queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== "running", execution: execParams.execution, onReadControl: execParams.onReadControl }) };
|
|
884
|
+
return runExecute({ queryable, scope, ...execParams }).catch((error) => abortTransactionOnServerFailure({ scope, error }));
|
|
885
|
+
} });
|
|
702
886
|
},
|
|
703
887
|
async executeUnsafe(execParams) {
|
|
704
888
|
const turn = scopeTurn({ scope });
|
|
@@ -714,7 +898,7 @@ function pgAdapter(options) {
|
|
|
714
898
|
scope,
|
|
715
899
|
execute: (statement) => savepointControl(statement, false),
|
|
716
900
|
cleanup: (statement) => savepointControl(statement, true),
|
|
717
|
-
body: (child) => fn(createClientAdapter({ dispatch, scope: child, budget, deferred }))
|
|
901
|
+
body: (child) => fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation }))
|
|
718
902
|
});
|
|
719
903
|
},
|
|
720
904
|
async connect() {
|
|
@@ -782,28 +966,35 @@ function pgAdapter(options) {
|
|
|
782
966
|
});
|
|
783
967
|
},
|
|
784
968
|
async execute(params) {
|
|
969
|
+
assertReadExecution2({ ...params, support: readCancellation, provider: PROVIDER });
|
|
970
|
+
if (params.execution?.cancellation === "running") {
|
|
971
|
+
const options = { accessMode: "readOnly" };
|
|
972
|
+
return root.transaction((tx) => tx.execute(params), options);
|
|
973
|
+
}
|
|
785
974
|
const config = buildQueryConfig({ text: params.text, values: params.values });
|
|
786
975
|
const rootPool = getPool();
|
|
787
976
|
if (!rootViaPoolQuery) {
|
|
788
977
|
const client = await runMapped(() => rootPool.connect());
|
|
789
|
-
if (hasReadyForQuerySeam({ client })) {
|
|
790
|
-
const seam = observeReadyForQuery({ client });
|
|
978
|
+
if (hasReadyForQuerySeam({ client }) || params.execution !== undefined) {
|
|
979
|
+
const seam = hasReadyForQuerySeam({ client }) ? observeReadyForQuery({ client }) : undefined;
|
|
791
980
|
let reuse = false;
|
|
792
981
|
try {
|
|
982
|
+
assertReadExecution2({ ...params, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
793
983
|
const result = await runMapped(() => client.query(config));
|
|
794
984
|
reuse = true;
|
|
795
985
|
return finalResult({ result }).rows;
|
|
796
986
|
} catch (error) {
|
|
797
|
-
reuse = await seam
|
|
987
|
+
reuse = await seam?.reusableAfter({ error, named: config.name !== undefined }) ?? false;
|
|
798
988
|
throw error;
|
|
799
989
|
} finally {
|
|
800
|
-
seam
|
|
990
|
+
seam?.detach();
|
|
801
991
|
client.release(!reuse);
|
|
802
992
|
}
|
|
803
993
|
}
|
|
804
994
|
client.release();
|
|
805
995
|
rootViaPoolQuery = true;
|
|
806
996
|
}
|
|
997
|
+
assertReadExecution2({ ...params, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
807
998
|
return finalResult({ result: await runMapped(() => rootPool.query(config)) }).rows;
|
|
808
999
|
},
|
|
809
1000
|
async executeUnsafe(params) {
|
|
@@ -871,9 +1062,12 @@ function pgAdapter(options) {
|
|
|
871
1062
|
const sendWithControl = (dispatchParams) => {
|
|
872
1063
|
const { config, cleanup } = dispatchParams;
|
|
873
1064
|
const handOver = () => {
|
|
1065
|
+
assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
874
1066
|
dispatchParams.sent?.();
|
|
875
|
-
return client.query(config);
|
|
1067
|
+
return cancellation.run({ config, execution: dispatchParams.execution, onReadControl: dispatchParams.onReadControl, send: () => client.query(config) });
|
|
876
1068
|
};
|
|
1069
|
+
if (cleanup !== true && cancellationAttempted)
|
|
1070
|
+
throw transactionAbortedError({ cause: readCancelledError2({ stage: "running" }) });
|
|
877
1071
|
if (cleanup !== true && openingFailure !== undefined)
|
|
878
1072
|
throw transactionAbortedError({ cause: openingFailure });
|
|
879
1073
|
let bound;
|
|
@@ -903,6 +1097,7 @@ function pgAdapter(options) {
|
|
|
903
1097
|
if (bound !== undefined)
|
|
904
1098
|
control.push(bound);
|
|
905
1099
|
const openingSteps = carriesOpening ? opening.length : 0;
|
|
1100
|
+
assertReadExecution2({ execution: dispatchParams.execution, effect: "read", support: readCancellation, provider: PROVIDER, stage: "waiting-for-connection" });
|
|
906
1101
|
if (statement.length > 0)
|
|
907
1102
|
dispatchParams.sent?.();
|
|
908
1103
|
const burst = runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES }).then((outcome) => {
|
|
@@ -935,24 +1130,43 @@ function pgAdapter(options) {
|
|
|
935
1130
|
}
|
|
936
1131
|
};
|
|
937
1132
|
let tail = Promise.resolve();
|
|
1133
|
+
const activeDispatches = new Set;
|
|
1134
|
+
let cancellationQueue = false;
|
|
938
1135
|
const dispatch = (dispatchParams) => {
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1136
|
+
if (dispatchParams.execution?.cancellation === "running" && !cancellationQueue) {
|
|
1137
|
+
cancellationQueue = true;
|
|
1138
|
+
tail = Promise.allSettled([...activeDispatches]);
|
|
1139
|
+
}
|
|
1140
|
+
if (budget.totalMs === null && !cancellationQueue) {
|
|
1141
|
+
const pending = inFlight === null ? send(dispatchParams) : inFlight.then(() => send(dispatchParams));
|
|
1142
|
+
activeDispatches.add(pending);
|
|
1143
|
+
pending.then(() => activeDispatches.delete(pending), () => activeDispatches.delete(pending));
|
|
1144
|
+
return pending;
|
|
943
1145
|
}
|
|
944
1146
|
const pending = tail.then(() => send(dispatchParams));
|
|
945
1147
|
tail = pending.then(ignore, ignore);
|
|
946
1148
|
return pending;
|
|
947
1149
|
};
|
|
948
1150
|
let discard = false;
|
|
1151
|
+
let released = false;
|
|
1152
|
+
let cancellationAttempted = false;
|
|
1153
|
+
const cancellation = createPgReadCancellation({ connectionString: cancellationAddress, target: client, terminate: () => {
|
|
1154
|
+
if (!released) {
|
|
1155
|
+
released = true;
|
|
1156
|
+
client.release(true);
|
|
1157
|
+
}
|
|
1158
|
+
}, discard: () => {
|
|
1159
|
+
discard = true;
|
|
1160
|
+
cancellationAttempted = true;
|
|
1161
|
+
markTransactionAborted({ scope, cause: readCancelledError2({ stage: "running" }) });
|
|
1162
|
+
} });
|
|
949
1163
|
let commitProgress = "not-sent";
|
|
950
1164
|
try {
|
|
951
1165
|
if (!deferOpening)
|
|
952
1166
|
await runMapped(() => client.query({ text: openText }));
|
|
953
1167
|
const result = await runRootScope({
|
|
954
1168
|
scope,
|
|
955
|
-
body: () => fn(createClientAdapter({ dispatch, scope, budget, deferred: capable ? deferred : null }))
|
|
1169
|
+
body: () => fn(createClientAdapter({ dispatch, scope, budget, deferred: capable ? deferred : null, cancellation }))
|
|
956
1170
|
});
|
|
957
1171
|
budget.assertUsable({ stage: "commit" });
|
|
958
1172
|
if (!deferred.opened()) {
|
|
@@ -1014,7 +1228,8 @@ function pgAdapter(options) {
|
|
|
1014
1228
|
});
|
|
1015
1229
|
} finally {
|
|
1016
1230
|
unguard();
|
|
1017
|
-
|
|
1231
|
+
if (!released)
|
|
1232
|
+
client.release(discard);
|
|
1018
1233
|
}
|
|
1019
1234
|
},
|
|
1020
1235
|
async connect() {
|
|
@@ -1027,13 +1242,14 @@ function pgAdapter(options) {
|
|
|
1027
1242
|
provider: PROVIDER,
|
|
1028
1243
|
wire: WIRE,
|
|
1029
1244
|
budgets: BUDGETS,
|
|
1245
|
+
readCancellation,
|
|
1030
1246
|
roundTrips: ROUND_TRIPS,
|
|
1031
1247
|
transactionOutcome: "reported",
|
|
1032
1248
|
withSession({ run }) {
|
|
1033
1249
|
return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });
|
|
1034
1250
|
},
|
|
1035
1251
|
execute(params) {
|
|
1036
|
-
return lifecycle.admit({ run: () => root.execute(params) });
|
|
1252
|
+
return lifecycle.admit({ run: () => params.execution === undefined ? root.execute(params) : runReadExecution({ ...params, support: readCancellation, provider: PROVIDER, run: () => root.execute(params) }) });
|
|
1037
1253
|
},
|
|
1038
1254
|
executeUnsafe(params) {
|
|
1039
1255
|
return lifecycle.admit({ run: () => root.executeUnsafe(params) });
|
|
@@ -1081,4 +1297,4 @@ export {
|
|
|
1081
1297
|
validateTransactionDeadline2 as validateTransactionDeadline
|
|
1082
1298
|
};
|
|
1083
1299
|
|
|
1084
|
-
//# debugId=
|
|
1300
|
+
//# debugId=3DBA8F0584DC025664756E2164756E21
|
package/dist/index.js.map
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/batch.ts", "../src/errors.ts", "../src/params.ts", "../src/pool-config.ts", "../src/statement-cache.ts", "../src/transaction-sql.ts"],
|
|
3
|
+
"sources": ["../src/index.ts", "../src/batch.ts", "../src/read-cancellation.ts", "../src/config.ts", "../src/errors.ts", "../src/params.ts", "../src/pool-config.ts", "../src/statement-cache.ts", "../src/transaction-sql.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * @vibeorm/adapter-pg — node-postgres (`pg`) adapter for VibeORM v2.\n *\n * Runs on Bun and Node. Owns pooling, optional named prepared statements,\n * transactions (nested → savepoints on the same client) and driver-error\n * mapping. Only `VibeError` ever escapes.\n */\n\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n RowChangeCount,\n StatementEffect,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport {\n type BudgetStage,\n abortTransactionOnServerFailure,\n createDeferredControl,\n type DeferredControl,\n observeRowChanges,\n readRowChanges,\n statementRowChanges,\n createAdapterLifecycleGate,\n runPinnedSession,\n runShutdownCleanup,\n transactionLifecycleRefusal,\n createSavepointScope,\n engineStatementBudgetMs,\n type CommitProgress,\n isSavepointRollbackFailedError,\n readOnlyRequested,\n type RollbackOutcome,\n runNestedSavepoint,\n runRootScope,\n type SavepointScope,\n scopeTurn,\n settleFailedTransaction,\n startTransactionBudget,\n type TransactionBudget,\n transactionAbortedError,\n validateTransactionDeadline,\n} from \"@vibeorm/runtime/adapter-kit\";\nimport { VibeError } from \"@vibeorm/schema\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport pg from \"pg\";\nimport { firstBatchFailure, prepareBatchValues, runBatch, supportsBatch, type BatchStep } from \"./batch.ts\";\nimport { mapPgDriverError, runMapped } from \"./errors.ts\";\nimport { PgArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nimport { assertPlanCacheMode, buildPoolConfig, type PlanCacheMode } from \"./pool-config.ts\";\nimport { createStatementNameCache, type StatementNameCache } from \"./statement-cache.ts\";\nimport {\n beginStatement,\n classifyRawTransactionControl,\n openTransactionStatement,\n refuseNestedTransactionOptions,\n statementTimeoutStatement,\n} from \"./transaction-sql.ts\";\n\n// ─── Minimal pg surface ───────────────────────────────────────────\n\n/** The single `pg` query form this adapter uses (config object, never overloads). */\nexport type PgQueryConfig = {\n readonly text: string;\n readonly values?: unknown[];\n readonly name?: string;\n /**\n * QUERY-LOCAL result parsers ({@link EXACT_TYPES}). node-postgres reads this\n * off the query config, so nothing outside VibeORM's own statements is\n * affected — a caller's pool keeps the driver's documented behaviour.\n */\n readonly types?: PgTypeParsers;\n};\n\n/** The `pg-types` surface a query config may carry. */\nexport type PgTypeParsers = {\n getTypeParser(oid: number, format?: \"text\" | \"binary\"): (value: string) => unknown;\n};\n\n/** The parts of a `pg` result this adapter reads. */\nexport type PgQueryResultLike = {\n readonly rows: Record<string, unknown>[];\n readonly rowCount: number | null;\n /**\n * The command tag (`INSERT`, `COMMIT`, `ROLLBACK`, …). Optional so test\n * doubles need not supply it; the adapter reads it after `COMMIT`, which\n * PostgreSQL answers with `ROLLBACK` — and no error — when the transaction\n * was aborted.\n */\n readonly command?: string;\n};\n\n/** pg returns one result per command for simple-protocol scripts. */\nexport type PgQueryResultsLike = PgQueryResultLike | PgQueryResultLike[];\n\n/** The parts of a `pg` PoolClient this adapter uses. */\nexport type PgPoolClientLike = {\n query(config: PgQueryConfig): Promise<PgQueryResultsLike>;\n /**\n * Hand the client back. A TRUTHY argument tells node-postgres to DESTROY it\n * instead of pooling it — used when a rollback failed and the session state\n * can no longer be asserted (EPIC 6). Optional so existing test doubles that\n * declare `release(): void` still satisfy this type.\n */\n release(destroy?: boolean): void;\n /**\n * EventEmitter surface. A real `pg.PoolClient` has it; test doubles need not,\n * so both members are optional and the adapter feature-detects them.\n */\n on?(event: \"error\", listener: (error: unknown) => void): void;\n removeListener?(event: \"error\", listener: (error: unknown) => void): void;\n};\n\n/** The parts of a `pg` Pool this adapter uses — satisfied by a real `pg.Pool`. */\nexport type PgPoolLike = {\n connect(): Promise<PgPoolClientLike>;\n query(config: PgQueryConfig): Promise<PgQueryResultsLike>;\n end(): Promise<void>;\n /** EventEmitter surface; optional for the same reason as on {@link PgPoolClientLike}. */\n on?(event: \"error\", listener: (error: unknown, client: PgPoolClientLike) => void): void;\n};\n\n/** Anything that can run a query: the pool itself or a checked-out client. */\ntype Queryable = { query(config: PgQueryConfig): Promise<PgQueryResultsLike> };\n\ntype TransactionDispatch = (params: {\n config: PgQueryConfig;\n stage?: BudgetStage;\n cleanup?: boolean;\n /**\n * The text is ONE statement and may ride in a one-Sync batch behind pending\n * control (EPIC 6). False for raw text without bound values, which\n * node-postgres sends as a simple-protocol message that may hold several\n * statements.\n */\n batchable?: boolean;\n /**\n * Called immediately before the statement is handed to node-postgres —\n * after every check that can still refuse it. COMMIT uses it to record that\n * it was sent, the fact its outcome is decided by (EPIC T).\n */\n sent?: () => void;\n}) => Promise<PgQueryResultsLike>;\n\n/** PostgreSQL's ceiling for `statement_timeout` (an int4 of milliseconds). */\nconst MAX_STATEMENT_TIMEOUT_MS = 2_147_483_647;\n\nconst ignore = (): undefined => undefined;\n\nfunction finalResult(params: { result: PgQueryResultsLike }): PgQueryResultLike {\n return Array.isArray(params.result)\n ? (params.result.at(-1) ?? { rows: [], rowCount: 0 })\n : params.result;\n}\n\n/**\n * Keep a CHECKED-OUT client's background failures from killing the process,\n * returning the detach to run before the client goes back to the pool.\n *\n * node-postgres listens for `'error'` on a pooled client only while that client\n * sits IDLE, and removes the listener the moment the client is checked out\n * (restoring it on release). A client this adapter holds — for a transaction,\n * or as a pinned raw session — therefore has NO listener, so a backend that\n * dies mid-transaction reaches `client.emit('error', …)` with none. An\n * `'error'` event without a listener is an uncaught exception: fatal under\n * Node, which turns a database failover into a dead application process.\n *\n * The listener deliberately does nothing with the error and is NOT an error\n * channel: the in-flight query already rejects on its own with the mapped\n * `VibeError`, and the pool discards the broken client. Its only job is to keep\n * the process alive. Detaching before `release()` matters just as much — a\n * listener left attached would shadow pg's own idle listener and stop the pool\n * evicting a client that fails later, while idle.\n */\n/**\n * Prepared-statement name registries, one per POOL (F16/F18) — see\n * ./statement-cache.ts for why they are shared and bounded. Weakly held, so a\n * pool that goes out of scope takes its registry with it.\n */\nconst POOL_STATEMENT_NAMES: WeakMap<PgPoolLike, StatementNameCache> = new WeakMap();\n\n/**\n * Whether a mapped driver failure means the CONNECTION is gone rather than the\n * server having answered: no SQLSTATE at all (socket-level), class 08\n * (connection exception) or an admin/crash shutdown (57P01–57P03). The same\n * rule pins a migration session as lost and decides whether a failed COMMIT's\n * outcome is unknown.\n */\nfunction isConnectionLoss(params: { error: unknown }): boolean {\n const { error } = params;\n if (!(error instanceof VibeError)) return true;\n const sqlstate = error.meta.sqlstate;\n return typeof sqlstate !== \"string\" || sqlstate.startsWith(\"08\") || [\"57P01\", \"57P02\", \"57P03\"].includes(sqlstate);\n}\n\n/**\n * A COMMIT whose acknowledgement never arrived: the write may or may not be\n * durable, and nothing on the client can tell. The mysql and bun adapters\n * report the same shape (`transaction-outcome-unknown`, stage `commit`).\n */\nfunction commitOutcomeUnknownError(params: { cause: unknown }): VibeError {\n return new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n \"the connection was lost while COMMIT was in flight, so the transaction's outcome is unknown: the write may have committed. The connection was discarded; inspect the data before retrying.\",\n meta: { provider: PROVIDER, reason: \"transaction-outcome-unknown\", outcome: \"unknown\", stage: \"commit\" },\n cause: params.cause,\n });\n}\n\n/**\n * SQLSTATE classes after which a session is never reused, whatever its\n * transaction status says: connection exception, insufficient resources,\n * operator intervention, system error, internal error.\n */\nconst NEVER_REUSE_SQLSTATE_CLASSES: ReadonlySet<string> = new Set([\"08\", \"53\", \"57\", \"58\", \"XX\"]);\n\n/**\n * SQLSTATEs after which a NAMED prepared statement can no longer be trusted on\n * the session that ran it (fix lane FX-A, RV2 MJ-1): the client's record of\n * the name and the backend's disagree. node-postgres keeps a name it once\n * parsed (`parsedStatements`) across errors, so a reused session would fail\n * the same way on every later call:\n * - `0A000` — \"cached plan must not change result type\" after a schema change;\n * - `26000` — the backend does not know the name (a transaction-mode pooler);\n * - `42P05` — the backend already holds the name, so every Parse is refused.\n * Destroying the client makes the next call prepare on a fresh session — the\n * self-healing behaviour `pool.query` gave before round-trip EPIC 3.\n */\nconst STALE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\"0A000\", \"26000\", \"42P05\"]);\n\n/** The node-postgres `Client` members the reuse seam needs; feature-detected, never assumed. */\ntype ReadyForQuerySurface = {\n getTransactionStatus?: () => string | null;\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n removeListener?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n once?: unknown;\n pipeline?: unknown;\n};\n\n/** What the root path learns from one checked-out client while its query runs. */\ntype ReadyForQuerySeam = {\n /**\n * After a failure: may this client go back to the pool? Never true without\n * proof. `named`: the failing query was a named prepared statement.\n */\n readonly reusableAfter: (params: { error: unknown; named: boolean }) => Promise<boolean>;\n /** Remove every listener; runs before the client is released. */\n readonly detach: () => void;\n};\n\n/**\n * Round-trip EPIC 3 (A-12): prove that a root ORM statement's failure left its\n * session idle, so the client can be pooled instead of destroyed (today\n * `pool.query` destroys it on ANY error, and the next call pays a reconnect).\n *\n * Listeners for `drain` / `error` / `end` are attached BEFORE the query is\n * submitted. node-postgres sets `readyForQuery = false` when it sends a query\n * on an idle client and emits `drain` only from `_pulseQueryQueue` after\n * `_handleReadyForQuery` stored the status of THAT query's ReadyForQuery\n * (pg 8.23 `client.js` 373-396, 616-641), so the first `drain` after\n * submission carries the failing query's own status. Sampling the status at\n * rejection instead is timing-dependent (spike §3: stale in 59/200 compound\n * runs). A lost connection emits `error` / `end`, so the wait always ends.\n *\n * Reuse needs ALL of: the mapped error has a SQLSTATE outside\n * {@link NEVER_REUSE_SQLSTATE_CLASSES} (and, for a named statement, outside\n * {@link STALE_STATEMENT_SQLSTATES}); the first event was `drain`; the status\n * after it is `I`; no `error` event during the checkout. Anything else destroys\n * the client — today's behaviour. The `error` listener also keeps a background\n * failure of the checked-out client from becoming an uncaught exception (see\n * {@link guardBackgroundErrors}). Only for a client that passed\n * {@link hasReadyForQuerySeam}.\n */\nfunction observeReadyForQuery(params: { client: PgPoolClientLike }): ReadyForQuerySeam {\n const client = params.client as PgPoolClientLike & Required<Pick<ReadyForQuerySurface, \"on\" | \"removeListener\" | \"getTransactionStatus\">>;\n let backgroundFailure = false;\n let settle: (event: \"drain\" | \"error\" | \"end\") => void = () => undefined;\n const firstEvent = new Promise<\"drain\" | \"error\" | \"end\">((resolve) => { settle = resolve; });\n const onDrain = (): void => settle(\"drain\");\n const onError = (): void => { backgroundFailure = true; settle(\"error\"); };\n const onEnd = (): void => settle(\"end\");\n client.on(\"error\", onError);\n client.on(\"drain\", onDrain);\n client.on(\"end\", onEnd);\n return {\n reusableAfter: async ({ error, named }) => {\n if (!(error instanceof VibeError)) return false;\n const sqlstate = error.meta.sqlstate;\n if (typeof sqlstate !== \"string\" || NEVER_REUSE_SQLSTATE_CLASSES.has(sqlstate.slice(0, 2))) return false;\n if (named && STALE_STATEMENT_SQLSTATES.has(sqlstate)) return false;\n const event = await firstEvent;\n return event === \"drain\" && !backgroundFailure && client.getTransactionStatus() === \"I\";\n },\n detach: () => {\n client.removeListener(\"error\", onError);\n client.removeListener(\"drain\", onDrain);\n client.removeListener(\"end\", onEnd);\n },\n };\n}\n\n/**\n * Whether a checked-out client exposes node-postgres' events and status the\n * reuse seam needs (`on`, `removeListener`, `once`, `getTransactionStatus`) and\n * is not a `pipeline: true` client. A client without them (an injected pool's\n * wrapper) cannot even carry the `error` listener that keeps a socket failure\n * from becoming an uncaught exception, so the root path never runs a\n * statement on it (fix lane FX-A, RV2 MJ-3).\n */\nfunction hasReadyForQuerySeam(params: { client: PgPoolClientLike }): boolean {\n const client = params.client as PgPoolClientLike & ReadyForQuerySurface;\n return typeof client.on === \"function\" &&\n typeof client.removeListener === \"function\" &&\n typeof client.once === \"function\" &&\n typeof client.getTransactionStatus === \"function\" &&\n client.pipeline !== true;\n}\n\nfunction guardBackgroundErrors(params: { client: PgPoolClientLike; onError?: (error: unknown) => void }): () => void {\n const { client, onError } = params;\n if (typeof client.on !== \"function\") return (): void => undefined;\n const swallow = (error: unknown): void => {\n /* the failing call rejects with a mapped VibeError; nothing to report here */\n onError?.(error);\n };\n client.on(\"error\", swallow);\n return (): void => {\n client.removeListener?.(\"error\", swallow);\n };\n}\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/**\n * Every {@link pgAdapter} option. {@link PgAdapterOptions} adds the rule that\n * one of `connectionString` and `pool` is present.\n */\nexport type PgAdapterSettings = {\n /**\n * PostgreSQL connection URL. The adapter never reads `DATABASE_URL` (or any\n * other environment variable) itself: pass the URL you mean. Give a complete\n * URL — node-postgres fills parts the URL omits (host, user, database, …)\n * from the `PG*` environment variables.\n */\n readonly connectionString?: string;\n /**\n * An existing `pg.Pool` to use instead of creating one. The adapter never\n * ends a pool it did not create: `disconnect()` only ends this adapter (and\n * rolls back and releases its own raw session); the pool stays usable.\n * Wins over `connectionString` when both are given.\n */\n readonly pool?: PgPoolLike;\n /** Maximum pooled connections (default 10). Ignored when `pool` is given. */\n readonly max?: number;\n /**\n * Pool-wide `statement_timeout` in milliseconds. Every connection inherits\n * it; a per-transaction `timeout` overrides it for that transaction.\n */\n readonly statementTimeout?: number;\n /** Milliseconds to wait for a pool checkout before failing (pg's `connectionTimeoutMillis`). */\n readonly connectionTimeout?: number;\n /**\n * Send ORM-path queries as named prepared statements (default false).\n *\n * Measured on this repo's benchmark (postgres 16, interleaved rounds):\n * −17…−25 % on reads, −6 % on a by-key update — the server skips the\n * parse/analyze and the protocol Parse/Describe round on every repeat. The\n * gain survives {@link PgAdapterOptions.planCacheMode} `force_custom_plan`,\n * which removes the classic downside (a generic plan after ~5 executions\n * picking worse indexes for range/LIKE/skewed filters).\n *\n * Off by default because named statements live on the BACKEND connection:\n * behind a transaction-mode pooler (PgBouncer, Supavisor, Neon's pooler)\n * without prepared-statement support a client that remembers a name from an\n * earlier transaction is handed another backend later — SQLSTATE 26000,\n * 42P05, or, worst, another client's statement under the same name executed\n * without any error. Turn it on for direct connections and session pooling.\n *\n * Schema changes can invalidate a retained result descriptor (SQLSTATE\n * 0A000). No statement or transaction callback is retried. Drain traffic and\n * recycle backend connections across incompatible schema deployments. For\n * an injected pool its owner must replace the pool; adapter.disconnect()\n * does not end it. force_custom_plan does not remove this requirement.\n */\n readonly preparedStatements?: boolean;\n /**\n * Budget of prepared-statement NAMES per pool (default 1000). Names never\n * evict — a name evicted here would stay prepared on every backend that saw\n * it — so this is a bound on the server's retention per connection, and a\n * text arriving after the budget is spent runs unnamed (it still binds its\n * parameters; only the server-side plan cache is skipped). A name is\n * allocated on a text's SECOND sighting, so one-off texts — offset pagination\n * emits one per page — never spend the budget.\n */\n readonly stmtCacheMax?: number;\n /**\n * PostgreSQL `plan_cache_mode` for every connection of the pool this adapter\n * builds (default `\"auto\"`, the server default — nothing is sent). Pair\n * `\"force_custom_plan\"` with {@link PgAdapterOptions.preparedStatements}: the\n * statement is still prepared once, but planned with the real parameter\n * values on every execution. Rides the `options` startup parameter, merged\n * with whatever the connection string already carries (a URL that sets\n * `plan_cache_mode` itself wins). Refused with `VIBE_CONFIG` together with\n * a caller-supplied `pool` — the adapter never reconfigures a pool it does\n * not own; set it on that pool's own `options` instead.\n */\n readonly planCacheMode?: PlanCacheMode;\n};\n\n/**\n * Options for {@link pgAdapter}: an explicit address is required. Either point\n * it at a connection string (the adapter then owns the pool it creates) or hand\n * it a pool you already own. Without either, the factory throws `VIBE_CONFIG`\n * before any pool or connection exists.\n */\nexport type PgAdapterOptions = PgAdapterSettings &\n ({ readonly connectionString: string } | { readonly pool: PgPoolLike });\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\nconst DIALECT: Dialect = \"postgres\";\nconst PROVIDER: Provider = \"postgresql\";\n\n/**\n * node-postgres wire (board #30, live-verified on both execute paths):\n * timestamptz → `Date`, json/jsonb → parsed values, int4/float8 → numbers.\n * (int8 stays a string — BigInt decoding is real work and never pruned.)\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"native\", json: \"parsed\", numbers: \"native\" } as const);\n\n/**\n * A PostgreSQL server: every round trip crosses a network (a local socket\n * included), so the runtime's one-statement folds apply. The transaction\n * facades carry the same declaration.\n */\nconst ROUND_TRIPS: NonNullable<DatabaseAdapter[\"roundTrips\"]> = \"network\";\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: true` — {@link PgAdapterOptions.connectionTimeout} maps to\n * node-postgres' `connectionTimeoutMillis`, which bounds the checkout wait\n * and rejects when the pool stays exhausted. The capability is declared here;\n * it is OFF until the option is set (pg's own default is \"wait forever\").\n * - `statementTimeout: \"all-statements\"` — PostgreSQL's `statement_timeout`\n * cancels ANY over-long statement, writes included (SQLSTATE 57014).\n * - `transactionDeadline: \"cancel-running-statements\"` — the strongest level:\n * the remaining budget is handed to the SERVER before each statement, so the\n * database itself stops work that would outlive the deadline. Nothing here is\n * a `Promise.race` over a statement that keeps running.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: true,\n statementTimeout: \"all-statements\",\n transactionDeadline: \"cancel-running-statements\",\n accessModes: Object.freeze([\"readOnly\"] as const),\n} as const);\n\n// ─── Exact numeric arrays (F12) ───────────────────────────────\n\n/** `_numeric`: the array form of `numeric`/`DECIMAL`. */\nconst NUMERIC_ARRAY_OID = 1231;\n/** `_text`: its parser splits a postgres array literal into RAW element strings. */\nconst TEXT_ARRAY_OID = 1009;\n/** `timestamp without time zone` and `date` — the zone-less types — and their array forms. */\nconst TIMESTAMP_OID = 1114;\nconst TIMESTAMP_ARRAY_OID = 1115;\nconst DATE_OID = 1082;\nconst DATE_ARRAY_OID = 1182;\n\n/**\n * `pg.types.getTypeParser` behind a plain numeric-OID signature. `@types/pg`\n * narrows the two-argument overload to its own `TypeId` union of well-known\n * OIDs, which does not include every array OID this function must look up.\n */\nconst driverTypeParser: (oid: number, format?: \"text\" | \"binary\") => (value: string) => unknown =\n pg.types.getTypeParser as unknown as (oid: number, format?: \"text\" | \"binary\") => (value: string) => unknown;\n\n/** postgres' text output for the zone-less types: `2026-03-08 19:38:08.538` or `2026-03-08`. */\nconst ZONE_LESS_TEXT = /^\\d{4}-\\d{2}-\\d{2}(?: \\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,6})?)?$/;\n\n/**\n * Zone-less date/timestamp text read as **UTC** — the same rule as the codec\n * table's `parseDateTimeText` (@vibeorm/sql), applied at the driver level\n * because node-postgres hands these columns to the ORM as `Date` objects\n * already (`wire.dateTime: \"native\"` prunes the codec decode). pg-types reads\n * them as host-LOCAL time, so one stored row decoded to a different instant\n * on every host TZ, and to a different instant than bun:sql, PGlite's ORM\n * path and Prisma (all UTC) return. `infinity`, BC dates and anything else\n * outside the plain form keep the driver's own parser.\n */\nfunction parseZoneLessAsUtc(value: string): unknown {\n if (ZONE_LESS_TEXT.test(value)) {\n const iso = value.includes(\" \") ? `${value.replace(\" \", \"T\").replace(/(\\.\\d{3})\\d+$/, \"$1\")}Z` : value;\n const parsed = new Date(iso);\n if (!Number.isNaN(parsed.getTime())) return parsed;\n }\n return driverTypeParser(TIMESTAMP_OID, \"text\")(value);\n}\n\n/** The array forms: the driver's `_text` parser splits the literal, elements go through {@link parseZoneLessAsUtc}. */\nfunction parseZoneLessArrayAsUtc(value: string): unknown {\n const mapElements = (element: unknown): unknown => {\n if (element === null) return null;\n if (Array.isArray(element)) return element.map(mapElements);\n return parseZoneLessAsUtc(String(element));\n };\n return mapElements(driverTypeParser(TEXT_ARRAY_OID, \"text\")(value));\n}\n\n/**\n * Query-local type parsers that keep `numeric[]` EXACT and read zone-less\n * timestamps as UTC.\n *\n * pg-types decodes `_numeric` with `parseFloat` per element, so\n * `9007199254740993` comes back as `9007199254740992` and `12.340` as the\n * number `12.34` — while the SCALAR `numeric` correctly stays a string and\n * VibeORM's Decimal contract is \"string in, string out\" on every dialect. The\n * array is the anomaly, and the loss happens inside the driver, before any\n * codec can see the value.\n *\n * The fix reuses the driver's OWN array-literal parser (`_text`), so array\n * syntax, quoting and NULL elements stay node-postgres' business; only the\n * per-element float conversion is dropped. It is attached PER QUERY (F12's\n * acceptance criterion), never by mutating `pg.types` globally: an injected\n * pool is shared property, and a global override would silently change every\n * other consumer in the process. The zone-less parsers ride the same hook for\n * the same reason.\n */\nconst EXACT_TYPES: PgTypeParsers = {\n getTypeParser(oid: number, format?: \"text\" | \"binary\"): (value: string) => unknown {\n if (format === undefined || format === \"text\") {\n if (oid === NUMERIC_ARRAY_OID) return driverTypeParser(TEXT_ARRAY_OID, \"text\");\n if (oid === TIMESTAMP_OID || oid === DATE_OID) return parseZoneLessAsUtc;\n if (oid === TIMESTAMP_ARRAY_OID || oid === DATE_ARRAY_OID) return parseZoneLessArrayAsUtc;\n }\n return driverTypeParser(oid, format);\n },\n};\n\n/**\n * The one address this adapter will ever use, taken at construction: an\n * injected pool (it wins, as documented) or a non-empty connection string.\n * Nothing is read from the environment, and nothing is re-read later.\n *\n * @throws VibeError `VIBE_CONFIG` (`meta.reason: \"missing-address\"`) when\n * neither is present. The message never echoes a value.\n */\nfunction takeAddress(params: { options: PgAdapterOptions | undefined }): { pool: PgPoolLike } | { connectionString: string } {\n const options: PgAdapterSettings | undefined = params.options;\n if (options?.pool !== undefined && options.pool !== null) return { pool: options.pool };\n const connectionString = options?.connectionString;\n if (typeof connectionString === \"string\" && connectionString.trim() !== \"\") return { connectionString };\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"pgAdapter needs an explicit address: pass `connectionString` (a PostgreSQL URL) or an existing `pool`. \" +\n \"The adapter does not read DATABASE_URL or the PG* environment variables.\",\n meta: { adapter: \"pg\", reason: \"missing-address\" },\n });\n}\n\n/**\n * Create a VibeORM adapter backed by node-postgres.\n *\n * Requires an explicit address (`connectionString` or `pool`); see\n * {@link PgAdapterOptions}. Construction opens nothing — the pool is created\n * on first use.\n *\n * @example\n * ```ts\n * const adapter = pgAdapter({ connectionString: \"postgresql://app:secret@db.internal:5432/app\", max: 20 });\n * await adapter.connect();\n * const rows = await adapter.execute({ text: 'SELECT * FROM \"User\" WHERE \"id\" = $1', values: [1] });\n * ```\n *\n * @example Reusing a pool you already own\n * ```ts\n * import pg from \"pg\";\n * const pool = new pg.Pool({ connectionString });\n * const adapter = pgAdapter({ pool });\n * ```\n */\nexport function pgAdapter(options: PgAdapterOptions): DatabaseAdapter {\n const address = takeAddress({ options });\n const usePrepared = options?.preparedStatements ?? false;\n const stmtCacheMax = options?.stmtCacheMax ?? 1000;\n const planCacheMode = options?.planCacheMode === undefined ? \"auto\" : assertPlanCacheMode({ value: options.planCacheMode });\n if (planCacheMode !== \"auto\" && \"pool\" in address) {\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"planCacheMode configures the pool the adapter builds — it cannot be applied to a caller-supplied `pool`; \" +\n \"set `options: \\\"-c plan_cache_mode=…\\\"` on that pool instead\",\n meta: { planCacheMode },\n });\n }\n\n let pool: PgPoolLike | null = null;\n let ownsPool = false;\n\n /**\n * The checked-out client of an OPEN raw transaction — session affinity for\n * the raw path. @vibeorm/migrate wraps transactional DDL in literal `BEGIN`\n * / `COMMIT` / `ROLLBACK` sent through `executeUnsafe`; without pinning,\n * `pool.query(\"BEGIN\")` sequences stay on one client only by LIFO checkout\n * luck, and any concurrent traffic silently splits the transaction across\n * connections. A pure `BEGIN` checks a client out, every raw statement until\n * the matching `COMMIT`/`ROLLBACK` rides it, and the close releases it. The\n * ORM path (`execute`) and `transaction()` stay on the pool. Concurrent raw\n * transactions on one adapter are unsupported.\n */\n let rawSession: PgPoolClientLike | null = null;\n /** Detach for {@link rawSession}'s background-error guard; runs before its release. */\n let rawSessionUnguard: (() => void) | null = null;\n /**\n * Set once a checked-out client lacked {@link hasReadyForQuerySeam}: the\n * pool hands out wrappers, so root ORM statements keep running through the\n * pool's own `query()` — the path (and the guard) they had before round-trip\n * EPIC 3 (fix lane FX-A, RV2 MJ-3 / MN-6c).\n */\n let rootViaPoolQuery = false;\n\n function getPool(): PgPoolLike {\n if (pool !== null) return pool;\n\n if (\"pool\" in address) {\n pool = address.pool;\n ownsPool = false;\n return pool;\n }\n\n const config = buildPoolConfig({\n connectionString: address.connectionString,\n max: options?.max ?? 10,\n ...(options?.statementTimeout === undefined ? {} : { statementTimeout: options.statementTimeout }),\n ...(options?.connectionTimeout === undefined ? {} : { connectionTimeout: options.connectionTimeout }),\n planCacheMode,\n });\n\n const PoolCtor = pg.Pool as unknown as new (poolConfig: Record<string, unknown>) => PgPoolLike;\n pool = new PoolCtor(config);\n /**\n * The IDLE half of the same hazard {@link guardBackgroundErrors} covers: a\n * pooled client that loses its socket while idle makes pg re-emit on the\n * POOL, and an unhandled `'error'` there is equally fatal. The pool already\n * evicts that client, so this listener only keeps the process alive.\n *\n * Attached ONLY to a pool this adapter constructed. A caller-supplied pool\n * (the `pool` option) has its own owner: the adapter never ends it either,\n * node-postgres documents attaching an `'error'` listener as that owner's\n * job, and registering one here that is never removed would stack up a\n * listener per adapter sharing that pool. The dangerous case — a client\n * checked out for a transaction or a raw session, which pg leaves\n * unlistened by construction — is guarded in BOTH modes.\n */\n pool.on?.(\"error\", () => {\n /* the pool has already discarded the failed client */\n });\n ownsPool = true;\n return pool;\n }\n\n /**\n * Prepared-statement names for the pool this adapter runs on (F18): the\n * registry is keyed by the POOL, so two adapters sharing one pool share one\n * name space instead of both starting at `vibeorm_0` and colliding on a\n * connection. It also bounds how many names that pool can ever put on a\n * backend (F16) no matter how many adapters come and go over it.\n */\n function statementNamesFor(params: { pool: PgPoolLike }): StatementNameCache {\n const existing = POOL_STATEMENT_NAMES.get(params.pool);\n if (existing !== undefined) return existing;\n const created = createStatementNameCache({ max: stmtCacheMax });\n POOL_STATEMENT_NAMES.set(params.pool, created);\n return created;\n }\n\n /** Build the config for one ORM-path query (statement name only when enabled). */\n function buildQueryConfig(params: { text: string; values: unknown[] }): PgQueryConfig {\n const values = serializeParams({ values: params.values });\n if (!usePrepared) return { text: params.text, values, types: EXACT_TYPES };\n // `undefined` on a text's first sighting and once the pool's name budget is\n // spent: the statement runs unnamed rather than growing the server's\n // prepared-statement retention.\n const name = statementNamesFor({ pool: getPool() }).getName({ text: params.text });\n return name === undefined\n ? { text: params.text, values, types: EXACT_TYPES }\n : { name, text: params.text, values, types: EXACT_TYPES };\n }\n\n function observeResult(params: { scope?: SavepointScope; effect?: StatementEffect; result: PgQueryResultsLike }): void {\n if (params.scope === undefined) return;\n const results = Array.isArray(params.result) ? params.result : [params.result];\n if (results.length === 0) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect }) });\n for (const result of results) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.rowCount, rows: result.rows?.length, resultRows: result.rows }) });\n }\n\n async function runExecute(params: {\n queryable: Queryable;\n text: string;\n values: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n }): Promise<Record<string, unknown>[]> {\n const config = buildQueryConfig({ text: params.text, values: params.values });\n const result = await runMapped(() => params.queryable.query(config));\n observeResult({ scope: params.scope, effect: params.effect, result });\n return finalResult({ result }).rows;\n }\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 values = params.values === undefined ? undefined : serializeParamsRaw({ values: params.values });\n const result = await runMapped(() => params.queryable.query({ text: params.text, values, types: EXACT_TYPES }));\n observeResult({ scope: params.scope, effect: params.effect, result });\n const final = finalResult({ result });\n return { rows: final.rows, affectedRows: final.rowCount ?? 0 };\n }\n\n /**\n * Adapter bound to one checked-out client that is already inside a\n * transaction. Nested `transaction()` calls open savepoints on that same\n * client, and their LIFETIMES are serialized by the savepoint gate (F14):\n * overlapping siblings share one savepoint stack, so the earlier one's\n * rollback would erase the later one's already released work.\n */\n function createClientAdapter(params: {\n dispatch: TransactionDispatch;\n scope: SavepointScope;\n budget: TransactionBudget;\n /** Pending control of this transaction; `null` when the client cannot batch (nothing is deferred). */\n deferred: DeferredControl | null;\n }): DatabaseAdapter {\n const { dispatch, scope, budget, deferred } = params;\n // ORM statements are always ONE statement; raw text is one only when it\n // binds values (node-postgres then uses the extended protocol itself).\n const client: Queryable = { query: (config) => dispatch({ config, batchable: true }) };\n const rawClient: Queryable = { query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 }) };\n // EPIC 6: SAVEPOINT rides with the body's first statement; a body that\n // sent nothing sends neither its RELEASE nor its ROLLBACK TO.\n const savepointControl = (statement: string, cleanup: boolean): Promise<unknown> | undefined => {\n if (deferred !== null && deferred.routeSavepoint({ text: statement }) !== \"send\") return undefined;\n return runMapped(() => dispatch({\n config: { text: statement },\n stage: \"nested\",\n batchable: true,\n ...(cleanup ? { cleanup: true } : {}),\n }));\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n roundTrips: ROUND_TRIPS,\n rowChanges: (): RowChangeCount => { budget.assertUsable({ stage: \"statement\" }); return readRowChanges({ scope }); },\n\n async execute(execParams) {\n const turn = scopeTurn({ scope });\n if (turn !== undefined) await turn;\n // After the turn, not before: waiting for a sibling savepoint spends\n // the same budget the statement is about to run against.\n budget.assertUsable({ stage: \"statement\" });\n // A server-reported failure aborts a postgres transaction: record it so\n // a callback that swallows it cannot commit nothing.\n return runExecute({ queryable: client, scope, ...execParams })\n .catch((error: unknown) => abortTransactionOnServerFailure({ scope, error }));\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: rawClient, 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) => savepointControl(statement, false),\n cleanup: (statement: string) => savepointControl(statement, true),\n // The child shares the ROOT budget by reference: a savepoint never\n // gets an independent clock or session setting.\n body: (child) => fn(createClientAdapter({ dispatch, scope: child, budget, deferred })),\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 PgArrayParam(values);\n },\n };\n }\n\n /**\n * Final cleanup, run ONCE by the lifecycle gate after every admitted\n * operation settled — so a raw BEGIN that was still pending at the\n * `disconnect()` call has published its session by now. Rolls back and\n * releases an abandoned raw session (destroyed when the rollback failed),\n * then ends the pool the adapter created. An injected pool is never ended.\n */\n function closeOwnedResources(): Promise<void> {\n return runShutdownCleanup({\n steps: [\n async (): Promise<void> => {\n if (rawSession === null) return;\n const session = rawSession;\n const unguard = rawSessionUnguard;\n rawSession = null;\n rawSessionUnguard = null;\n // An abandoned raw checkout is reusable only after a successful rollback.\n await runMapped(() => session.query({ text: \"ROLLBACK\" })).then(\n () => { unguard?.(); session.release(); },\n (error: unknown) => { unguard?.(); session.release(true); throw error; },\n );\n },\n async (): Promise<void> => {\n if (pool === null || !ownsPool) return;\n const closing = pool;\n await runMapped(() => closing.end());\n },\n ],\n });\n }\n\n const lifecycle = createAdapterLifecycleGate({ driver: \"pg\", cleanup: closeOwnedResources });\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 const client = await runMapped(() => getPool().connect());\n let backgroundFailure: unknown;\n const onError = (error: unknown): void => { backgroundFailure = error; };\n client.on?.(\"error\", onError);\n return runPinnedSession({\n run,\n execute: async (query) => {\n if (backgroundFailure !== undefined) throw mapPgDriverError({ error: backgroundFailure });\n return (await runExecuteUnsafe({ queryable: client, ...query })).rows;\n },\n isLost: (error) => backgroundFailure !== undefined || isConnectionLoss({ error }),\n release: async () => {\n client.removeListener?.(\"error\", onError);\n // Migration SQL may change arbitrary session state. Never pool it.\n client.release(true);\n },\n });\n },\n\n async execute(params) {\n // Round-trip EPIC 3 (A-12): own checkout instead of `pool.query`, so an\n // ordinary error can keep a session proven idle (see observeReadyForQuery).\n const config = buildQueryConfig({ text: params.text, values: params.values });\n const rootPool = getPool();\n if (!rootViaPoolQuery) {\n const client = await runMapped(() => rootPool.connect());\n if (hasReadyForQuerySeam({ client })) {\n const seam = observeReadyForQuery({ client });\n let reuse = false;\n try {\n const result = await runMapped(() => client.query(config));\n reuse = true;\n return finalResult({ result }).rows;\n } catch (error) {\n reuse = await seam.reusableAfter({ error, named: config.name !== undefined });\n throw error;\n } finally {\n seam.detach();\n client.release(!reuse);\n }\n }\n // A client without the seam: hand it back unused and keep today's path.\n client.release();\n rootViaPoolQuery = true;\n }\n return finalResult({ result: await runMapped(() => rootPool.query(config)) }).rows;\n },\n\n async executeUnsafe(params) {\n const control = classifyRawTransactionControl({ text: params.text });\n if (rawSession !== null) {\n if (control === \"close\") {\n const session = rawSession;\n const unguard = rawSessionUnguard;\n rawSession = null;\n rawSessionUnguard = null;\n try {\n return await runExecuteUnsafe({ queryable: session, ...params });\n } finally {\n unguard?.();\n session.release();\n }\n }\n // A nested raw BEGIN lands here too — postgres treats it as a warning\n // no-op on the open session, exactly like a single connection would.\n return runExecuteUnsafe({ queryable: rawSession, ...params });\n }\n if (control === \"open\") {\n const client = await runMapped(() => getPool().connect());\n const unguard = guardBackgroundErrors({ client });\n try {\n const result = await runExecuteUnsafe({ queryable: client, ...params });\n rawSession = client;\n rawSessionUnguard = unguard;\n return result;\n } catch (error) {\n unguard();\n client.release();\n throw error;\n }\n }\n return runExecuteUnsafe({ queryable: getPool(), ...params });\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n // Refuse an unsupportable budget BEFORE checking a connection out, so a\n // bad option never costs a checkout and never leaves a transaction open.\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n });\n // Round-trip EPIC 3 (A-02): BEGIN [… READ ONLY] and the timeout's SET as\n // ONE message. Built (and the timeout validated) before the checkout.\n const openingOptions = {\n isolationLevel: transactionOptions?.isolationLevel,\n readOnly: readOnlyRequested({ options: transactionOptions, provider: PROVIDER }),\n timeout: transactionOptions?.timeout,\n };\n const openText = openTransactionStatement(openingOptions);\n const client = await runMapped(() => getPool().connect());\n /**\n * The connection failure node-postgres reported on this client while the\n * transaction held it (EPIC T review, finding 1). node-postgres marks the\n * client unqueryable in the same handler that emits `error`, and from\n * then on refuses every query locally, without writing it: a COMMIT\n * after this point never leaves the client.\n */\n let connectionFailure: { readonly cause: unknown } | undefined;\n const unguard = guardBackgroundErrors({\n client,\n onError: (cause) => {\n if (connectionFailure === undefined) connectionFailure = { cause };\n },\n });\n const scope = createSavepointScope();\n // The deadline clock starts HERE: the connection is already in hand, so\n // acquisition (bounded separately by `connectionTimeout`) is not charged\n // against the caller's transaction budget.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n // Round-trip EPIC 6 (pipelining): on a client that can run a one-Sync\n // batch, the opening, every SAVEPOINT and the deadline's SET LOCAL wait\n // here and ride IN FRONT OF the next statement — one wait for all of\n // them. A transaction whose callback sends nothing sends nothing. The\n // opening is sent eagerly (EPIC 3's one message) only when a `timeout`\n // beyond PostgreSQL's int4 ceiling could make its SET fail: that error\n // keeps surfacing before the callback runs, as today.\n const capable = supportsBatch({ client });\n const deferOpening = capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);\n const opening: readonly string[] = deferOpening\n ? [beginStatement(openingOptions), ...(openingOptions.timeout === undefined ? [] : [statementTimeoutStatement({ timeout: openingOptions.timeout })])]\n : [];\n const deferred = createDeferredControl({ opening });\n /** The deferred opening failed (mapped): the server holds no transaction, so nothing more may be sent. */\n let openingFailure: VibeError | undefined;\n /** A burst carrying deferred control that has not settled yet (no deadline); later statements wait for it. */\n let inFlight: Promise<void> | null = null;\n\n const sendWithControl = (dispatchParams: Parameters<TransactionDispatch>[0]): Promise<PgQueryResultsLike> => {\n const { config, cleanup } = dispatchParams;\n const handOver = (): Promise<PgQueryResultsLike> => {\n dispatchParams.sent?.();\n return client.query(config);\n };\n if (cleanup !== true && openingFailure !== undefined) throw transactionAbortedError({ cause: openingFailure });\n let bound: string | undefined;\n if (cleanup !== true && budget.totalMs !== null) {\n if (scope.state.poisoned !== null) throw scope.state.poisoned;\n budget.assertUsable({ stage: dispatchParams.stage ?? \"statement\" });\n const ms = engineStatementBudgetMs({ budget });\n if (ms !== null) bound = statementTimeoutStatement({ timeout: ms });\n }\n if (!capable) {\n // Nothing is deferred on this client: today's awaited SET, re-check, statement.\n if (bound === undefined) return handOver();\n const stage = dispatchParams.stage ?? \"statement\";\n return runMapped(() => client.query({ text: bound })).then(() => {\n // SET LOCAL itself costs time. Refuse if its round trip exhausted\n // the deadline; no user query may enter pg's queue afterwards.\n budget.assertUsable({ stage });\n return handOver();\n });\n }\n // Nothing to carry: node-postgres sends the statement and serializes\n // its values itself (fix lane FX-A, RV5 MI-7: never twice).\n const pendingControl = cleanup !== true && deferred.hasPending();\n if (!pendingControl && bound === undefined) return handOver();\n // Serialize the statement's values BEFORE taking the pending control:\n // a value the driver refuses must leave the control pending, unsent.\n const statement: BatchStep[] = dispatchParams.batchable === true\n ? [{ text: config.text, values: prepareBatchValues({ values: config.values ?? [] }) }]\n : [];\n const carriesOpening = !deferred.opened();\n const control = pendingControl ? [...deferred.take()] : [];\n if (bound !== undefined) control.push(bound);\n const openingSteps = carriesOpening ? opening.length : 0;\n // A statement that cannot ride (raw text without values) goes alone,\n // AFTER its control was confirmed: never behind a BEGIN that may fail.\n if (statement.length > 0) dispatchParams.sent?.();\n const burst = runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES })\n .then((outcome): PgQueryResultsLike | Promise<PgQueryResultsLike> => {\n const failure = firstBatchFailure({ outcome });\n if (failure !== undefined) {\n // A failed control step rejects the statement it rode with — the\n // error today's awaited control step raised before the statement.\n if (failure.index < openingSteps || (outcome.transportError !== undefined && openingSteps > 0)) {\n // Mapped, so the refusals it causes carry its code and SQLSTATE.\n openingFailure = mapPgDriverError({ error: failure.error });\n }\n throw failure.error;\n }\n if (statement.length === 0) return handOver();\n const last = outcome.steps.at(-1);\n return last?.status === \"ok\" ? last.result : { rows: [], rowCount: 0 };\n });\n if (budget.totalMs === null && carriesOpening) {\n // Without a deadline, statements are not serialized here: a statement\n // dispatched while the opening is unconfirmed must wait for it, or a\n // failed BEGIN would let it run in autocommit.\n const settled: Promise<void> = burst.then(ignore, ignore).then(() => {\n if (inFlight === settled) inFlight = null;\n });\n inFlight = settled;\n }\n return burst;\n };\n const send = (dispatchParams: Parameters<TransactionDispatch>[0]): Promise<PgQueryResultsLike> => {\n try {\n return sendWithControl(dispatchParams);\n } catch (error) {\n return Promise.reject(error);\n }\n };\n // With a deadline, one turn for SET LOCAL + dispatch + completion, shared\n // by every nested handle. Never queue merely the timeout update: pg queues\n // queries itself, which would let concurrent statements spend the same\n // stale budget.\n let tail: Promise<unknown> = Promise.resolve();\n const dispatch: TransactionDispatch = (dispatchParams) => {\n if (budget.totalMs === null) {\n if (inFlight !== null) return inFlight.then(() => dispatch(dispatchParams));\n return send(dispatchParams);\n }\n const pending = tail.then(() => send(dispatchParams));\n // A failed statement must not strand ROLLBACK behind a rejected tail.\n tail = pending.then(ignore, ignore);\n return pending;\n };\n /** Set when the session's state can no longer be asserted (see below). */\n let discard = false;\n /** How far COMMIT got — the only input of the commit outcome (EPIC T). */\n let commitProgress: CommitProgress = \"not-sent\";\n try {\n if (!deferOpening) await runMapped(() => client.query({ text: openText }));\n const result = await runRootScope({\n scope,\n body: () => fn(createClientAdapter({ dispatch, scope, budget, deferred: capable ? deferred : null })),\n });\n // An expired budget must never commit, however the callback ended.\n budget.assertUsable({ stage: \"commit\" });\n if (!deferred.opened()) {\n // The callback sent nothing: no BEGIN left the client, so there is\n // nothing to commit (EPIC 6, empty transaction: 2 waits → 0).\n budget.close({ outcome: \"committed\" });\n return result;\n }\n // The deferred opening failed and the callback swallowed it: there is\n // no transaction to commit. Refuse here, so the ordinary cleanup below\n // runs — not the COMMIT branch, which would report a COMMIT that was\n // never sent as lost in flight (fix lane FX-A, RV5 MI-2).\n if (openingFailure !== undefined) throw transactionAbortedError({ cause: openingFailure });\n const commit = await runMapped(() => dispatch({\n config: { text: \"COMMIT\" },\n stage: \"commit\",\n batchable: true,\n sent: () => {\n // A client whose connection already failed refuses COMMIT locally:\n // raise that failure here, with COMMIT still unsent.\n if (connectionFailure !== undefined) throw mapPgDriverError({ error: connectionFailure.cause });\n commitProgress = \"sent\";\n },\n }))\n .catch((cause: unknown) => {\n // Refused before it reached node-postgres (the deadline re-check in\n // the dispatch turn, a poisoned scope, a connection that had\n // already failed): COMMIT was never sent, so the outcome is known\n // and the refusal is rethrown as it is.\n if (commitProgress === \"not-sent\") throw cause;\n // The server answered (a serialization failure at commit is a\n // definite rollback) — or the acknowledgement was LOST, in which\n // case a later successful ROLLBACK disproves nothing: the write may\n // have committed. Close unknown BEFORE cleanup, like mysql and bun.\n if (!isConnectionLoss({ error: cause })) {\n commitProgress = \"answered\";\n throw cause;\n }\n commitProgress = \"unanswered\";\n discard = true;\n budget.close({ outcome: \"unknown\" });\n throw commitOutcomeUnknownError({ cause });\n });\n // Belt and braces over the gate's tracking: an aborted transaction's\n // COMMIT is answered with a ROLLBACK tag and no error.\n if (finalResult({ result: commit }).command === \"ROLLBACK\") {\n commitProgress = \"answered\";\n throw transactionAbortedError({ cause: scope.state.aborted?.cause });\n }\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n if (isSavepointRollbackFailedError(error) || scope.state.poisoned !== null) {\n // A successful outer ROLLBACK cannot establish what happened when the\n // savepoint stack was lost. Keep the shared poison's unknown outcome.\n discard = true;\n budget.close({ outcome: \"unknown\" });\n }\n let rollback: RollbackOutcome = \"not-needed\";\n try {\n // A deferred opening that never left the client left nothing to undo.\n if (deferred.opened()) {\n await dispatch({ config: { text: \"ROLLBACK\" }, cleanup: true });\n rollback = \"acknowledged\";\n }\n } catch {\n // The rollback that should have cleaned up failed: this connection may\n // still be inside a transaction with `SET LOCAL` settings that were\n // never reverted, so it is DESTROYED rather than pooled. The commit\n // outcome does not change: it follows from how far COMMIT got, and\n // the server ends an open transaction when the connection closes.\n discard = true;\n rollback = \"failed\";\n }\n // EPIC T: the same error, with the facts recorded beside it.\n throw settleFailedTransaction({\n error,\n provider: PROVIDER,\n budget,\n scope,\n commit: commitProgress,\n rollback,\n connection: discard ? \"discarded\" : \"returned\",\n });\n } finally {\n unguard();\n client.release(discard);\n }\n },\n\n async connect(): Promise<void> {\n const client = await runMapped(() => getPool().connect());\n client.release();\n },\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n roundTrips: ROUND_TRIPS,\n transactionOutcome: \"reported\",\n\n withSession({ run }) {\n return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });\n },\n\n execute(params) {\n return lifecycle.admit({ 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((txAdapter) => lifecycle.inCallback({ run: () => fn(txAdapter) }), 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 * whole transaction and session callbacks included — then closes what the\n * adapter owns, once. Repeated calls share that completion. A new adapter\n * is the only way to connect again.\n */\n disconnect(): Promise<void> {\n return lifecycle.disconnect();\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgArrayParam(values);\n },\n };\n}\n\n// ─── Re-exports (driver-error table, markers, helpers) ────────────\n\nexport { PG_SQLSTATE_ERROR_CODES, PG_SQLSTATE_REASONS, mapPgDriverError, pgErrorSqlstate } from \"./errors.ts\";\nexport { PgArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nexport { createSavepointCounter, nextSavepointName } from \"@vibeorm/runtime/adapter-kit\";\nexport type { SavepointCounter } from \"@vibeorm/runtime/adapter-kit\";\nexport { createStatementNameCache } from \"./statement-cache.ts\";\nexport type { StatementNameCache } from \"./statement-cache.ts\";\nexport { buildPoolConfig, placePlanCacheMode } from \"./pool-config.ts\";\nexport type { PlanCacheMode, StartupOptionsPlacement } from \"./pool-config.ts\";\nexport {\n ISOLATION_LEVEL_SQL,\n beginStatement,\n classifyRawTransactionControl,\n refuseNestedTransactionOptions,\n statementTimeoutStatement,\n} from \"./transaction-sql.ts\";\nexport type { RawTransactionControl } 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-pg — node-postgres (`pg`) adapter for VibeORM v2.\n *\n * Runs on Bun and Node. Owns pooling, optional named prepared statements,\n * transactions (nested → savepoints on the same client) and driver-error\n * mapping. Only `VibeError` ever escapes.\n */\n\nimport type {\n AdapterBudgetSupport,\n DatabaseAdapter,\n QueryResult,\n RowChangeCount,\n StatementEffect,\n TransactionOptions,\n WireFidelity,\n} from \"@vibeorm/runtime\";\nimport {\n assertReadExecution,\n readCancelledError,\n markTransactionAborted,\n runReadExecution,\n type BudgetStage,\n abortTransactionOnServerFailure,\n createDeferredControl,\n type DeferredControl,\n observeRowChanges,\n readRowChanges,\n statementRowChanges,\n createAdapterLifecycleGate,\n runPinnedSession,\n runShutdownCleanup,\n transactionLifecycleRefusal,\n createSavepointScope,\n engineStatementBudgetMs,\n type CommitProgress,\n isSavepointRollbackFailedError,\n readOnlyRequested,\n type RollbackOutcome,\n runNestedSavepoint,\n runRootScope,\n type SavepointScope,\n scopeTurn,\n settleFailedTransaction,\n startTransactionBudget,\n type TransactionBudget,\n transactionAbortedError,\n validateTransactionDeadline,\n} from \"@vibeorm/runtime/adapter-kit\";\nimport { VibeError } from \"@vibeorm/schema\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport pg from \"pg\";\nimport { firstBatchFailure, prepareBatchValues, runBatch, supportsBatch, type BatchStep } from \"./batch.ts\";\nimport { createPgReadCancellation, type PgReadCancellation } from \"./read-cancellation.ts\";\nimport { mapPgDriverError, runMapped } from \"./errors.ts\";\nimport { PgArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nimport { assertPlanCacheMode, buildPoolConfig, type PlanCacheMode } from \"./pool-config.ts\";\nimport { createStatementNameCache, type StatementNameCache } from \"./statement-cache.ts\";\nimport {\n beginStatement,\n classifyRawTransactionControl,\n openTransactionStatement,\n refuseNestedTransactionOptions,\n statementTimeoutStatement,\n} from \"./transaction-sql.ts\";\n\n// ─── Minimal pg surface ───────────────────────────────────────────\n\n/** The single `pg` query form this adapter uses (config object, never overloads). */\nexport type PgQueryConfig = {\n readonly text: string;\n readonly values?: unknown[];\n readonly name?: string;\n /**\n * QUERY-LOCAL result parsers ({@link EXACT_TYPES}). node-postgres reads this\n * off the query config, so nothing outside VibeORM's own statements is\n * affected — a caller's pool keeps the driver's documented behaviour.\n */\n readonly types?: PgTypeParsers;\n};\n\n/** The `pg-types` surface a query config may carry. */\nexport type PgTypeParsers = {\n getTypeParser(oid: number, format?: \"text\" | \"binary\"): (value: string) => unknown;\n};\n\n/** The parts of a `pg` result this adapter reads. */\nexport type PgQueryResultLike = {\n readonly rows: Record<string, unknown>[];\n readonly rowCount: number | null;\n /**\n * The command tag (`INSERT`, `COMMIT`, `ROLLBACK`, …). Optional so test\n * doubles need not supply it; the adapter reads it after `COMMIT`, which\n * PostgreSQL answers with `ROLLBACK` — and no error — when the transaction\n * was aborted.\n */\n readonly command?: string;\n};\n\n/** pg returns one result per command for simple-protocol scripts. */\nexport type PgQueryResultsLike = PgQueryResultLike | PgQueryResultLike[];\n\n/** The parts of a `pg` PoolClient this adapter uses. */\nexport type PgPoolClientLike = {\n query(config: PgQueryConfig): Promise<PgQueryResultsLike>;\n /** Installed pg pipelined end waits for drain; strong cancellation refuses that lifetime. */\n readonly pipeline?: boolean;\n /** Local rejection timeouts cannot prove original statement settlement. */\n readonly connectionParameters?: { readonly query_timeout?: number };\n /**\n * Hand the client back. A TRUTHY argument tells node-postgres to DESTROY it\n * instead of pooling it — used when a rollback failed and the session state\n * can no longer be asserted (EPIC 6). Optional so existing test doubles that\n * declare `release(): void` still satisfy this type.\n */\n release(destroy?: boolean): void;\n /**\n * EventEmitter surface. A real `pg.PoolClient` has it; test doubles need not,\n * so both members are optional and the adapter feature-detects them.\n */\n on?(event: \"error\", listener: (error: unknown) => void): void;\n removeListener?(event: \"error\", listener: (error: unknown) => void): void;\n};\n\n/** The parts of a `pg` Pool this adapter uses — satisfied by a real `pg.Pool`. */\nexport type PgPoolLike = {\n connect(): Promise<PgPoolClientLike>;\n query(config: PgQueryConfig): Promise<PgQueryResultsLike>;\n end(): Promise<void>;\n /** EventEmitter surface; optional for the same reason as on {@link PgPoolClientLike}. */\n on?(event: \"error\", listener: (error: unknown, client: PgPoolClientLike) => void): void;\n};\n\n/** Anything that can run a query: the pool itself or a checked-out client. */\ntype Queryable = { query(config: PgQueryConfig): Promise<PgQueryResultsLike> };\n\ntype TransactionDispatch = (params: {\n config: PgQueryConfig;\n execution?: Parameters<DatabaseAdapter[\"execute\"]>[0][\"execution\"];\n onReadControl?: Parameters<DatabaseAdapter[\"execute\"]>[0][\"onReadControl\"];\n stage?: BudgetStage;\n cleanup?: boolean;\n /**\n * The text is ONE statement and may ride in a one-Sync batch behind pending\n * control (EPIC 6). False for raw text without bound values, which\n * node-postgres sends as a simple-protocol message that may hold several\n * statements.\n */\n batchable?: boolean;\n /**\n * Called immediately before the statement is handed to node-postgres —\n * after every check that can still refuse it. COMMIT uses it to record that\n * it was sent, the fact its outcome is decided by (EPIC T).\n */\n sent?: () => void;\n}) => Promise<PgQueryResultsLike>;\n\n/** PostgreSQL's ceiling for `statement_timeout` (an int4 of milliseconds). */\nconst MAX_STATEMENT_TIMEOUT_MS = 2_147_483_647;\n\nconst ignore = (): undefined => undefined;\n\nfunction finalResult(params: { result: PgQueryResultsLike }): PgQueryResultLike {\n return Array.isArray(params.result)\n ? (params.result.at(-1) ?? { rows: [], rowCount: 0 })\n : params.result;\n}\n\n/**\n * Keep a CHECKED-OUT client's background failures from killing the process,\n * returning the detach to run before the client goes back to the pool.\n *\n * node-postgres listens for `'error'` on a pooled client only while that client\n * sits IDLE, and removes the listener the moment the client is checked out\n * (restoring it on release). A client this adapter holds — for a transaction,\n * or as a pinned raw session — therefore has NO listener, so a backend that\n * dies mid-transaction reaches `client.emit('error', …)` with none. An\n * `'error'` event without a listener is an uncaught exception: fatal under\n * Node, which turns a database failover into a dead application process.\n *\n * The listener deliberately does nothing with the error and is NOT an error\n * channel: the in-flight query already rejects on its own with the mapped\n * `VibeError`, and the pool discards the broken client. Its only job is to keep\n * the process alive. Detaching before `release()` matters just as much — a\n * listener left attached would shadow pg's own idle listener and stop the pool\n * evicting a client that fails later, while idle.\n */\n/**\n * Prepared-statement name registries, one per POOL (F16/F18) — see\n * ./statement-cache.ts for why they are shared and bounded. Weakly held, so a\n * pool that goes out of scope takes its registry with it.\n */\nconst POOL_STATEMENT_NAMES: WeakMap<PgPoolLike, StatementNameCache> = new WeakMap();\n\n/**\n * Whether a mapped driver failure means the CONNECTION is gone rather than the\n * server having answered: no SQLSTATE at all (socket-level), class 08\n * (connection exception) or an admin/crash shutdown (57P01–57P03). The same\n * rule pins a migration session as lost and decides whether a failed COMMIT's\n * outcome is unknown.\n */\nfunction isConnectionLoss(params: { error: unknown }): boolean {\n const { error } = params;\n if (!(error instanceof VibeError)) return true;\n const sqlstate = error.meta.sqlstate;\n return typeof sqlstate !== \"string\" || sqlstate.startsWith(\"08\") || [\"57P01\", \"57P02\", \"57P03\"].includes(sqlstate);\n}\n\n/**\n * A COMMIT whose acknowledgement never arrived: the write may or may not be\n * durable, and nothing on the client can tell. The mysql and bun adapters\n * report the same shape (`transaction-outcome-unknown`, stage `commit`).\n */\nfunction commitOutcomeUnknownError(params: { cause: unknown }): VibeError {\n return new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n \"the connection was lost while COMMIT was in flight, so the transaction's outcome is unknown: the write may have committed. The connection was discarded; inspect the data before retrying.\",\n meta: { provider: PROVIDER, reason: \"transaction-outcome-unknown\", outcome: \"unknown\", stage: \"commit\" },\n cause: params.cause,\n });\n}\n\n/**\n * SQLSTATE classes after which a session is never reused, whatever its\n * transaction status says: connection exception, insufficient resources,\n * operator intervention, system error, internal error.\n */\nconst NEVER_REUSE_SQLSTATE_CLASSES: ReadonlySet<string> = new Set([\"08\", \"53\", \"57\", \"58\", \"XX\"]);\n\n/**\n * SQLSTATEs after which a NAMED prepared statement can no longer be trusted on\n * the session that ran it (fix lane FX-A, RV2 MJ-1): the client's record of\n * the name and the backend's disagree. node-postgres keeps a name it once\n * parsed (`parsedStatements`) across errors, so a reused session would fail\n * the same way on every later call:\n * - `0A000` — \"cached plan must not change result type\" after a schema change;\n * - `26000` — the backend does not know the name (a transaction-mode pooler);\n * - `42P05` — the backend already holds the name, so every Parse is refused.\n * Destroying the client makes the next call prepare on a fresh session — the\n * self-healing behaviour `pool.query` gave before round-trip EPIC 3.\n */\nconst STALE_STATEMENT_SQLSTATES: ReadonlySet<string> = new Set([\"0A000\", \"26000\", \"42P05\"]);\n\n/** The node-postgres `Client` members the reuse seam needs; feature-detected, never assumed. */\ntype ReadyForQuerySurface = {\n getTransactionStatus?: () => string | null;\n on?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n removeListener?: (event: string, listener: (...args: unknown[]) => void) => unknown;\n once?: unknown;\n pipeline?: unknown;\n};\n\n/** What the root path learns from one checked-out client while its query runs. */\ntype ReadyForQuerySeam = {\n /**\n * After a failure: may this client go back to the pool? Never true without\n * proof. `named`: the failing query was a named prepared statement.\n */\n readonly reusableAfter: (params: { error: unknown; named: boolean }) => Promise<boolean>;\n /** Remove every listener; runs before the client is released. */\n readonly detach: () => void;\n};\n\n/**\n * Round-trip EPIC 3 (A-12): prove that a root ORM statement's failure left its\n * session idle, so the client can be pooled instead of destroyed (today\n * `pool.query` destroys it on ANY error, and the next call pays a reconnect).\n *\n * Listeners for `drain` / `error` / `end` are attached BEFORE the query is\n * submitted. node-postgres sets `readyForQuery = false` when it sends a query\n * on an idle client and emits `drain` only from `_pulseQueryQueue` after\n * `_handleReadyForQuery` stored the status of THAT query's ReadyForQuery\n * (pg 8.23 `client.js` 373-396, 616-641), so the first `drain` after\n * submission carries the failing query's own status. Sampling the status at\n * rejection instead is timing-dependent (spike §3: stale in 59/200 compound\n * runs). A lost connection emits `error` / `end`, so the wait always ends.\n *\n * Reuse needs ALL of: the mapped error has a SQLSTATE outside\n * {@link NEVER_REUSE_SQLSTATE_CLASSES} (and, for a named statement, outside\n * {@link STALE_STATEMENT_SQLSTATES}); the first event was `drain`; the status\n * after it is `I`; no `error` event during the checkout. Anything else destroys\n * the client — today's behaviour. The `error` listener also keeps a background\n * failure of the checked-out client from becoming an uncaught exception (see\n * {@link guardBackgroundErrors}). Only for a client that passed\n * {@link hasReadyForQuerySeam}.\n */\nfunction observeReadyForQuery(params: { client: PgPoolClientLike }): ReadyForQuerySeam {\n const client = params.client as PgPoolClientLike & Required<Pick<ReadyForQuerySurface, \"on\" | \"removeListener\" | \"getTransactionStatus\">>;\n let backgroundFailure = false;\n let settle: (event: \"drain\" | \"error\" | \"end\") => void = () => undefined;\n const firstEvent = new Promise<\"drain\" | \"error\" | \"end\">((resolve) => { settle = resolve; });\n const onDrain = (): void => settle(\"drain\");\n const onError = (): void => { backgroundFailure = true; settle(\"error\"); };\n const onEnd = (): void => settle(\"end\");\n client.on(\"error\", onError);\n client.on(\"drain\", onDrain);\n client.on(\"end\", onEnd);\n return {\n reusableAfter: async ({ error, named }) => {\n if (!(error instanceof VibeError)) return false;\n const sqlstate = error.meta.sqlstate;\n if (typeof sqlstate !== \"string\" || NEVER_REUSE_SQLSTATE_CLASSES.has(sqlstate.slice(0, 2))) return false;\n if (named && STALE_STATEMENT_SQLSTATES.has(sqlstate)) return false;\n const event = await firstEvent;\n return event === \"drain\" && !backgroundFailure && client.getTransactionStatus() === \"I\";\n },\n detach: () => {\n client.removeListener(\"error\", onError);\n client.removeListener(\"drain\", onDrain);\n client.removeListener(\"end\", onEnd);\n },\n };\n}\n\n/**\n * Whether a checked-out client exposes node-postgres' events and status the\n * reuse seam needs (`on`, `removeListener`, `once`, `getTransactionStatus`) and\n * is not a `pipeline: true` client. A client without them (an injected pool's\n * wrapper) cannot even carry the `error` listener that keeps a socket failure\n * from becoming an uncaught exception, so the root path never runs a\n * statement on it (fix lane FX-A, RV2 MJ-3).\n */\nfunction hasReadyForQuerySeam(params: { client: PgPoolClientLike }): boolean {\n const client = params.client as PgPoolClientLike & ReadyForQuerySurface;\n return typeof client.on === \"function\" &&\n typeof client.removeListener === \"function\" &&\n typeof client.once === \"function\" &&\n typeof client.getTransactionStatus === \"function\" &&\n client.pipeline !== true;\n}\n\nfunction guardBackgroundErrors(params: { client: PgPoolClientLike; onError?: (error: unknown) => void }): () => void {\n const { client, onError } = params;\n if (typeof client.on !== \"function\") return (): void => undefined;\n const swallow = (error: unknown): void => {\n /* the failing call rejects with a mapped VibeError; nothing to report here */\n onError?.(error);\n };\n client.on(\"error\", swallow);\n return (): void => {\n client.removeListener?.(\"error\", swallow);\n };\n}\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/**\n * Every {@link pgAdapter} option. {@link PgAdapterOptions} adds the rule that\n * one of `connectionString` and `pool` is present.\n */\nexport type PgAdapterSettings = {\n /**\n * PostgreSQL connection URL. The adapter never reads `DATABASE_URL` (or any\n * other environment variable) itself: pass the URL you mean. Give a complete\n * URL — node-postgres fills parts the URL omits (host, user, database, …)\n * from the `PG*` environment variables.\n */\n readonly connectionString?: string;\n /**\n * An existing `pg.Pool` to use instead of creating one. The adapter never\n * ends a pool it did not create: `disconnect()` only ends this adapter (and\n * rolls back and releases its own raw session); the pool stays usable.\n * Wins over `connectionString` when both are given.\n */\n readonly pool?: PgPoolLike;\n /** Maximum pooled connections (default 10). Ignored when `pool` is given. */\n readonly max?: number;\n /**\n * Pool-wide `statement_timeout` in milliseconds. Every connection inherits\n * it; a per-transaction `timeout` overrides it for that transaction.\n */\n readonly statementTimeout?: number;\n /** Milliseconds to wait for a pool checkout before failing (pg's `connectionTimeoutMillis`). */\n readonly connectionTimeout?: number;\n /** Separate direct/session-pinned PostgreSQL control endpoint for running reads.\n * Required with an injected pool; never inferred from that pool or ambient env.\n * The same role must be allowed to signal its target. Server support needs live acceptance.\n */\n readonly cancellationConnectionString?: string;\n /**\n * Send ORM-path queries as named prepared statements (default false).\n *\n * Measured on this repo's benchmark (postgres 16, interleaved rounds):\n * −17…−25 % on reads, −6 % on a by-key update — the server skips the\n * parse/analyze and the protocol Parse/Describe round on every repeat. The\n * gain survives {@link PgAdapterOptions.planCacheMode} `force_custom_plan`,\n * which removes the classic downside (a generic plan after ~5 executions\n * picking worse indexes for range/LIKE/skewed filters).\n *\n * Off by default because named statements live on the BACKEND connection:\n * behind a transaction-mode pooler (PgBouncer, Supavisor, Neon's pooler)\n * without prepared-statement support a client that remembers a name from an\n * earlier transaction is handed another backend later — SQLSTATE 26000,\n * 42P05, or, worst, another client's statement under the same name executed\n * without any error. Turn it on for direct connections and session pooling.\n *\n * Schema changes can invalidate a retained result descriptor (SQLSTATE\n * 0A000). No statement or transaction callback is retried. Drain traffic and\n * recycle backend connections across incompatible schema deployments. For\n * an injected pool its owner must replace the pool; adapter.disconnect()\n * does not end it. force_custom_plan does not remove this requirement.\n */\n readonly preparedStatements?: boolean;\n /**\n * Budget of prepared-statement NAMES per pool (default 1000). Names never\n * evict — a name evicted here would stay prepared on every backend that saw\n * it — so this is a bound on the server's retention per connection, and a\n * text arriving after the budget is spent runs unnamed (it still binds its\n * parameters; only the server-side plan cache is skipped). A name is\n * allocated on a text's SECOND sighting, so one-off texts — offset pagination\n * emits one per page — never spend the budget.\n */\n readonly stmtCacheMax?: number;\n /**\n * PostgreSQL `plan_cache_mode` for every connection of the pool this adapter\n * builds (default `\"auto\"`, the server default — nothing is sent). Pair\n * `\"force_custom_plan\"` with {@link PgAdapterOptions.preparedStatements}: the\n * statement is still prepared once, but planned with the real parameter\n * values on every execution. Rides the `options` startup parameter, merged\n * with whatever the connection string already carries (a URL that sets\n * `plan_cache_mode` itself wins). Refused with `VIBE_CONFIG` together with\n * a caller-supplied `pool` — the adapter never reconfigures a pool it does\n * not own; set it on that pool's own `options` instead.\n */\n readonly planCacheMode?: PlanCacheMode;\n};\n\n/**\n * Options for {@link pgAdapter}: an explicit address is required. Either point\n * it at a connection string (the adapter then owns the pool it creates) or hand\n * it a pool you already own. Without either, the factory throws `VIBE_CONFIG`\n * before any pool or connection exists.\n */\nexport type PgAdapterOptions = PgAdapterSettings &\n ({ readonly connectionString: string } | { readonly pool: PgPoolLike });\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\nconst DIALECT: Dialect = \"postgres\";\nconst PROVIDER: Provider = \"postgresql\";\n\n/**\n * node-postgres wire (board #30, live-verified on both execute paths):\n * timestamptz → `Date`, json/jsonb → parsed values, int4/float8 → numbers.\n * (int8 stays a string — BigInt decoding is real work and never pruned.)\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"native\", json: \"parsed\", numbers: \"native\" } as const);\n\n/**\n * A PostgreSQL server: every round trip crosses a network (a local socket\n * included), so the runtime's one-statement folds apply. The transaction\n * facades carry the same declaration.\n */\nconst ROUND_TRIPS: NonNullable<DatabaseAdapter[\"roundTrips\"]> = \"network\";\n\n/**\n * Timing budgets this adapter genuinely enforces (EPIC 6).\n *\n * - `acquireTimeout: true` — {@link PgAdapterOptions.connectionTimeout} maps to\n * node-postgres' `connectionTimeoutMillis`, which bounds the checkout wait\n * and rejects when the pool stays exhausted. The capability is declared here;\n * it is OFF until the option is set (pg's own default is \"wait forever\").\n * - `statementTimeout: \"all-statements\"` — PostgreSQL's `statement_timeout`\n * cancels ANY over-long statement, writes included (SQLSTATE 57014).\n * - `transactionDeadline: \"cancel-running-statements\"` — the strongest level:\n * the remaining budget is handed to the SERVER before each statement, so the\n * database itself stops work that would outlive the deadline. Nothing here is\n * a `Promise.race` over a statement that keeps running.\n */\nconst BUDGETS: AdapterBudgetSupport = Object.freeze({\n acquireTimeout: true,\n statementTimeout: \"all-statements\",\n transactionDeadline: \"cancel-running-statements\",\n accessModes: Object.freeze([\"readOnly\"] as const),\n} as const);\n\n// ─── Exact numeric arrays (F12) ───────────────────────────────\n\n/** `_numeric`: the array form of `numeric`/`DECIMAL`. */\nconst NUMERIC_ARRAY_OID = 1231;\n/** `_text`: its parser splits a postgres array literal into RAW element strings. */\nconst TEXT_ARRAY_OID = 1009;\n/** `timestamp without time zone` and `date` — the zone-less types — and their array forms. */\nconst TIMESTAMP_OID = 1114;\nconst TIMESTAMP_ARRAY_OID = 1115;\nconst DATE_OID = 1082;\nconst DATE_ARRAY_OID = 1182;\n\n/**\n * `pg.types.getTypeParser` behind a plain numeric-OID signature. `@types/pg`\n * narrows the two-argument overload to its own `TypeId` union of well-known\n * OIDs, which does not include every array OID this function must look up.\n */\nconst driverTypeParser: (oid: number, format?: \"text\" | \"binary\") => (value: string) => unknown =\n pg.types.getTypeParser as unknown as (oid: number, format?: \"text\" | \"binary\") => (value: string) => unknown;\n\n/** postgres' text output for the zone-less types: `2026-03-08 19:38:08.538` or `2026-03-08`. */\nconst ZONE_LESS_TEXT = /^\\d{4}-\\d{2}-\\d{2}(?: \\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,6})?)?$/;\n\n/**\n * Zone-less date/timestamp text read as **UTC** — the same rule as the codec\n * table's `parseDateTimeText` (@vibeorm/sql), applied at the driver level\n * because node-postgres hands these columns to the ORM as `Date` objects\n * already (`wire.dateTime: \"native\"` prunes the codec decode). pg-types reads\n * them as host-LOCAL time, so one stored row decoded to a different instant\n * on every host TZ, and to a different instant than bun:sql, PGlite's ORM\n * path and Prisma (all UTC) return. `infinity`, BC dates and anything else\n * outside the plain form keep the driver's own parser.\n */\nfunction parseZoneLessAsUtc(value: string): unknown {\n if (ZONE_LESS_TEXT.test(value)) {\n const iso = value.includes(\" \") ? `${value.replace(\" \", \"T\").replace(/(\\.\\d{3})\\d+$/, \"$1\")}Z` : value;\n const parsed = new Date(iso);\n if (!Number.isNaN(parsed.getTime())) return parsed;\n }\n return driverTypeParser(TIMESTAMP_OID, \"text\")(value);\n}\n\n/** The array forms: the driver's `_text` parser splits the literal, elements go through {@link parseZoneLessAsUtc}. */\nfunction parseZoneLessArrayAsUtc(value: string): unknown {\n const mapElements = (element: unknown): unknown => {\n if (element === null) return null;\n if (Array.isArray(element)) return element.map(mapElements);\n return parseZoneLessAsUtc(String(element));\n };\n return mapElements(driverTypeParser(TEXT_ARRAY_OID, \"text\")(value));\n}\n\n/**\n * Query-local type parsers that keep `numeric[]` EXACT and read zone-less\n * timestamps as UTC.\n *\n * pg-types decodes `_numeric` with `parseFloat` per element, so\n * `9007199254740993` comes back as `9007199254740992` and `12.340` as the\n * number `12.34` — while the SCALAR `numeric` correctly stays a string and\n * VibeORM's Decimal contract is \"string in, string out\" on every dialect. The\n * array is the anomaly, and the loss happens inside the driver, before any\n * codec can see the value.\n *\n * The fix reuses the driver's OWN array-literal parser (`_text`), so array\n * syntax, quoting and NULL elements stay node-postgres' business; only the\n * per-element float conversion is dropped. It is attached PER QUERY (F12's\n * acceptance criterion), never by mutating `pg.types` globally: an injected\n * pool is shared property, and a global override would silently change every\n * other consumer in the process. The zone-less parsers ride the same hook for\n * the same reason.\n */\nconst EXACT_TYPES: PgTypeParsers = {\n getTypeParser(oid: number, format?: \"text\" | \"binary\"): (value: string) => unknown {\n if (format === undefined || format === \"text\") {\n if (oid === NUMERIC_ARRAY_OID) return driverTypeParser(TEXT_ARRAY_OID, \"text\");\n if (oid === TIMESTAMP_OID || oid === DATE_OID) return parseZoneLessAsUtc;\n if (oid === TIMESTAMP_ARRAY_OID || oid === DATE_ARRAY_OID) return parseZoneLessArrayAsUtc;\n }\n return driverTypeParser(oid, format);\n },\n};\n\n/**\n * The one address this adapter will ever use, taken at construction: an\n * injected pool (it wins, as documented) or a non-empty connection string.\n * Nothing is read from the environment, and nothing is re-read later.\n *\n * @throws VibeError `VIBE_CONFIG` (`meta.reason: \"missing-address\"`) when\n * neither is present. The message never echoes a value.\n */\nfunction takeAddress(params: { options: PgAdapterOptions | undefined }): { pool: PgPoolLike } | { connectionString: string } {\n const options: PgAdapterSettings | undefined = params.options;\n if (options?.pool !== undefined && options.pool !== null) return { pool: options.pool };\n const connectionString = options?.connectionString;\n if (typeof connectionString === \"string\" && connectionString.trim() !== \"\") return { connectionString };\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"pgAdapter needs an explicit address: pass `connectionString` (a PostgreSQL URL) or an existing `pool`. \" +\n \"The adapter does not read DATABASE_URL or the PG* environment variables.\",\n meta: { adapter: \"pg\", reason: \"missing-address\" },\n });\n}\n\n/**\n * Create a VibeORM adapter backed by node-postgres.\n *\n * Requires an explicit address (`connectionString` or `pool`); see\n * {@link PgAdapterOptions}. Construction opens nothing — the pool is created\n * on first use.\n *\n * @example\n * ```ts\n * const adapter = pgAdapter({ connectionString: \"postgresql://app:secret@db.internal:5432/app\", max: 20 });\n * await adapter.connect();\n * const rows = await adapter.execute({ text: 'SELECT * FROM \"User\" WHERE \"id\" = $1', values: [1] });\n * ```\n *\n * @example Reusing a pool you already own\n * ```ts\n * import pg from \"pg\";\n * const pool = new pg.Pool({ connectionString });\n * const adapter = pgAdapter({ pool });\n * ```\n */\nexport function pgAdapter(options: PgAdapterOptions): DatabaseAdapter {\n const address = takeAddress({ options });\n const cancellationAddress = options.cancellationConnectionString ?? (\"connectionString\" in address ? address.connectionString : undefined);\n const readCancellation: NonNullable<DatabaseAdapter[\"readCancellation\"]> = cancellationAddress === undefined ? \"before-dispatch\" : \"running\";\n const usePrepared = options?.preparedStatements ?? false;\n const stmtCacheMax = options?.stmtCacheMax ?? 1000;\n const planCacheMode = options?.planCacheMode === undefined ? \"auto\" : assertPlanCacheMode({ value: options.planCacheMode });\n if (planCacheMode !== \"auto\" && \"pool\" in address) {\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message:\n \"planCacheMode configures the pool the adapter builds — it cannot be applied to a caller-supplied `pool`; \" +\n \"set `options: \\\"-c plan_cache_mode=…\\\"` on that pool instead\",\n meta: { planCacheMode },\n });\n }\n\n let pool: PgPoolLike | null = null;\n let ownsPool = false;\n\n /**\n * The checked-out client of an OPEN raw transaction — session affinity for\n * the raw path. @vibeorm/migrate wraps transactional DDL in literal `BEGIN`\n * / `COMMIT` / `ROLLBACK` sent through `executeUnsafe`; without pinning,\n * `pool.query(\"BEGIN\")` sequences stay on one client only by LIFO checkout\n * luck, and any concurrent traffic silently splits the transaction across\n * connections. A pure `BEGIN` checks a client out, every raw statement until\n * the matching `COMMIT`/`ROLLBACK` rides it, and the close releases it. The\n * ORM path (`execute`) and `transaction()` stay on the pool. Concurrent raw\n * transactions on one adapter are unsupported.\n */\n let rawSession: PgPoolClientLike | null = null;\n /** Detach for {@link rawSession}'s background-error guard; runs before its release. */\n let rawSessionUnguard: (() => void) | null = null;\n /**\n * Set once a checked-out client lacked {@link hasReadyForQuerySeam}: the\n * pool hands out wrappers, so root ORM statements keep running through the\n * pool's own `query()` — the path (and the guard) they had before round-trip\n * EPIC 3 (fix lane FX-A, RV2 MJ-3 / MN-6c).\n */\n let rootViaPoolQuery = false;\n\n function getPool(): PgPoolLike {\n if (pool !== null) return pool;\n\n if (\"pool\" in address) {\n pool = address.pool;\n ownsPool = false;\n return pool;\n }\n\n const config = buildPoolConfig({\n connectionString: address.connectionString,\n max: options?.max ?? 10,\n ...(options?.statementTimeout === undefined ? {} : { statementTimeout: options.statementTimeout }),\n ...(options?.connectionTimeout === undefined ? {} : { connectionTimeout: options.connectionTimeout }),\n planCacheMode,\n });\n\n const PoolCtor = pg.Pool as unknown as new (poolConfig: Record<string, unknown>) => PgPoolLike;\n pool = new PoolCtor(config);\n /**\n * The IDLE half of the same hazard {@link guardBackgroundErrors} covers: a\n * pooled client that loses its socket while idle makes pg re-emit on the\n * POOL, and an unhandled `'error'` there is equally fatal. The pool already\n * evicts that client, so this listener only keeps the process alive.\n *\n * Attached ONLY to a pool this adapter constructed. A caller-supplied pool\n * (the `pool` option) has its own owner: the adapter never ends it either,\n * node-postgres documents attaching an `'error'` listener as that owner's\n * job, and registering one here that is never removed would stack up a\n * listener per adapter sharing that pool. The dangerous case — a client\n * checked out for a transaction or a raw session, which pg leaves\n * unlistened by construction — is guarded in BOTH modes.\n */\n pool.on?.(\"error\", () => {\n /* the pool has already discarded the failed client */\n });\n ownsPool = true;\n return pool;\n }\n\n /**\n * Prepared-statement names for the pool this adapter runs on (F18): the\n * registry is keyed by the POOL, so two adapters sharing one pool share one\n * name space instead of both starting at `vibeorm_0` and colliding on a\n * connection. It also bounds how many names that pool can ever put on a\n * backend (F16) no matter how many adapters come and go over it.\n */\n function statementNamesFor(params: { pool: PgPoolLike }): StatementNameCache {\n const existing = POOL_STATEMENT_NAMES.get(params.pool);\n if (existing !== undefined) return existing;\n const created = createStatementNameCache({ max: stmtCacheMax });\n POOL_STATEMENT_NAMES.set(params.pool, created);\n return created;\n }\n\n /** Build the config for one ORM-path query (statement name only when enabled). */\n function buildQueryConfig(params: { text: string; values: unknown[] }): PgQueryConfig {\n const values = serializeParams({ values: params.values });\n if (!usePrepared) return { text: params.text, values, types: EXACT_TYPES };\n // `undefined` on a text's first sighting and once the pool's name budget is\n // spent: the statement runs unnamed rather than growing the server's\n // prepared-statement retention.\n const name = statementNamesFor({ pool: getPool() }).getName({ text: params.text });\n return name === undefined\n ? { text: params.text, values, types: EXACT_TYPES }\n : { name, text: params.text, values, types: EXACT_TYPES };\n }\n\n function observeResult(params: { scope?: SavepointScope; effect?: StatementEffect; result: PgQueryResultsLike }): void {\n if (params.scope === undefined) return;\n const results = Array.isArray(params.result) ? params.result : [params.result];\n if (results.length === 0) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect }) });\n for (const result of results) observeRowChanges({ scope: params.scope, count: statementRowChanges({ effect: params.effect, count: result.rowCount, rows: result.rows?.length, resultRows: result.rows }) });\n }\n\n async function runExecute(params: {\n queryable: Queryable;\n text: string;\n values: unknown[];\n scope?: SavepointScope;\n effect?: StatementEffect;\n }): Promise<Record<string, unknown>[]> {\n const config = buildQueryConfig({ text: params.text, values: params.values });\n const result = await runMapped(() => params.queryable.query(config));\n observeResult({ scope: params.scope, effect: params.effect, result });\n return finalResult({ result }).rows;\n }\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 values = params.values === undefined ? undefined : serializeParamsRaw({ values: params.values });\n const result = await runMapped(() => params.queryable.query({ text: params.text, values, types: EXACT_TYPES }));\n observeResult({ scope: params.scope, effect: params.effect, result });\n const final = finalResult({ result });\n return { rows: final.rows, affectedRows: final.rowCount ?? 0 };\n }\n\n /**\n * Adapter bound to one checked-out client that is already inside a\n * transaction. Nested `transaction()` calls open savepoints on that same\n * client, and their LIFETIMES are serialized by the savepoint gate (F14):\n * overlapping siblings share one savepoint stack, so the earlier one's\n * rollback would erase the later one's already released work.\n */\n function createClientAdapter(params: {\n dispatch: TransactionDispatch;\n scope: SavepointScope;\n budget: TransactionBudget;\n /** Pending control of this transaction; `null` when the client cannot batch (nothing is deferred). */\n deferred: DeferredControl | null;\n cancellation: PgReadCancellation;\n }): DatabaseAdapter {\n const { dispatch, scope, budget, deferred, cancellation } = params;\n // ORM statements are always ONE statement; raw text is one only when it\n // binds values (node-postgres then uses the extended protocol itself).\n const rawClient: Queryable = { query: (config) => dispatch({ config, batchable: (config.values?.length ?? 0) > 0 }) };\n // EPIC 6: SAVEPOINT rides with the body's first statement; a body that\n // sent nothing sends neither its RELEASE nor its ROLLBACK TO.\n const savepointControl = (statement: string, cleanup: boolean): Promise<unknown> | undefined => {\n if (deferred !== null && deferred.routeSavepoint({ text: statement }) !== \"send\") return undefined;\n return runMapped(() => dispatch({\n config: { text: statement },\n stage: \"nested\",\n batchable: true,\n ...(cleanup ? { cleanup: true } : {}),\n }));\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation: cancellation.support,\n roundTrips: ROUND_TRIPS,\n rowChanges: (): RowChangeCount => { budget.assertUsable({ stage: \"statement\" }); return readRowChanges({ scope }); },\n\n async execute(execParams) {\n return runReadExecution({ ...execParams, support: cancellation.support, 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 assertReadExecution({ ...execParams, support: cancellation.support, provider: PROVIDER, stage: \"waiting-for-connection\" });\n // A server-reported failure aborts a postgres transaction: record it so\n // a callback that swallows it cannot commit nothing.\n const queryable: Queryable = { query: (config) => dispatch({ config, batchable: execParams.execution?.cancellation !== \"running\", execution: execParams.execution, onReadControl: execParams.onReadControl }) };\n return runExecute({ queryable, 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: rawClient, 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) => savepointControl(statement, false),\n cleanup: (statement: string) => savepointControl(statement, true),\n // The child shares the ROOT budget by reference: a savepoint never\n // gets an independent clock or session setting.\n body: (child) => fn(createClientAdapter({ dispatch, scope: child, budget, deferred, cancellation })),\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 PgArrayParam(values);\n },\n };\n }\n\n /**\n * Final cleanup, run ONCE by the lifecycle gate after every admitted\n * operation settled — so a raw BEGIN that was still pending at the\n * `disconnect()` call has published its session by now. Rolls back and\n * releases an abandoned raw session (destroyed when the rollback failed),\n * then ends the pool the adapter created. An injected pool is never ended.\n */\n function closeOwnedResources(): Promise<void> {\n return runShutdownCleanup({\n steps: [\n async (): Promise<void> => {\n if (rawSession === null) return;\n const session = rawSession;\n const unguard = rawSessionUnguard;\n rawSession = null;\n rawSessionUnguard = null;\n // An abandoned raw checkout is reusable only after a successful rollback.\n await runMapped(() => session.query({ text: \"ROLLBACK\" })).then(\n () => { unguard?.(); session.release(); },\n (error: unknown) => { unguard?.(); session.release(true); throw error; },\n );\n },\n async (): Promise<void> => {\n if (pool === null || !ownsPool) return;\n const closing = pool;\n await runMapped(() => closing.end());\n },\n ],\n });\n }\n\n const lifecycle = createAdapterLifecycleGate({ driver: \"pg\", cleanup: closeOwnedResources });\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 const client = await runMapped(() => getPool().connect());\n let backgroundFailure: unknown;\n const onError = (error: unknown): void => { backgroundFailure = error; };\n client.on?.(\"error\", onError);\n return runPinnedSession({\n run,\n execute: async (query) => {\n if (backgroundFailure !== undefined) throw mapPgDriverError({ error: backgroundFailure });\n return (await runExecuteUnsafe({ queryable: client, ...query })).rows;\n },\n isLost: (error) => backgroundFailure !== undefined || isConnectionLoss({ error }),\n release: async () => {\n client.removeListener?.(\"error\", onError);\n // Migration SQL may change arbitrary session state. Never pool it.\n client.release(true);\n },\n });\n },\n\n async execute(params) {\n assertReadExecution({ ...params, support: readCancellation, provider: PROVIDER });\n if (params.execution?.cancellation === \"running\") {\n // READ ONLY pins a backend even behind a transaction-mode pooler.\n // Transaction lifecycle stays outside the individual read receipt.\n const options = { accessMode: \"readOnly\" as const };\n return root.transaction((tx) => tx.execute(params), options);\n }\n // Round-trip EPIC 3 (A-12): own checkout instead of `pool.query`, so an\n // ordinary error can keep a session proven idle (see observeReadyForQuery).\n const config = buildQueryConfig({ text: params.text, values: params.values });\n const rootPool = getPool();\n if (!rootViaPoolQuery) {\n const client = await runMapped(() => rootPool.connect());\n if (hasReadyForQuerySeam({ client }) || params.execution !== undefined) {\n const seam = hasReadyForQuerySeam({ client }) ? observeReadyForQuery({ client }) : undefined;\n let reuse = false;\n try {\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n const result = await runMapped(() => client.query(config));\n reuse = true;\n return finalResult({ result }).rows;\n } catch (error) {\n reuse = await seam?.reusableAfter({ error, named: config.name !== undefined }) ?? false;\n throw error;\n } finally {\n seam?.detach();\n client.release(!reuse);\n }\n }\n // A client without the seam: hand it back unused and keep today's path.\n client.release();\n rootViaPoolQuery = true;\n }\n assertReadExecution({ ...params, provider: PROVIDER, stage: \"waiting-for-connection\" });\n return finalResult({ result: await runMapped(() => rootPool.query(config)) }).rows;\n },\n\n async executeUnsafe(params) {\n const control = classifyRawTransactionControl({ text: params.text });\n if (rawSession !== null) {\n if (control === \"close\") {\n const session = rawSession;\n const unguard = rawSessionUnguard;\n rawSession = null;\n rawSessionUnguard = null;\n try {\n return await runExecuteUnsafe({ queryable: session, ...params });\n } finally {\n unguard?.();\n session.release();\n }\n }\n // A nested raw BEGIN lands here too — postgres treats it as a warning\n // no-op on the open session, exactly like a single connection would.\n return runExecuteUnsafe({ queryable: rawSession, ...params });\n }\n if (control === \"open\") {\n const client = await runMapped(() => getPool().connect());\n const unguard = guardBackgroundErrors({ client });\n try {\n const result = await runExecuteUnsafe({ queryable: client, ...params });\n rawSession = client;\n rawSessionUnguard = unguard;\n return result;\n } catch (error) {\n unguard();\n client.release();\n throw error;\n }\n }\n return runExecuteUnsafe({ queryable: getPool(), ...params });\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n // Refuse an unsupportable budget BEFORE checking a connection out, so a\n // bad option never costs a checkout and never leaves a transaction open.\n validateTransactionDeadline({\n options: transactionOptions,\n support: BUDGETS.transactionDeadline,\n provider: PROVIDER,\n });\n // Round-trip EPIC 3 (A-02): BEGIN [… READ ONLY] and the timeout's SET as\n // ONE message. Built (and the timeout validated) before the checkout.\n const openingOptions = {\n isolationLevel: transactionOptions?.isolationLevel,\n readOnly: readOnlyRequested({ options: transactionOptions, provider: PROVIDER }),\n timeout: transactionOptions?.timeout,\n };\n const openText = openTransactionStatement(openingOptions);\n const client = await runMapped(() => getPool().connect());\n /**\n * The connection failure node-postgres reported on this client while the\n * transaction held it (EPIC T review, finding 1). node-postgres marks the\n * client unqueryable in the same handler that emits `error`, and from\n * then on refuses every query locally, without writing it: a COMMIT\n * after this point never leaves the client.\n */\n let connectionFailure: { readonly cause: unknown } | undefined;\n const unguard = guardBackgroundErrors({\n client,\n onError: (cause) => {\n if (connectionFailure === undefined) connectionFailure = { cause };\n },\n });\n const scope = createSavepointScope();\n // The deadline clock starts HERE: the connection is already in hand, so\n // acquisition (bounded separately by `connectionTimeout`) is not charged\n // against the caller's transaction budget.\n const budget = startTransactionBudget({ options: transactionOptions, provider: PROVIDER });\n // Round-trip EPIC 6 (pipelining): on a client that can run a one-Sync\n // batch, the opening, every SAVEPOINT and the deadline's SET LOCAL wait\n // here and ride IN FRONT OF the next statement — one wait for all of\n // them. A transaction whose callback sends nothing sends nothing. The\n // opening is sent eagerly (EPIC 3's one message) only when a `timeout`\n // beyond PostgreSQL's int4 ceiling could make its SET fail: that error\n // keeps surfacing before the callback runs, as today.\n const capable = supportsBatch({ client });\n const deferOpening = capable && (openingOptions.timeout === undefined || openingOptions.timeout <= MAX_STATEMENT_TIMEOUT_MS);\n const opening: readonly string[] = deferOpening\n ? [beginStatement(openingOptions), ...(openingOptions.timeout === undefined ? [] : [statementTimeoutStatement({ timeout: openingOptions.timeout })])]\n : [];\n const deferred = createDeferredControl({ opening });\n /** The deferred opening failed (mapped): the server holds no transaction, so nothing more may be sent. */\n let openingFailure: VibeError | undefined;\n /** A burst carrying deferred control that has not settled yet (no deadline); later statements wait for it. */\n let inFlight: Promise<void> | null = null;\n\n const sendWithControl = (dispatchParams: Parameters<TransactionDispatch>[0]): Promise<PgQueryResultsLike> => {\n const { config, cleanup } = dispatchParams;\n const handOver = (): Promise<PgQueryResultsLike> => {\n assertReadExecution({ execution: dispatchParams.execution, effect: \"read\", support: readCancellation, provider: PROVIDER, stage: \"waiting-for-connection\" });\n dispatchParams.sent?.();\n return cancellation.run({ config, execution: dispatchParams.execution, onReadControl: dispatchParams.onReadControl, send: () => client.query(config) });\n };\n if (cleanup !== true && cancellationAttempted) throw transactionAbortedError({ cause: readCancelledError({ stage: \"running\" }) });\n if (cleanup !== true && openingFailure !== undefined) throw transactionAbortedError({ cause: openingFailure });\n let bound: string | undefined;\n if (cleanup !== true && budget.totalMs !== null) {\n if (scope.state.poisoned !== null) throw scope.state.poisoned;\n budget.assertUsable({ stage: dispatchParams.stage ?? \"statement\" });\n const ms = engineStatementBudgetMs({ budget });\n if (ms !== null) bound = statementTimeoutStatement({ timeout: ms });\n }\n if (!capable) {\n // Nothing is deferred on this client: today's awaited SET, re-check, statement.\n if (bound === undefined) return handOver();\n const stage = dispatchParams.stage ?? \"statement\";\n return runMapped(() => client.query({ text: bound })).then(() => {\n // SET LOCAL itself costs time. Refuse if its round trip exhausted\n // the deadline; no user query may enter pg's queue afterwards.\n budget.assertUsable({ stage });\n return handOver();\n });\n }\n // Nothing to carry: node-postgres sends the statement and serializes\n // its values itself (fix lane FX-A, RV5 MI-7: never twice).\n const pendingControl = cleanup !== true && deferred.hasPending();\n if (!pendingControl && bound === undefined) return handOver();\n // Serialize the statement's values BEFORE taking the pending control:\n // a value the driver refuses must leave the control pending, unsent.\n const statement: BatchStep[] = dispatchParams.batchable === true\n ? [{ text: config.text, values: prepareBatchValues({ values: config.values ?? [] }) }]\n : [];\n const carriesOpening = !deferred.opened();\n const control = pendingControl ? [...deferred.take()] : [];\n if (bound !== undefined) control.push(bound);\n const openingSteps = carriesOpening ? opening.length : 0;\n // A statement that cannot ride (raw text without values) goes alone,\n // AFTER its control was confirmed: never behind a BEGIN that may fail.\n assertReadExecution({ execution: dispatchParams.execution, effect: \"read\", support: readCancellation, provider: PROVIDER, stage: \"waiting-for-connection\" });\n if (statement.length > 0) dispatchParams.sent?.();\n const burst = runBatch({ client, steps: [...control.map((text) => ({ text })), ...statement], types: EXACT_TYPES })\n .then((outcome): PgQueryResultsLike | Promise<PgQueryResultsLike> => {\n const failure = firstBatchFailure({ outcome });\n if (failure !== undefined) {\n // A failed control step rejects the statement it rode with — the\n // error today's awaited control step raised before the statement.\n if (failure.index < openingSteps || (outcome.transportError !== undefined && openingSteps > 0)) {\n // Mapped, so the refusals it causes carry its code and SQLSTATE.\n openingFailure = mapPgDriverError({ error: failure.error });\n }\n throw failure.error;\n }\n if (statement.length === 0) return handOver();\n const last = outcome.steps.at(-1);\n return last?.status === \"ok\" ? last.result : { rows: [], rowCount: 0 };\n });\n if (budget.totalMs === null && carriesOpening) {\n // Without a deadline, statements are not serialized here: a statement\n // dispatched while the opening is unconfirmed must wait for it, or a\n // failed BEGIN would let it run in autocommit.\n const settled: Promise<void> = burst.then(ignore, ignore).then(() => {\n if (inFlight === settled) inFlight = null;\n });\n inFlight = settled;\n }\n return burst;\n };\n const send = (dispatchParams: Parameters<TransactionDispatch>[0]): Promise<PgQueryResultsLike> => {\n try {\n return sendWithControl(dispatchParams);\n } catch (error) {\n return Promise.reject(error);\n }\n };\n // With a deadline, one turn for SET LOCAL + dispatch + completion, shared\n // by every nested handle. Never queue merely the timeout update: pg queues\n // queries itself, which would let concurrent statements spend the same\n // stale budget.\n let tail: Promise<unknown> = Promise.resolve();\n const activeDispatches = new Set<Promise<PgQueryResultsLike>>();\n let cancellationQueue = false;\n const dispatch: TransactionDispatch = (dispatchParams) => {\n if (dispatchParams.execution?.cancellation === \"running\" && !cancellationQueue) {\n // Drain earlier ordinary statements before naming a target. All later\n // work (including siblings/cleanup) waits for BOTH query and control.\n cancellationQueue = true;\n tail = Promise.allSettled([...activeDispatches]);\n }\n if (budget.totalMs === null && !cancellationQueue) {\n // Register this arrival before waiting for the deferred opening. A\n // later strong read must drain it, not recursively move it behind\n // the cancellation queue when that opening finally settles.\n const pending = inFlight === null\n ? send(dispatchParams)\n : inFlight.then(() => send(dispatchParams));\n activeDispatches.add(pending);\n void pending.then(() => activeDispatches.delete(pending), () => activeDispatches.delete(pending));\n return pending;\n }\n const pending = tail.then(() => send(dispatchParams));\n // A failed statement must not strand ROLLBACK behind a rejected tail.\n tail = pending.then(ignore, ignore);\n return pending;\n };\n /** Set when the session's state can no longer be asserted (see below). */\n let discard = false;\n let released = false;\n let cancellationAttempted = false;\n const cancellation = createPgReadCancellation({ connectionString: cancellationAddress, target: client, terminate: () => {\n if (!released) { released = true; client.release(true); }\n }, discard: () => {\n discard = true;\n cancellationAttempted = true;\n // Even a successful read can race an asynchronous backend signal. No\n // later user statement/commit may enter this target transaction.\n markTransactionAborted({ scope, cause: readCancelledError({ stage: \"running\" }) });\n } });\n /** How far COMMIT got — the only input of the commit outcome (EPIC T). */\n let commitProgress: CommitProgress = \"not-sent\";\n try {\n if (!deferOpening) await runMapped(() => client.query({ text: openText }));\n const result = await runRootScope({\n scope,\n body: () => fn(createClientAdapter({ dispatch, scope, budget, deferred: capable ? deferred : null, cancellation })),\n });\n // An expired budget must never commit, however the callback ended.\n budget.assertUsable({ stage: \"commit\" });\n if (!deferred.opened()) {\n // The callback sent nothing: no BEGIN left the client, so there is\n // nothing to commit (EPIC 6, empty transaction: 2 waits → 0).\n budget.close({ outcome: \"committed\" });\n return result;\n }\n // The deferred opening failed and the callback swallowed it: there is\n // no transaction to commit. Refuse here, so the ordinary cleanup below\n // runs — not the COMMIT branch, which would report a COMMIT that was\n // never sent as lost in flight (fix lane FX-A, RV5 MI-2).\n if (openingFailure !== undefined) throw transactionAbortedError({ cause: openingFailure });\n const commit = await runMapped(() => dispatch({\n config: { text: \"COMMIT\" },\n stage: \"commit\",\n batchable: true,\n sent: () => {\n // A client whose connection already failed refuses COMMIT locally:\n // raise that failure here, with COMMIT still unsent.\n if (connectionFailure !== undefined) throw mapPgDriverError({ error: connectionFailure.cause });\n commitProgress = \"sent\";\n },\n }))\n .catch((cause: unknown) => {\n // Refused before it reached node-postgres (the deadline re-check in\n // the dispatch turn, a poisoned scope, a connection that had\n // already failed): COMMIT was never sent, so the outcome is known\n // and the refusal is rethrown as it is.\n if (commitProgress === \"not-sent\") throw cause;\n // The server answered (a serialization failure at commit is a\n // definite rollback) — or the acknowledgement was LOST, in which\n // case a later successful ROLLBACK disproves nothing: the write may\n // have committed. Close unknown BEFORE cleanup, like mysql and bun.\n if (!isConnectionLoss({ error: cause })) {\n commitProgress = \"answered\";\n throw cause;\n }\n commitProgress = \"unanswered\";\n discard = true;\n budget.close({ outcome: \"unknown\" });\n throw commitOutcomeUnknownError({ cause });\n });\n // Belt and braces over the gate's tracking: an aborted transaction's\n // COMMIT is answered with a ROLLBACK tag and no error.\n if (finalResult({ result: commit }).command === \"ROLLBACK\") {\n commitProgress = \"answered\";\n throw transactionAbortedError({ cause: scope.state.aborted?.cause });\n }\n budget.close({ outcome: \"committed\" });\n return result;\n } catch (error) {\n if (isSavepointRollbackFailedError(error) || scope.state.poisoned !== null) {\n // A successful outer ROLLBACK cannot establish what happened when the\n // savepoint stack was lost. Keep the shared poison's unknown outcome.\n discard = true;\n budget.close({ outcome: \"unknown\" });\n }\n let rollback: RollbackOutcome = \"not-needed\";\n try {\n // A deferred opening that never left the client left nothing to undo.\n if (deferred.opened()) {\n await dispatch({ config: { text: \"ROLLBACK\" }, cleanup: true });\n rollback = \"acknowledged\";\n }\n } catch {\n // The rollback that should have cleaned up failed: this connection may\n // still be inside a transaction with `SET LOCAL` settings that were\n // never reverted, so it is DESTROYED rather than pooled. The commit\n // outcome does not change: it follows from how far COMMIT got, and\n // the server ends an open transaction when the connection closes.\n discard = true;\n rollback = \"failed\";\n }\n // EPIC T: the same error, with the facts recorded beside it.\n throw settleFailedTransaction({\n error,\n provider: PROVIDER,\n budget,\n scope,\n commit: commitProgress,\n rollback,\n connection: discard ? \"discarded\" : \"returned\",\n });\n } finally {\n unguard();\n if (!released) client.release(discard);\n }\n },\n\n async connect(): Promise<void> {\n const client = await runMapped(() => getPool().connect());\n client.release();\n },\n };\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n budgets: BUDGETS,\n readCancellation,\n roundTrips: ROUND_TRIPS,\n transactionOutcome: \"reported\",\n\n withSession({ run }) {\n return lifecycle.admit({ run: () => root.withSession({ run: (executor) => lifecycle.inCallback({ run: () => run(executor) }) }) });\n },\n\n execute(params) {\n return lifecycle.admit({ run: () => params.execution === undefined ? root.execute(params) : runReadExecution({ ...params, support: readCancellation, 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((txAdapter) => lifecycle.inCallback({ run: () => fn(txAdapter) }), 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 * whole transaction and session callbacks included — then closes what the\n * adapter owns, once. Repeated calls share that completion. A new adapter\n * is the only way to connect again.\n */\n disconnect(): Promise<void> {\n return lifecycle.disconnect();\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgArrayParam(values);\n },\n };\n}\n\n// ─── Re-exports (driver-error table, markers, helpers) ────────────\n\nexport { PG_SQLSTATE_ERROR_CODES, PG_SQLSTATE_REASONS, mapPgDriverError, pgErrorSqlstate } from \"./errors.ts\";\nexport { PgArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nexport { createSavepointCounter, nextSavepointName } from \"@vibeorm/runtime/adapter-kit\";\nexport type { SavepointCounter } from \"@vibeorm/runtime/adapter-kit\";\nexport { createStatementNameCache } from \"./statement-cache.ts\";\nexport type { StatementNameCache } from \"./statement-cache.ts\";\nexport { buildPoolConfig, placePlanCacheMode } from \"./pool-config.ts\";\nexport type { PlanCacheMode, StartupOptionsPlacement } from \"./pool-config.ts\";\nexport {\n ISOLATION_LEVEL_SQL,\n beginStatement,\n classifyRawTransactionControl,\n refuseNestedTransactionOptions,\n statementTimeoutStatement,\n} from \"./transaction-sql.ts\";\nexport type { RawTransactionControl } 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 * One-Sync batch for node-postgres (round-trip EPIC 6, pipelining).\n *\n * Several statements go to the server as Parse / Bind / Describe / Execute\n * each, followed by ONE Sync — one wait instead of one per statement. It is a\n * `Submittable` (the extension point `client.query(object)` documents and\n * pg-cursor uses) running on an ordinary, NON-pipeline client: the batch is one\n * query object, so the client's own queue, ReadyForQuery handling and status\n * bookkeeping stay node-postgres'.\n *\n * PostgreSQL's extended protocol skips every message after an error until the\n * Sync (as libpq pipeline mode does), so a failing step leaves every later step\n * UNRUN — a failed `BEGIN` can never let a later write run in autocommit, and\n * no later step answers 25P02 (spike-report §1.3, \"pg one-Sync batch\").\n *\n * Only API that node-postgres exposes is used: `connection.parse / bind /\n * describe / execute / sync` (declared on `Connection` in @types/pg), the\n * message handlers of the Submittable contract, and the driver's own\n * `utils.prepareValue` (a property of the `pg` module object, feature-detected)\n * so parameters serialize exactly as `client.query` serializes them. Rows are\n * decoded HERE, with the adapter's query-local type parsers — never through\n * node-postgres' internal `lib/result.js`.\n */\n\nimport pg from \"pg\";\n\n// ─── Types ────────────────────────────────────────────────────────\n\n/** One statement of a batch. Values are the adapter's already-serialized params. */\nexport type BatchStep = {\n readonly text: string;\n readonly values?: readonly unknown[];\n};\n\n/** A step's decoded result, shaped like the node-postgres result the adapter reads. */\nexport type BatchStepResult = {\n readonly rows: Record<string, unknown>[];\n readonly rowCount: number | null;\n readonly command?: string;\n};\n\n/** Per-step outcome after the batch's ReadyForQuery. */\nexport type BatchStepOutcome =\n | { readonly status: \"ok\"; readonly result: BatchStepResult }\n | { readonly status: \"failed\"; readonly error: unknown }\n | { readonly status: \"skipped\" };\n\n/** What a batch reports once it settled. */\nexport type BatchOutcome = {\n /** One entry per step, in order. */\n readonly steps: readonly BatchStepOutcome[];\n /**\n * A failure WITHOUT a server answer — the socket broke, or the client's\n * `query_timeout` fired while the batch was still on the wire. What the\n * server did with the batch is unknown; `steps` lists only what was seen.\n */\n readonly transportError?: unknown;\n};\n\n/** The query-local type parser surface (the adapter's `EXACT_TYPES`). */\nexport type BatchTypeParsers = {\n getTypeParser(oid: number, format?: \"text\" | \"binary\"): (value: string) => unknown;\n};\n\n/** The protocol writer surface of node-postgres' `Connection`. */\ntype BatchConnection = {\n readonly stream?: { cork?: () => void; uncork?: () => void };\n parse(params: { text: string; name?: string; types?: readonly number[] }): void;\n bind(params: { portal?: string; statement?: string; values: readonly unknown[] }): void;\n describe(params: { type: \"P\"; name: string }): void;\n execute(params: { portal: string; rows: number }): void;\n sync(): void;\n sendCopyFail?(message: string): void;\n};\n\n/** The client surface a batch needs; feature-detected, never assumed. */\ntype BatchClient = {\n readonly connection?: unknown;\n readonly pipeline?: unknown;\n};\n\ntype RowDescriptionField = { readonly name: string; readonly dataTypeID: number; readonly format?: \"text\" | \"binary\" };\n\n// ─── Feature detection ────────────────────────────────────────────\n\n/** node-postgres' own parameter serializer, when the installed driver exposes it. */\nconst prepareValue: ((value: unknown) => unknown) | undefined = (() => {\n const utils = (pg as unknown as { utils?: { prepareValue?: unknown } }).utils;\n return typeof utils?.prepareValue === \"function\"\n ? (utils.prepareValue as (value: unknown) => unknown)\n : undefined;\n})();\n\n/**\n * Whether `client` can run a one-Sync batch: a node-postgres client whose\n * connection exposes the protocol writers, NOT in pipeline mode (pipeline mode\n * sends a Sync per query and was rejected by the spike), with the driver's\n * value serializer available. Test doubles and foreign clients answer false\n * and keep today's one-statement-per-wait path.\n */\nexport function supportsBatch(params: { client: unknown }): boolean {\n if (prepareValue === undefined) return false;\n const client = params.client as BatchClient | null;\n if (client === null || typeof client !== \"object\" || client.pipeline === true) return false;\n const connection = client.connection as Partial<Record<keyof BatchConnection, unknown>> | null | undefined;\n return connection !== null && typeof connection === \"object\" &&\n typeof connection.parse === \"function\" &&\n typeof connection.bind === \"function\" &&\n typeof connection.describe === \"function\" &&\n typeof connection.execute === \"function\" &&\n typeof connection.sync === \"function\";\n}\n\n/**\n * Serialize a step's parameters with the driver's own `prepareValue`. Runs\n * BEFORE anything is written, so a value the driver cannot serialize refuses\n * the whole batch with nothing sent (node-postgres' own path closes and syncs\n * instead; the caller-visible error is the same driver error).\n */\nexport function prepareBatchValues(params: { values: readonly unknown[] }): unknown[] {\n const prepare = prepareValue as (value: unknown) => unknown;\n return params.values.map((value) => prepare(value));\n}\n\n// ─── The batch ────────────────────────────────────────────────────\n\n/** `INSERT 0 3` → command `INSERT`, rows 3 (the CommandComplete tag grammar). */\nconst COMMAND_TAG = /^([A-Za-z]+)(?: (\\d+))?(?: (\\d+))?/;\n\n/** A server ErrorResponse (pg-protocol `DatabaseError`), as opposed to a client/socket failure. */\nfunction isServerError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) return false;\n const candidate = error as { code?: unknown; severity?: unknown };\n return typeof candidate.code === \"string\" && /^[0-9A-Z]{5}$/.test(candidate.code) && typeof candidate.severity === \"string\";\n}\n\n/**\n * The Submittable. Construct with steps whose values are already prepared\n * ({@link prepareBatchValues}); pass to `client.query(batch)`; await\n * {@link PgBatch.done}.\n */\nexport class PgBatch {\n /** Settles once: at the batch's ReadyForQuery, at its first server error, or at a transport failure. */\n readonly done: Promise<BatchOutcome>;\n /**\n * Set by node-postgres when the client has a `query_timeout`: its wrapper\n * clears the read-timeout timer. Called once when the batch settles, so a\n * timer never outlives the batch (fix lane FX-A, RV5 MI-6).\n */\n callback: ((error: unknown, result?: unknown) => void) | undefined = undefined;\n\n private readonly steps: readonly BatchStep[];\n private readonly types: BatchTypeParsers;\n private readonly outcomes: BatchStepOutcome[] = [];\n private parsers: ((value: string) => unknown)[] = [];\n private fields: readonly RowDescriptionField[] = [];\n private rows: Record<string, unknown>[] = [];\n /** The first type-parser failure of the current step; its remaining rows are skipped. */\n private rowError: { readonly error: unknown } | undefined = undefined;\n private settled = false;\n private resolve: (outcome: BatchOutcome) => void = () => undefined;\n\n constructor(params: { steps: readonly BatchStep[]; types: BatchTypeParsers }) {\n this.steps = params.steps;\n this.types = params.types;\n this.done = new Promise<BatchOutcome>((resolve) => {\n this.resolve = resolve;\n });\n }\n\n /** Called by node-postgres when the batch reaches the head of the client's queue. */\n submit(connection: unknown): void {\n const writer = connection as BatchConnection;\n writer.stream?.cork?.();\n try {\n for (const step of this.steps) {\n writer.parse({ text: step.text, types: [] });\n writer.bind({ values: step.values ?? [] });\n writer.describe({ type: \"P\", name: \"\" });\n writer.execute({ portal: \"\", rows: 0 });\n }\n writer.sync();\n } finally {\n writer.stream?.uncork?.();\n }\n }\n\n handleRowDescription(message: { fields: readonly RowDescriptionField[] }): void {\n this.fields = message.fields;\n this.parsers = message.fields.map((field) => this.types.getTypeParser(field.dataTypeID, field.format ?? \"text\"));\n this.rows = [];\n }\n\n handleDataRow(message: { fields: readonly (string | null)[] }): void {\n if (this.settled || this.rowError !== undefined) return;\n // Same object shape as node-postgres' default row mode: one property per\n // column in RowDescription order; a repeated name keeps its first position\n // and its last value.\n const row: Record<string, unknown> = {};\n try {\n for (let index = 0; index < this.fields.length; index++) {\n const raw = message.fields[index] ?? null;\n row[(this.fields[index] as RowDescriptionField).name] = raw === null ? null : (this.parsers[index] as (value: string) => unknown)(raw);\n }\n } catch (error) {\n // APPROVED driver boundary (plan §3 \"Final-review decisions\", RV5 MJ-2):\n // a type parser (the application's global `pg.types` reach it through\n // EXACT_TYPES) may throw. Escaping here would leave node-postgres'\n // socket `data` handler mid-buffer — the batch never settles and the\n // error becomes an uncaught exception. node-postgres' own Query keeps\n // the first parser error, skips the remaining rows and rejects with it\n // (`_canceledDueToError`); the batch reports it as this step's failure.\n this.rowError = { error };\n return;\n }\n this.rows.push(row);\n }\n\n handleCommandComplete(message: { text?: string; command?: string }): void {\n if (this.settled) return;\n if (this.rowError !== undefined) {\n // The server ran the step; its rows could not be decoded.\n this.outcomes.push({ status: \"failed\", error: this.rowError.error });\n this.rowError = undefined;\n this.rows = [];\n this.fields = [];\n this.parsers = [];\n return;\n }\n const match = COMMAND_TAG.exec(message.text ?? message.command ?? \"\");\n const command = match?.[1];\n const rowCount = match?.[3] !== undefined\n ? Number.parseInt(match[3], 10)\n : match?.[2] !== undefined ? Number.parseInt(match[2], 10) : null;\n this.outcomes.push({ status: \"ok\", result: { rows: this.rows, rowCount, ...(command === undefined ? {} : { command }) } });\n this.rows = [];\n this.fields = [];\n this.parsers = [];\n }\n\n handleEmptyQuery(): void {\n if (this.settled) return;\n this.outcomes.push({ status: \"ok\", result: { rows: [], rowCount: null } });\n }\n\n handleError(error: unknown): void {\n if (this.settled) return;\n if (!isServerError(error)) {\n // No server answer: the socket broke, or `query_timeout` fired with the\n // batch still on the wire (node-postgres keeps the query running there).\n this.finish({ transportError: error });\n return;\n }\n // The server skips everything up to the Sync: the remaining steps never\n // ran. Settle NOW: node-postgres drops the active query before routing an\n // ErrorResponse (pg 8.23 `client.js` `_handleErrorMessage`), so this\n // batch never sees its ReadyForQuery. The client queues the next query\n // until that ReadyForQuery arrives, so a ROLLBACK sent after this still\n // runs after the Sync. A parser failure of this step wins, as in\n // node-postgres' Query.\n this.outcomes.push({ status: \"failed\", error: this.rowError === undefined ? error : this.rowError.error });\n while (this.outcomes.length < this.steps.length) this.outcomes.push({ status: \"skipped\" });\n this.finish({});\n }\n\n handleReadyForQuery(): void {\n this.finish({});\n }\n\n handlePortalSuspended(): void {\n // Never requested: every Execute asks for all rows.\n }\n\n handleCopyInResponse(connection: unknown): void {\n (connection as BatchConnection).sendCopyFail?.(\"COPY FROM STDIN is not supported inside a batch\");\n }\n\n handleCopyData(): void {\n // COPY TO STDOUT data is not collected (node-postgres' Query ignores it too).\n }\n\n private finish(params: { transportError?: unknown }): void {\n if (this.settled) return;\n this.settled = true;\n this.callback?.(params.transportError ?? null);\n this.resolve({\n steps: this.outcomes,\n ...(params.transportError === undefined ? {} : { transportError: params.transportError }),\n });\n }\n}\n\n/** Run `steps` as one batch on `client` and wait for it to settle. */\nexport async function runBatch(params: {\n /** A checked-out client that passed {@link supportsBatch}. */\n client: { query: (...args: never[]) => unknown };\n steps: readonly BatchStep[];\n types: BatchTypeParsers;\n}): Promise<BatchOutcome> {\n const batch = new PgBatch({ steps: params.steps, types: params.types });\n (params.client.query as (submittable: PgBatch) => unknown).call(params.client, batch);\n return batch.done;\n}\n\n/**\n * The first failure of a settled batch: the transport error, or the first\n * failed step with its index. `undefined` when every step succeeded.\n */\nexport function firstBatchFailure(params: { outcome: BatchOutcome }): { readonly index: number; readonly error: unknown } | undefined {\n const { outcome } = params;\n const index = outcome.steps.findIndex((step) => step.status === \"failed\");\n if (index !== -1) return { index, error: (outcome.steps[index] as { error: unknown }).error };\n if (outcome.transportError !== undefined) return { index: outcome.steps.length, error: outcome.transportError };\n return undefined;\n}\n",
|
|
7
|
+
"import pg from \"pg\";\nimport { VibeError } from \"@vibeorm/schema\";\nimport type { DatabaseAdapter } from \"@vibeorm/runtime\";\nimport { assertReadExecution, readCancelledError } from \"@vibeorm/runtime/adapter-kit\";\nimport { PG_READ_CANCELLATION } from \"./config.ts\";\nimport { pgErrorSqlstate, runMapped } from \"./errors.ts\";\nimport type { PgQueryConfig, PgQueryResultsLike, PgPoolClientLike } from \"./index.ts\";\n\ntype Execution = Parameters<DatabaseAdapter[\"execute\"]>[0];\ntype ControlClient = {\n connect(): Promise<void>;\n query(config: PgQueryConfig): Promise<PgQueryResultsLike>;\n end(): Promise<void>;\n on(event: \"error\", listener: (error: unknown) => void): unknown;\n};\n\n/** One pinned transaction's running-read transport. Original and control settlement are both awaited. */\nexport type PgReadCancellation = {\n readonly support: \"before-dispatch\" | \"running\";\n run(params: {\n config: PgQueryConfig;\n execution?: Execution[\"execution\"];\n onReadControl?: Execution[\"onReadControl\"];\n send: () => Promise<PgQueryResultsLike>;\n }): Promise<PgQueryResultsLike>;\n};\n\nfunction rowsOf(result: PgQueryResultsLike): Record<string, unknown>[] {\n return (Array.isArray(result) ? result.at(-1)?.rows : result.rows) ?? [];\n}\n\n/**\n * Official-driver control SQL seam. The target must already be in a transaction,\n * held exclusively until this run settles; every cancel attempt discards it.\n * PostgreSQL documents pg_cancel_backend as signal dispatch, not SQL settlement:\n * https://www.postgresql.org/docs/18/functions-admin.html#FUNCTIONS-ADMIN-SIGNAL\n */\nexport function createPgReadCancellation(params: {\n connectionString?: string;\n target: Pick<PgPoolClientLike, \"query\" | \"pipeline\" | \"connectionParameters\">;\n discard: () => void;\n /** Destroy exactly the held target on failed/unsettled cancellation, never a pool-wide close. */\n terminate: () => void;\n /** Hermetic driver injection; production uses the installed official pg.Client. */\n createControl?: (params: { connectionString: string }) => ControlClient;\n}): PgReadCancellation {\n const support = params.connectionString === undefined ? \"before-dispatch\" : \"running\";\n return {\n support,\n async run(query) {\n assertReadExecution({ execution: query.execution, effect: \"read\", support, provider: \"postgresql\", stage: \"waiting-for-connection\" });\n if (query.execution?.cancellation !== \"running\") return query.send();\n if (params.target.pipeline === true || (params.target.connectionParameters?.query_timeout ?? 0) > 0) {\n throw new VibeError({ code: \"VIBE_UNSUPPORTED_CAPABILITY\", message: \"Running cancellation requires a non-pipelined target without local query_timeout\", meta: { capability: \"readCancellation\", reason: \"target-lifetime\" } });\n }\n const address = params.connectionString;\n if (address === undefined) throw new VibeError({ code: \"VIBE_INTERNAL\", message: \"Running cancellation requires a control address\" });\n const token = `vibe_cancel_${crypto.randomUUID()}`;\n // URL keys override explicit pg config. Remove local query_timeout (not\n // SQL settlement) and pin the server/connection bound IN the URL itself.\n if (!URL.canParse(address)) throw new VibeError({ code: \"VIBE_CONFIG\", message: \"The PostgreSQL cancellation control address must be a URL\" });\n const url = new URL(address);\n url.searchParams.delete(\"query_timeout\");\n url.searchParams.set(\"connect_timeout\", String(PG_READ_CANCELLATION.controlTimeoutMs / 1_000));\n url.searchParams.set(\"statement_timeout\", String(PG_READ_CANCELLATION.controlTimeoutMs));\n url.searchParams.set(\"options\", `-c statement_timeout=${PG_READ_CANCELLATION.controlTimeoutMs} -c lock_timeout=${PG_READ_CANCELLATION.controlTimeoutMs}`);\n const control = await runMapped(async () => params.createControl?.({ connectionString: url.toString() }) ??\n new pg.Client({ connectionString: url.toString(), connectionTimeoutMillis: PG_READ_CANCELLATION.controlTimeoutMs, query_timeout: 0 }) as unknown as ControlClient);\n // Checked-out pg clients do not have pool error handlers. Own this one.\n control.on(\"error\", () => undefined);\n const signal = query.execution.signal;\n let cancel: Promise<void> | undefined;\n let attempted = false;\n let signalSent = false;\n let finished = false;\n let controlFailure: unknown;\n let terminated = false;\n let settlementTimer: ReturnType<typeof setTimeout> | undefined;\n let preflightAborted = false;\n const abortPreflight = (): void => {\n preflightAborted = true;\n params.discard();\n if (!terminated) { terminated = true; params.terminate(); }\n };\n let original: Promise<PgQueryResultsLike> | undefined;\n const account = (stage: \"preflight\" | \"cancel\", send: () => Promise<PgQueryResultsLike>): Promise<PgQueryResultsLike> =>\n runMapped(send).then(\n (result) => { query.onReadControl?.({ stage, rows: rowsOf(result).length }); return result; },\n (error: unknown) => { query.onReadControl?.({ stage, rows: 0 }); throw error; },\n );\n let listener: (() => void) | undefined;\n const operation = runMapped(() => control.connect()).then(async () => {\n assertReadExecution({ execution: query.execution, effect: \"read\", support, provider: \"postgresql\", stage: \"waiting-for-connection\" });\n // Transaction-local nonce survives transaction poolers and expires at\n // commit/rollback. Exact backend_start text avoids timestamp truncation.\n signal?.addEventListener(\"abort\", abortPreflight, { once: true });\n const witness = rowsOf(await account(\"preflight\", () => params.target.query({\n text: \"SELECT pg_backend_pid() AS pid, extract(epoch FROM backend_start)::text AS backend_start, datname, usename, set_config('application_name', $1, true) AS tag FROM pg_stat_activity WHERE pid = pg_backend_pid()\",\n values: [token],\n })))[0];\n signal?.removeEventListener(\"abort\", abortPreflight);\n if (preflightAborted) throw readCancelledError({ stage: \"waiting-for-connection\" });\n if (witness === undefined || typeof witness.pid !== \"number\" || typeof witness.backend_start !== \"string\" || typeof witness.datname !== \"string\" || typeof witness.usename !== \"string\" || witness.tag !== token) {\n throw new VibeError({ code: \"VIBE_UNSUPPORTED_CAPABILITY\", message: \"Cannot prove the exact PostgreSQL cancellation target\", meta: { capability: \"readCancellation\", reason: \"target-witness\" } });\n }\n assertReadExecution({ execution: query.execution, effect: \"read\", support, provider: \"postgresql\", stage: \"waiting-for-connection\" });\n listener = (): void => {\n if (finished || attempted) return;\n attempted = true;\n params.discard();\n const failControl = (error: unknown): never => {\n controlFailure = error;\n if (!terminated) { terminated = true; params.terminate(); }\n throw error;\n };\n settlementTimer = setTimeout(() => {\n controlFailure = new VibeError({ code: \"VIBE_ADAPTER\", message: \"PostgreSQL read did not settle after cancellation; its exact session was discarded\", meta: { reason: \"read-cancellation-unsettled\", databaseCancelled: false } });\n if (!terminated) { terminated = true; params.terminate(); }\n }, PG_READ_CANCELLATION.controlTimeoutMs);\n cancel = account(\"cancel\", () => control.query({\n text: \"SELECT pg_cancel_backend(pid) AS signalled FROM pg_stat_activity WHERE pid = $1 AND extract(epoch FROM backend_start)::text = $2 AND datname = $3 AND usename = $4 AND application_name = $5 AND state = 'active' AND pid <> pg_backend_pid()\",\n values: [witness.pid, witness.backend_start, witness.datname, witness.usename, token],\n })).then((result) => {\n signalSent = rowsOf(result)[0]?.signalled === true;\n if (!signalSent) failControl(new VibeError({ code: \"VIBE_UNSUPPORTED_CAPABILITY\", message: \"The control endpoint did not signal the exact PostgreSQL target\", meta: { capability: \"readCancellation\", reason: \"target-not-signalled\", databaseCancelled: false } }));\n }, failControl);\n // The original query may still be running; consume control rejection\n // now, then report it after both operations have drained.\n void cancel.then(() => undefined, () => undefined);\n };\n // Submit the target first. The event loop cannot interleave abort between\n // this synchronous send and listener registration.\n original = runMapped(query.send);\n signal?.addEventListener(\"abort\", listener, { once: true });\n if (signal?.aborted === true) listener();\n return original.then(\n async (result) => {\n finished = true;\n signal?.removeEventListener(\"abort\", listener!);\n if (cancel !== undefined) await cancel;\n if (controlFailure !== undefined) throw controlFailure;\n if (signal?.aborted === true) throw readCancelledError({ stage: \"running\" });\n return result;\n },\n async (error: unknown) => {\n finished = true;\n signal?.removeEventListener(\"abort\", listener!);\n if (cancel !== undefined) await cancel.then(() => undefined, () => undefined);\n // Attribution requires this operation's own successful control\n // signal AND the original server's 57014. A timeout alone stays as-is.\n const sqlstate = error instanceof VibeError ? error.meta.sqlstate : pgErrorSqlstate({ error });\n if (terminated && controlFailure !== undefined && sqlstate === undefined) throw controlFailure;\n if (attempted && signalSent && sqlstate === \"57014\") {\n const cancelled = readCancelledError({ stage: \"running\", databaseCancelled: true, cause: error });\n throw new VibeError({ code: cancelled.code, message: cancelled.message, meta: { ...cancelled.meta, sqlstate: \"57014\" }, cause: error });\n }\n throw error;\n },\n );\n });\n return operation.then(\n async (result) => { signal?.removeEventListener(\"abort\", abortPreflight); if (settlementTimer !== undefined) clearTimeout(settlementTimer); if (listener !== undefined) signal?.removeEventListener(\"abort\", listener); await runMapped(() => control.end()); return result; },\n async (error: unknown) => {\n signal?.removeEventListener(\"abort\", abortPreflight);\n if (settlementTimer !== undefined) clearTimeout(settlementTimer);\n if (listener !== undefined) signal?.removeEventListener(\"abort\", listener);\n await runMapped(() => control.end()).then(() => undefined, () => undefined);\n if (preflightAborted) throw readCancelledError({ stage: \"waiting-for-connection\", cause: error });\n throw error;\n },\n );\n },\n };\n}\n",
|
|
8
|
+
"/** Bounds the independent PostgreSQL cancellation control path and post-abort settlement. */\nexport const PG_READ_CANCELLATION: Readonly<{ controlTimeoutMs: number }> = Object.freeze({\n controlTimeoutMs: 5_000,\n});\n",
|
|
7
9
|
"/**\n * Driver-error mapping for node-postgres (`pg`).\n *\n * Constitution rule 5: a raw driver error never escapes an adapter. Every\n * failure becomes a `VibeError` with a stable code, the driver error attached\n * as `cause`, and the SQLSTATE (plus constraint/table/column when the server\n * reported them) in `meta`.\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 PG_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 /**\n * The other data-exception SQLSTATEs the same bad INPUT raises — a string\n * too long for varchar(n) (22001), a number outside the column's range\n * (22003), an unparseable date/time (22007) or one whose fields overflow\n * (22008). mysql maps its peers (1406 / 1264 / 1292) to VIBE_VALIDATION;\n * postgres reported them as VIBE_ADAPTER, so the same request was a 400 on\n * one dialect and a 500 on the other.\n */\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 /** deadlock_detected */\n \"40P01\": \"VIBE_TRANSACTION\",\n /** query_canceled — statement_timeout fired */\n \"57014\": \"VIBE_TRANSACTION\",\n /**\n * lock_not_available — a `nowait` locking read whose rows were already\n * locked, OR a `lock_timeout` that expired while blocking. The server reports\n * both with this ONE SQLSTATE (verified on postgres 18.6: \"could not obtain\n * lock on row in relation\" and \"canceling statement due to lock timeout\"), so\n * VibeORM does not invent a distinction the server does not make. Nothing is\n * retried automatically — waiting, skipping or failing is the caller's policy.\n */\n \"55P03\": \"VIBE_LOCK_NOT_AVAILABLE\",\n /**\n * read_only_sql_transaction — a write inside a READ ONLY transaction\n * (`accessMode: \"readOnly\"`, or a server whose default is read-only, e.g. a\n * hot standby). Before round-trip EPIC 3 it fell through to VIBE_ADAPTER.\n */\n \"25006\": \"VIBE_TRANSACTION\",\n});\n\n/** SQLSTATEs that additionally explain themselves through `meta.reason`. */\nexport const PG_SQLSTATE_REASONS: Readonly<Record<string, string>> = Object.freeze({\n \"23P01\": \"exclusion-violation\",\n \"57014\": \"timeout\",\n \"55P03\": \"lock-not-available\",\n /** Same reason string mysql already uses for errno 1213 — one word per dialect. */\n \"40P01\": \"deadlock\",\n \"40001\": \"serialization-failure\",\n \"25006\": \"read-only-transaction\",\n});\n\n// ─── Reading driver errors ────────────────────────────────────────\n\n/**\n * A PostgreSQL SQLSTATE is exactly five digits/uppercase letters (e.g. `40P01`)\n * and every one PostgreSQL defines contains a digit. Requiring one keeps a\n * five-letter socket errno (`EPIPE`, `EPERM`) from passing as a server answer:\n * a COMMIT that failed with it is a lost connection, not a definite rollback\n * (fix lane FX-A, RV2 \"outside EPIC 3\").\n */\nconst SQLSTATE_PATTERN = /^(?=[A-Z]*\\d)[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 pg driver error. `pg` puts the server's SQLSTATE\n * on `error.code`; client-side failures (`ECONNREFUSED`, `ETIMEDOUT`, …) use\n * the same field for a non-SQLSTATE string, which this rejects.\n */\nexport function pgErrorSqlstate(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 `pg` 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 mapPgDriverError(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 = pgErrorSqlstate({ error });\n const code: VibeErrorCode =\n (sqlstate === undefined ? undefined : PG_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 : PG_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 postgres 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 mapPgDriverError({ error });\n }\n}\n",
|
|
8
10
|
"/**\n * Parameter serialization for node-postgres — the ORM path and the raw path\n * differ, deliberately (see LEARNINGS.md).\n *\n * `pg` natively turns a JS array into a PostgreSQL array literal (`{1,2,3}`),\n * which is exactly what a raw query wants. The ORM hot path cannot tell whether\n * a JS array is destined for a `jsonb` column or for a scalar-list / `= ANY($n)`\n * parameter, so it JSON-stringifies arrays and objects — scalar-list values are\n * tagged with `PgArrayParam` by `formatArrayParam` and unwrapped here, reaching\n * the driver as the untouched JS array.\n */\n\n// ─── Scalar-list marker ───────────────────────────────────────────\n\n/**\n * Opaque token returned by `pgAdapter().formatArrayParam` marking a value as a\n * PostgreSQL array parameter (rather than a JSON value that happens to be an\n * array). The wrapped array reaches `pg` as-is; the driver serializes it.\n */\nexport class PgArrayParam {\n /** The untouched scalar-list values. */\n readonly values: unknown[];\n\n constructor(values: unknown[]) {\n this.values = values;\n }\n}\n\n// ─── Serialization ────────────────────────────────────────────────\n\n/**\n * `Date` → UTC ISO-8601 text (`2026-03-08T19:38:08.538Z`).\n *\n * node-postgres' own serializer writes the host's LOCAL wall-clock plus its\n * offset (`2026-03-08T20:38:08.538+01:00` under Europe/Oslo). A `timestamptz`\n * parameter keeps the instant either way, but a `timestamp without time zone`\n * or `date` parameter DISCARDS the offset (\"PostgreSQL will silently ignore\n * any time zone indication\"), so the stored wall-clock would depend on the\n * host's TZ — and differ from what bun:sql, PGlite and Prisma store for the\n * same `Date` (all UTC). The UTC spelling stores one value on every host and\n * matches the UTC read side (`EXACT_TYPES` in index.ts). Dates the ISO form\n * cannot spell (invalid, BC, beyond year 9999) keep the driver's serializer.\n */\nexport function serializeDate(params: { value: Date }): unknown {\n const { value } = params;\n const time = value.getTime();\n if (Number.isNaN(time)) return value;\n const year = value.getUTCFullYear();\n if (year < 1 || year > 9999) return value;\n return value.toISOString();\n}\n\n/** True for values `pg` must receive verbatim (binary, primitives, null). */\nfunction isDriverNativeValue(value: unknown): boolean {\n return (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n );\n}\n\n/**\n * ORM hot path: unwrap scalar-list markers, spell `Date` as UTC, JSON-stringify\n * plain objects **and** plain arrays (they can only be `json`/`jsonb` values\n * at this point).\n */\nexport function serializeParams(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgArrayParam) return serializeList({ values: value.values });\n if (value instanceof Date) return serializeDate({ value });\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\n}\n\n/**\n * A scalar-list parameter: `Date` elements spelled as UTC (the driver's array\n * serializer would otherwise write them in local time); a list without dates\n * reaches `pg` as the untouched array.\n */\nfunction serializeList(params: { values: unknown[] }): unknown[] {\n const { values } = params;\n return values.some((value) => value instanceof Date)\n ? values.map((value) => (value instanceof Date ? serializeDate({ value }) : value))\n : values;\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 *\n * Trade-off (v1 lesson): binding a JS array **into** a `jsonb` column from a raw\n * query means calling `JSON.stringify` yourself.\n */\nexport function serializeParamsRaw(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgArrayParam) return serializeList({ values: value.values });\n if (Array.isArray(value)) return serializeList({ values: value });\n if (value instanceof Date) return serializeDate({ value });\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\n}\n",
|
|
9
11
|
"/**\n * Pool configuration for the pool {@link pgAdapter} builds itself.\n *\n * Per-connection PostgreSQL settings travel in the `options` startup\n * parameter (`-c key=value` pairs the server parses at connect time), so every\n * pooled connection inherits them without a per-query round trip. Two\n * node-postgres facts shape the merge below, both read from the installed\n * driver (pg 8.23):\n *\n * - `ConnectionParameters` parses `connectionString` OVER the explicit config\n * (`Object.assign({}, config, parse(connectionString))`), so a URL that\n * already carries `?options=…` silently wins over `config.options`.\n * - `pg-connection-string` turns EVERY query parameter into a config key, so\n * `?options=-c search_path=x` arrives as `config.options`.\n *\n * Hence: with no `options` in the URL the setting rides `config.options`\n * (nothing to collide with); with one, the URL's value is EXTENDED in place\n * and a `plan_cache_mode` the URL already sets is left alone — the caller's\n * connection string is the more explicit statement of intent.\n */\n\nimport { VibeError } from \"@vibeorm/schema\";\n\n/**\n * PostgreSQL plan-cache behaviour for named prepared statements\n * (`preparedStatements: true`). `auto` (the server default) lets PostgreSQL\n * switch to a generic plan after ~5 executions — cheaper per call, but it can\n * pick worse indexes for range/LIKE/skewed filters; `force_custom_plan`\n * replans with the real parameter values every time and keeps almost the whole\n * prepared-statement gain (the saving is the parse/analyze and the protocol\n * round, not the plan — measured −17…−25 % on reads either way, see\n * `.ai/plans/26.09.08 - performance-join-strategy.md` §1.3).\n */\nexport type PlanCacheMode = \"auto\" | \"force_custom_plan\" | \"force_generic_plan\";\n\nconst PLAN_CACHE_MODES: readonly PlanCacheMode[] = [\"auto\", \"force_custom_plan\", \"force_generic_plan\"];\n\nfunction isPlanCacheMode(value: unknown): value is PlanCacheMode {\n return typeof value === \"string\" && (PLAN_CACHE_MODES as readonly string[]).includes(value);\n}\n\n/** Validate a `planCacheMode` option value; the string ends up inside a startup parameter verbatim. */\nexport function assertPlanCacheMode(params: { value: unknown }): PlanCacheMode {\n const { value } = params;\n if (isPlanCacheMode(value)) return value;\n throw new VibeError({\n code: \"VIBE_CONFIG\",\n message: `planCacheMode must be one of ${PLAN_CACHE_MODES.map((mode) => `\"${mode}\"`).join(\", \")}, received ${JSON.stringify(value)}`,\n meta: { planCacheMode: value, allowed: PLAN_CACHE_MODES },\n });\n}\n\n/** The `options` startup parameter and (possibly rewritten) connection string carrying it. */\nexport type StartupOptionsPlacement = {\n readonly connectionString: string | undefined;\n /** `undefined` when nothing needs setting, or the URL already sets the parameter itself. */\n readonly options: string | undefined;\n};\n\n/**\n * Place `plan_cache_mode` into the startup options, honouring what the URL\n * already carries (see the module header for the precedence rule).\n */\nexport function placePlanCacheMode(params: {\n connectionString: string | undefined;\n planCacheMode: PlanCacheMode;\n}): StartupOptionsPlacement {\n const { connectionString, planCacheMode } = params;\n if (planCacheMode === \"auto\") return { connectionString, options: undefined };\n const setting = `-c plan_cache_mode=${planCacheMode}`;\n\n if (connectionString === undefined || !URL.canParse(connectionString)) {\n return { connectionString, options: setting };\n }\n const url = new URL(connectionString);\n const existing = url.searchParams.get(\"options\");\n if (existing === null) {\n // Absent from the URL → the parsed URL carries no `options` key and\n // `config.options` survives the driver's merge untouched.\n return { connectionString, options: setting };\n }\n if (/(^|\\s)-c\\s*plan_cache_mode=/.test(existing)) {\n // The URL is the more explicit statement of intent; leave it alone.\n return { connectionString, options: undefined };\n }\n const merged = existing.trim().length === 0 ? setting : `${existing.trim()} ${setting}`;\n // The URL's own value wins in the driver, so the merge has to live THERE.\n url.searchParams.set(\"options\", merged);\n return { connectionString: url.toString(), options: merged };\n}\n\n/** Build the `pg.Pool` constructor config for an adapter-owned pool. */\nexport function buildPoolConfig(params: {\n connectionString: string | undefined;\n max: number;\n statementTimeout?: number;\n connectionTimeout?: number;\n planCacheMode?: PlanCacheMode;\n}): Record<string, unknown> {\n const { max, statementTimeout, connectionTimeout } = params;\n const placement = placePlanCacheMode({\n connectionString: params.connectionString,\n planCacheMode: params.planCacheMode ?? \"auto\",\n });\n const config: Record<string, unknown> = { connectionString: placement.connectionString, max };\n if (statementTimeout !== undefined) config.statement_timeout = statementTimeout;\n if (connectionTimeout !== undefined) config.connectionTimeoutMillis = connectionTimeout;\n if (placement.options !== undefined) config.options = placement.options;\n return config;\n}\n",
|
|
10
12
|
"/**\n * Bounded prepared-statement NAME registry, shared per pool.\n *\n * With `preparedStatements: true` every SQL text that proves itself repeated\n * gets a stable name, so PostgreSQL keeps a cached plan for it. Three\n * properties matter, and the first version of this file had none of them:\n *\n * 1. **The bound has to be a bound on the SERVER** (F16). Evicting a local\n * mapping and minting a fresh name leaves the old prepared statement on\n * every backend that ever saw it — the local map stayed small while server\n * retention grew without limit. Nor can eviction be repaired with\n * `DEALLOCATE`: a name is prepared per CONNECTION, the adapter does not know\n * which pooled connections hold it, and deallocating behind node-postgres'\n * back invalidates its own per-client name registry (it would keep sending\n * the name and the server would refuse it).\n *\n * So the registry never re-uses a name for another text and never evicts:\n * it allocates at most `max` names, and once that budget is spent a new text\n * runs UNNAMED — bounded preparation fallback. Server retention is then\n * `max` per connection, permanently. Statements still bind their parameters;\n * only the server-side plan cache is skipped.\n *\n * 2. **Adapters sharing one pool must not collide** (F18). Each adapter used to\n * start its own counter at zero, so two adapters on one pool both sent\n * `vibeorm_0` for different SQL and postgres refused the second\n * (\"Prepared statements must be unique\"). Names are therefore allocated by a\n * registry shared per pool, which also stops N adapters from preparing the\n * same text N times.\n *\n * 3. **A one-off text must not spend the budget.** Because names never evict\n * (property 1), allocating on FIRST sight hands the whole budget to whatever\n * arrives first. Offset pagination emits a distinct text per page, so a\n * paginating workload used to burn all `max` names for the pool's lifetime\n * and leave every genuinely hot text unnamed thereafter. A name is therefore\n * allocated on the `promoteAfter`-th sighting of a text. Sightings live in\n * their own bounded LRU (capped at {@link SIGHTINGS_PER_NAME} × `max`);\n * evicting a sighting only delays a promotion, it can never name the wrong\n * text or exceed the budget. A hot text loses exactly one prepared\n * execution; a one-off text costs the server nothing.\n */\n\n/** Sightings of a text before it earns a name. */\nconst DEFAULT_PROMOTE_AFTER = 2;\n\n/**\n * Sighting slots per name of budget. The sighting map is a pure heuristic —\n * over-sizing it only wastes memory, under-sizing it only delays promotions —\n * so a small multiple of the name budget is enough to see a repeat through a\n * burst of one-off texts.\n */\nconst SIGHTINGS_PER_NAME = 4;\n\n/** Registry mapping SQL text to the prepared-statement name `pg` should send. */\nexport type StatementNameCache = {\n /**\n * Stable statement name for `text` — or `undefined` meaning \"send this one\n * unnamed\", which is the answer until the text has been seen `promoteAfter`\n * times and again once the name budget is spent.\n */\n getName(params: { text: string }): string | undefined;\n /** Number of names allocated — diagnostics and tests. */\n size(): number;\n /** Number of texts tracked toward promotion — diagnostics and tests. */\n sightings(): number;\n};\n\n/**\n * Create a statement-name registry that allocates at most `max` names\n * (`vibeorm_0`, `vibeorm_1`, …), each on the `promoteAfter`-th sighting of its\n * text (default 2). Texts seen fewer times, and texts arriving after the budget\n * is spent, get no name and run unprepared, so the server's prepared-statement\n * retention stays bounded and a stream of one-off texts cannot consume it.\n */\nexport function createStatementNameCache(params: { max: number; promoteAfter?: number }): StatementNameCache {\n const max = Math.max(0, params.max);\n const promoteAfter = Math.max(1, params.promoteAfter ?? DEFAULT_PROMOTE_AFTER);\n const sightingsMax = max * SIGHTINGS_PER_NAME;\n /** Never evicted, never re-used: this map is the server-side bound. */\n const names = new Map<string, string>();\n /** Insertion-ordered, so the first key is the least recently seen text. */\n const sightings = new Map<string, number>();\n let counter = 0;\n\n return {\n getName(nameParams: { text: string }): string | undefined {\n const { text } = nameParams;\n const existing = names.get(text);\n if (existing !== undefined) return existing;\n // Budget spent (and the `max: 0` case): nothing left to promote into, so\n // do not spend memory counting sightings either.\n if (names.size >= max) return undefined;\n\n const seen = (sightings.get(text) ?? 0) + 1;\n if (seen < promoteAfter) {\n // Re-insert to move the text to the most-recently-seen end.\n sightings.delete(text);\n sightings.set(text, seen);\n while (sightings.size > sightingsMax) {\n const oldest = sightings.keys().next();\n if (oldest.done === true) break;\n sightings.delete(oldest.value);\n }\n return undefined;\n }\n\n sightings.delete(text);\n const name = `vibeorm_${counter++}`;\n names.set(text, name);\n return name;\n },\n\n size(): number {\n return names.size;\n },\n\n sightings(): number {\n return sightings.size;\n },\n };\n}\n",
|
|
11
13
|
"/**\n * Transaction-control SQL text (postgres dialect).\n *\n * Kept apart from the adapter so the mapping from `TransactionOptions` to SQL\n * is unit-testable without a server, and so the timeout value is validated\n * before it is ever interpolated into a statement.\n */\n\nimport type { IsolationLevel } from \"@vibeorm/runtime\";\nimport { VibeError } from \"@vibeorm/schema\";\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 * `BEGIN`, with an `ISOLATION LEVEL` clause and/or `READ ONLY` when the caller\n * asked for them (`BEGIN ISOLATION LEVEL SERIALIZABLE, READ ONLY`).\n */\nexport function beginStatement(params: { isolationLevel?: IsolationLevel; readOnly?: boolean }): string {\n const { isolationLevel, readOnly } = params;\n const modes: string[] = [];\n if (isolationLevel !== undefined) modes.push(`ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[isolationLevel]}`);\n if (readOnly === true) modes.push(\"READ ONLY\");\n return modes.length === 0 ? \"BEGIN\" : `BEGIN ${modes.join(\", \")}`;\n}\n\n/**\n * The whole opening of a transaction as ONE simple-protocol message (round-trip\n * EPIC 3, A-02): `BEGIN …` alone, or `BEGIN …; SET LOCAL statement_timeout = N`\n * when a timeout is requested — one wait instead of two. Both parts are\n * parameterless text built from validated values. PostgreSQL stops at the\n * first failing statement of the message and the driver rejects with that\n * error, so the caller sees BEGIN's or the SET's own error exactly as when they\n * were two messages; the adapter's ROLLBACK cleanup is unchanged.\n *\n * @throws VibeError `VIBE_VALIDATION` for an invalid timeout (before anything\n * is sent).\n */\nexport function openTransactionStatement(params: {\n isolationLevel?: IsolationLevel;\n readOnly?: boolean;\n timeout?: number;\n}): string {\n const begin = beginStatement(params);\n return params.timeout === undefined ? begin : `${begin}; ${statementTimeoutStatement({ timeout: params.timeout })}`;\n}\n\n/**\n * `SET LOCAL statement_timeout = <ms>` — transaction-scoped, so PostgreSQL\n * cancels an over-long statement with SQLSTATE 57014 (mapped to\n * `VIBE_TRANSACTION` with `meta.reason = \"timeout\"`).\n *\n * @throws VibeError `VIBE_VALIDATION` when the timeout is not a non-negative\n * integer — the value is interpolated into SQL, so it is never trusted blindly.\n */\nexport function statementTimeoutStatement(params: { timeout: number }): string {\n const { timeout } = params;\n if (!Number.isInteger(timeout) || timeout < 0) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `transaction timeout must be a non-negative integer of milliseconds, received ${String(timeout)}`,\n meta: { timeout },\n });\n }\n return `SET LOCAL statement_timeout = ${timeout}`;\n}\n\n// ─── Raw transaction control (session affinity) ─────────────────\n\n/** How a raw statement steers the adapter's session-affine raw transaction. */\nexport type RawTransactionControl = \"open\" | \"close\" | null;\n\nconst RAW_OPEN_PATTERN: RegExp = /^\\s*(?:begin|start\\s+transaction)\\b[^;]*;?\\s*$/i;\nconst RAW_CLOSE_PATTERN: RegExp = /^\\s*(?:commit|end|rollback|abort)(?:\\s+(?:transaction|work))?\\s*;?\\s*$/i;\n\n/**\n * Classify a raw statement as transaction control.\n *\n * @vibeorm/migrate drives transactional DDL by sending literal `BEGIN` /\n * `COMMIT` / `ROLLBACK` through its stateless `SqlExecutor` — sound only when\n * every statement in between rides the SAME connection. Without pinning, a\n * pooled `pool.query(\"BEGIN\")` sequence stays on one client only by LIFO\n * checkout luck; any concurrent traffic silently splits the transaction\n * across connections. The adapter uses this to pin one client for the whole\n * raw transaction (\"open\" checks out, \"close\" releases).\n *\n * Deliberately strict: only PURE single-statement control text matches\n * (`BEGIN`, `BEGIN ISOLATION LEVEL …;`, `COMMIT`, `ROLLBACK WORK`, `END`,\n * `ABORT`). Compound scripts, `ROLLBACK TO SAVEPOINT`, two-phase\n * `COMMIT PREPARED` and `COMMIT AND CHAIN` (which keeps a transaction open)\n * never engage the pinning.\n */\nexport function classifyRawTransactionControl(params: { text: string }): RawTransactionControl {\n if (RAW_OPEN_PATTERN.test(params.text)) return \"open\";\n if (RAW_CLOSE_PATTERN.test(params.text)) return \"close\";\n return null;\n}\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"
|
|
12
14
|
],
|
|
13
|
-
"mappings": "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",
|
|
14
|
-
"debugId": "
|
|
15
|
+
"mappings": ";;AAiBA;AAAA,yBACE;AAAA,wBACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BF,sBAAS;AAET;;;AC3BA;AA8DA,IAAM,gBAA2D,MAAM;AAAA,EACrE,MAAM,QAAS,GAAyD;AAAA,EACxE,OAAO,OAAO,OAAO,iBAAiB,aACjC,MAAM,eACP;AAAA,GACH;AASI,SAAS,aAAa,CAAC,QAAsC;AAAA,EAClE,IAAI,iBAAiB;AAAA,IAAW,OAAO;AAAA,EACvC,MAAM,SAAS,OAAO;AAAA,EACtB,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,OAAO,aAAa;AAAA,IAAM,OAAO;AAAA,EACtF,MAAM,aAAa,OAAO;AAAA,EAC1B,OAAO,eAAe,QAAQ,OAAO,eAAe,YAClD,OAAO,WAAW,UAAU,cAC5B,OAAO,WAAW,SAAS,cAC3B,OAAO,WAAW,aAAa,cAC/B,OAAO,WAAW,YAAY,cAC9B,OAAO,WAAW,SAAS;AAAA;AASxB,SAAS,kBAAkB,CAAC,QAAmD;AAAA,EACpF,MAAM,UAAU;AAAA,EAChB,OAAO,OAAO,OAAO,IAAI,CAAC,UAAU,QAAQ,KAAK,CAAC;AAAA;AAMpD,IAAM,cAAc;AAGpB,SAAS,aAAa,CAAC,OAAyB;AAAA,EAC9C,IAAI,OAAO,UAAU,YAAY,UAAU;AAAA,IAAM,OAAO;AAAA,EACxD,MAAM,YAAY;AAAA,EAClB,OAAO,OAAO,UAAU,SAAS,YAAY,gBAAgB,KAAK,UAAU,IAAI,KAAK,OAAO,UAAU,aAAa;AAAA;AAAA;AAQ9G,MAAM,QAAQ;AAAA,EAEV;AAAA,EAMT,WAAqE;AAAA,EAEpD;AAAA,EACA;AAAA,EACA,WAA+B,CAAC;AAAA,EACzC,UAA0C,CAAC;AAAA,EAC3C,SAAyC,CAAC;AAAA,EAC1C,OAAkC,CAAC;AAAA,EAEnC,WAAoD;AAAA,EACpD,UAAU;AAAA,EACV,UAA2C,MAAG;AAAA,IAAG;AAAA;AAAA,EAEzD,WAAW,CAAC,QAAkE;AAAA,IAC5E,KAAK,QAAQ,OAAO;AAAA,IACpB,KAAK,QAAQ,OAAO;AAAA,IACpB,KAAK,OAAO,IAAI,QAAsB,CAAC,YAAY;AAAA,MACjD,KAAK,UAAU;AAAA,KAChB;AAAA;AAAA,EAIH,MAAM,CAAC,YAA2B;AAAA,IAChC,MAAM,SAAS;AAAA,IACf,OAAO,QAAQ,OAAO;AAAA,IACtB,IAAI;AAAA,MACF,WAAW,QAAQ,KAAK,OAAO;AAAA,QAC7B,OAAO,MAAM,EAAE,MAAM,KAAK,MAAM,OAAO,CAAC,EAAE,CAAC;AAAA,QAC3C,OAAO,KAAK,EAAE,QAAQ,KAAK,UAAU,CAAC,EAAE,CAAC;AAAA,QACzC,OAAO,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,CAAC;AAAA,QACvC,OAAO,QAAQ,EAAE,QAAQ,IAAI,MAAM,EAAE,CAAC;AAAA,MACxC;AAAA,MACA,OAAO,KAAK;AAAA,cACZ;AAAA,MACA,OAAO,QAAQ,SAAS;AAAA;AAAA;AAAA,EAI5B,oBAAoB,CAAC,SAA2D;AAAA,IAC9E,KAAK,SAAS,QAAQ;AAAA,IACtB,KAAK,UAAU,QAAQ,OAAO,IAAI,CAAC,UAAU,KAAK,MAAM,cAAc,MAAM,YAAY,MAAM,UAAU,MAAM,CAAC;AAAA,IAC/G,KAAK,OAAO,CAAC;AAAA;AAAA,EAGf,aAAa,CAAC,SAAuD;AAAA,IACnE,IAAI,KAAK,WAAW,KAAK,aAAa;AAAA,MAAW;AAAA,IAIjD,MAAM,MAA+B,CAAC;AAAA,IACtC,IAAI;AAAA,MACF,SAAS,QAAQ,EAAG,QAAQ,KAAK,OAAO,QAAQ,SAAS;AAAA,QACvD,MAAM,MAAM,QAAQ,OAAO,UAAU;AAAA,QACrC,IAAK,KAAK,OAAO,OAA+B,QAAQ,QAAQ,OAAO,OAAQ,KAAK,QAAQ,OAAsC,GAAG;AAAA,MACvI;AAAA,MACA,OAAO,OAAO;AAAA,MAQd,KAAK,WAAW,EAAE,MAAM;AAAA,MACxB;AAAA;AAAA,IAEF,KAAK,KAAK,KAAK,GAAG;AAAA;AAAA,EAGpB,qBAAqB,CAAC,SAAoD;AAAA,IACxE,IAAI,KAAK;AAAA,MAAS;AAAA,IAClB,IAAI,KAAK,aAAa,WAAW;AAAA,MAE/B,KAAK,SAAS,KAAK,EAAE,QAAQ,UAAU,OAAO,KAAK,SAAS,MAAM,CAAC;AAAA,MACnE,KAAK,WAAW;AAAA,MAChB,KAAK,OAAO,CAAC;AAAA,MACb,KAAK,SAAS,CAAC;AAAA,MACf,KAAK,UAAU,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,IACA,MAAM,QAAQ,YAAY,KAAK,QAAQ,QAAQ,QAAQ,WAAW,EAAE;AAAA,IACpE,MAAM,UAAU,QAAQ;AAAA,IACxB,MAAM,WAAW,QAAQ,OAAO,YAC5B,OAAO,SAAS,MAAM,IAAI,EAAE,IAC5B,QAAQ,OAAO,YAAY,OAAO,SAAS,MAAM,IAAI,EAAE,IAAI;AAAA,IAC/D,KAAK,SAAS,KAAK,EAAE,QAAQ,MAAM,QAAQ,EAAE,MAAM,KAAK,MAAM,aAAc,YAAY,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAG,EAAE,CAAC;AAAA,IACzH,KAAK,OAAO,CAAC;AAAA,IACb,KAAK,SAAS,CAAC;AAAA,IACf,KAAK,UAAU,CAAC;AAAA;AAAA,EAGlB,gBAAgB,GAAS;AAAA,IACvB,IAAI,KAAK;AAAA,MAAS;AAAA,IAClB,KAAK,SAAS,KAAK,EAAE,QAAQ,MAAM,QAAQ,EAAE,MAAM,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC;AAAA;AAAA,EAG3E,WAAW,CAAC,OAAsB;AAAA,IAChC,IAAI,KAAK;AAAA,MAAS;AAAA,IAClB,IAAI,CAAC,cAAc,KAAK,GAAG;AAAA,MAGzB,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAAA,MACrC;AAAA,IACF;AAAA,IAQA,KAAK,SAAS,KAAK,EAAE,QAAQ,UAAU,OAAO,KAAK,aAAa,YAAY,QAAQ,KAAK,SAAS,MAAM,CAAC;AAAA,IACzG,OAAO,KAAK,SAAS,SAAS,KAAK,MAAM;AAAA,MAAQ,KAAK,SAAS,KAAK,EAAE,QAAQ,UAAU,CAAC;AAAA,IACzF,KAAK,OAAO,CAAC,CAAC;AAAA;AAAA,EAGhB,mBAAmB,GAAS;AAAA,IAC1B,KAAK,OAAO,CAAC,CAAC;AAAA;AAAA,EAGhB,qBAAqB,GAAS;AAAA,EAI9B,oBAAoB,CAAC,YAA2B;AAAA,IAC7C,WAA+B,eAAe,iDAAiD;AAAA;AAAA,EAGlG,cAAc,GAAS;AAAA,EAIf,MAAM,CAAC,QAA4C;AAAA,IACzD,IAAI,KAAK;AAAA,MAAS;AAAA,IAClB,KAAK,UAAU;AAAA,IACf,KAAK,WAAW,OAAO,kBAAkB,IAAI;AAAA,IAC7C,KAAK,QAAQ;AAAA,MACX,OAAO,KAAK;AAAA,SACR,OAAO,mBAAmB,YAAY,CAAC,IAAI,EAAE,gBAAgB,OAAO,eAAe;AAAA,IACzF,CAAC;AAAA;AAEL;AAGA,eAAsB,QAAQ,CAAC,QAKL;AAAA,EACxB,MAAM,QAAQ,IAAI,QAAQ,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM,CAAC;AAAA,EACrE,OAAO,OAAO,MAA4C,KAAK,OAAO,QAAQ,KAAK;AAAA,EACpF,OAAO,MAAM;AAAA;AAOR,SAAS,iBAAiB,CAAC,QAAoG;AAAA,EACpI,QAAQ,YAAY;AAAA,EACpB,MAAM,QAAQ,QAAQ,MAAM,UAAU,CAAC,SAAS,KAAK,WAAW,QAAQ;AAAA,EACxE,IAAI,UAAU;AAAA,IAAI,OAAO,EAAE,OAAO,OAAQ,QAAQ,MAAM,OAA8B,MAAM;AAAA,EAC5F,IAAI,QAAQ,mBAAmB;AAAA,IAAW,OAAO,EAAE,OAAO,QAAQ,MAAM,QAAQ,OAAO,QAAQ,eAAe;AAAA,EAC9G;AAAA;;;ACzTF;AACA,sBAAS;AAET;;;ACFO,IAAM,uBAA+D,OAAO,OAAO;AAAA,EACxF,kBAAkB;AACpB,CAAC;;;ACMD;AAQO,IAAM,0BAAmE,OAAO,OAAO;AAAA,EAE5F,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAMT,SAAS;AAAA,EAST,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EAMT,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAET,SAAS;AAAA,EAST,SAAS;AAAA,EAMT,SAAS;AACX,CAAC;AAGM,IAAM,sBAAwD,OAAO,OAAO;AAAA,EACjF,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EAET,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACX,CAAC;AAWD,IAAM,mBAAmB;AAEzB,SAAS,eAAe,CAAC,QAA8E;AAAA,EACrG,MAAM,QAAQ,OAAO,OAAO,OAAO;AAAA,EACnC,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AAAA;AAQ1D,SAAS,eAAe,CAAC,QAAgD;AAAA,EAC9E,QAAQ,UAAU;AAAA,EAClB,IAAI,UAAU,QAAQ,OAAO,UAAU;AAAA,IAAU;AAAA,EACjD,MAAM,OAAO,gBAAgB,EAAE,QAAQ,OAAkC,KAAK,OAAO,CAAC;AAAA,EACtF,OAAO,SAAS,aAAa,iBAAiB,KAAK,IAAI,IAAI,OAAO;AAAA;AAW7D,SAAS,gBAAgB,CAAC,QAAuC;AAAA,EACtE,QAAQ,UAAU;AAAA,EAClB,IAAI,iBAAiB;AAAA,IAAW,OAAO;AAAA,EAEvC,MAAM,SACJ,UAAU,QAAQ,OAAO,UAAU,WAAY,QAAoC,CAAC;AAAA,EACtF,MAAM,WAAW,gBAAgB,EAAE,MAAM,CAAC;AAAA,EAC1C,MAAM,QACH,aAAa,YAAY,YAAY,wBAAwB,cAAc;AAAA,EAE9E,MAAM,OAAgC,CAAC;AAAA,EACvC,IAAI,aAAa;AAAA,IAAW,KAAK,WAAW;AAAA,EAC5C,MAAM,aAAa,gBAAgB,EAAE,QAAQ,KAAK,aAAa,CAAC;AAAA,EAChE,IAAI,eAAe;AAAA,IAAW,KAAK,aAAa;AAAA,EAChD,MAAM,QAAQ,gBAAgB,EAAE,QAAQ,KAAK,QAAQ,CAAC;AAAA,EACtD,IAAI,UAAU;AAAA,IAAW,KAAK,QAAQ;AAAA,EACtC,MAAM,SAAS,gBAAgB,EAAE,QAAQ,KAAK,SAAS,CAAC;AAAA,EACxD,IAAI,WAAW;AAAA,IAAW,KAAK,SAAS;AAAA,EACxC,MAAM,SAAS,aAAa,YAAY,YAAY,oBAAoB;AAAA,EACxE,IAAI,WAAW;AAAA,IAAW,KAAK,SAAS;AAAA,EAExC,MAAM,UACJ,gBAAgB,EAAE,QAAQ,KAAK,UAAU,CAAC,MACzC,OAAO,UAAU,WAAW,QAAQ;AAAA,EAEvC,OAAO,IAAI,UAAU,EAAE,MAAM,SAAS,MAAM,OAAO,MAAM,CAAC;AAAA;AAI5D,eAAsB,SAAY,CAAC,IAAkC;AAAA,EACnE,IAAI;AAAA,IACF,OAAO,MAAM,GAAG;AAAA,IAChB,OAAO,OAAO;AAAA,IACd,MAAM,iBAAiB,EAAE,MAAM,CAAC;AAAA;AAAA;;;AFlIpC,SAAS,MAAM,CAAC,QAAuD;AAAA,EACrE,QAAQ,MAAM,QAAQ,MAAM,IAAI,OAAO,GAAG,EAAE,GAAG,OAAO,OAAO,SAAS,CAAC;AAAA;AASlE,SAAS,wBAAwB,CAAC,QAQlB;AAAA,EACrB,MAAM,UAAU,OAAO,qBAAqB,YAAY,oBAAoB;AAAA,EAC5E,OAAO;AAAA,IACL;AAAA,SACM,IAAG,CAAC,OAAO;AAAA,MACf,oBAAoB,EAAE,WAAW,MAAM,WAAW,QAAQ,QAAQ,SAAS,UAAU,cAAc,OAAO,yBAAyB,CAAC;AAAA,MACpI,IAAI,MAAM,WAAW,iBAAiB;AAAA,QAAW,OAAO,MAAM,KAAK;AAAA,MACnE,IAAI,OAAO,OAAO,aAAa,SAAS,OAAO,OAAO,sBAAsB,iBAAiB,KAAK,GAAG;AAAA,QACnG,MAAM,IAAI,WAAU,EAAE,MAAM,+BAA+B,SAAS,oFAAoF,MAAM,EAAE,YAAY,oBAAoB,QAAQ,kBAAkB,EAAE,CAAC;AAAA,MAC/N;AAAA,MACA,MAAM,UAAU,OAAO;AAAA,MACvB,IAAI,YAAY;AAAA,QAAW,MAAM,IAAI,WAAU,EAAE,MAAM,iBAAiB,SAAS,kDAAkD,CAAC;AAAA,MACpI,MAAM,QAAQ,eAAe,OAAO,WAAW;AAAA,MAG/C,IAAI,CAAC,IAAI,SAAS,OAAO;AAAA,QAAG,MAAM,IAAI,WAAU,EAAE,MAAM,eAAe,SAAS,4DAA4D,CAAC;AAAA,MAC7I,MAAM,MAAM,IAAI,IAAI,OAAO;AAAA,MAC3B,IAAI,aAAa,OAAO,eAAe;AAAA,MACvC,IAAI,aAAa,IAAI,mBAAmB,OAAO,qBAAqB,mBAAmB,IAAK,CAAC;AAAA,MAC7F,IAAI,aAAa,IAAI,qBAAqB,OAAO,qBAAqB,gBAAgB,CAAC;AAAA,MACvF,IAAI,aAAa,IAAI,WAAW,wBAAwB,qBAAqB,oCAAoC,qBAAqB,kBAAkB;AAAA,MACxJ,MAAM,UAAU,MAAM,UAAU,YAAY,OAAO,gBAAgB,EAAE,kBAAkB,IAAI,SAAS,EAAE,CAAC,KACrG,IAAI,IAAG,OAAO,EAAE,kBAAkB,IAAI,SAAS,GAAG,yBAAyB,qBAAqB,kBAAkB,eAAe,EAAE,CAAC,CAA6B;AAAA,MAEnK,QAAQ,GAAG,SAAS,MAAG;AAAA,QAAG;AAAA,OAAS;AAAA,MACnC,MAAM,SAAS,MAAM,UAAU;AAAA,MAC/B,IAAI;AAAA,MACJ,IAAI,YAAY;AAAA,MAChB,IAAI,aAAa;AAAA,MACjB,IAAI,WAAW;AAAA,MACf,IAAI;AAAA,MACJ,IAAI,aAAa;AAAA,MACjB,IAAI;AAAA,MACJ,IAAI,mBAAmB;AAAA,MACvB,MAAM,iBAAiB,MAAY;AAAA,QACjC,mBAAmB;AAAA,QACnB,OAAO,QAAQ;AAAA,QACf,IAAI,CAAC,YAAY;AAAA,UAAE,aAAa;AAAA,UAAM,OAAO,UAAU;AAAA,QAAG;AAAA;AAAA,MAE5D,IAAI;AAAA,MACJ,MAAM,UAAU,CAAC,OAA+B,SAC9C,UAAU,IAAI,EAAE,KACd,CAAC,WAAW;AAAA,QAAE,MAAM,gBAAgB,EAAE,OAAO,MAAM,OAAO,MAAM,EAAE,OAAO,CAAC;AAAA,QAAG,OAAO;AAAA,SACpF,CAAC,UAAmB;AAAA,QAAE,MAAM,gBAAgB,EAAE,OAAO,MAAM,EAAE,CAAC;AAAA,QAAG,MAAM;AAAA,OACzE;AAAA,MACF,IAAI;AAAA,MACJ,MAAM,YAAY,UAAU,MAAM,QAAQ,QAAQ,CAAC,EAAE,KAAK,YAAY;AAAA,QACpE,oBAAoB,EAAE,WAAW,MAAM,WAAW,QAAQ,QAAQ,SAAS,UAAU,cAAc,OAAO,yBAAyB,CAAC;AAAA,QAGpI,QAAQ,iBAAiB,SAAS,gBAAgB,EAAE,MAAM,KAAK,CAAC;AAAA,QAChE,MAAM,UAAU,OAAO,MAAM,QAAQ,aAAa,MAAM,OAAO,OAAO,MAAM;AAAA,UAC1E,MAAM;AAAA,UACN,QAAQ,CAAC,KAAK;AAAA,QAChB,CAAC,CAAC,CAAC,EAAE;AAAA,QACL,QAAQ,oBAAoB,SAAS,cAAc;AAAA,QACnD,IAAI;AAAA,UAAkB,MAAM,mBAAmB,EAAE,OAAO,yBAAyB,CAAC;AAAA,QAClF,IAAI,YAAY,aAAa,OAAO,QAAQ,QAAQ,YAAY,OAAO,QAAQ,kBAAkB,YAAY,OAAO,QAAQ,YAAY,YAAY,OAAO,QAAQ,YAAY,YAAY,QAAQ,QAAQ,OAAO;AAAA,UAChN,MAAM,IAAI,WAAU,EAAE,MAAM,+BAA+B,SAAS,yDAAyD,MAAM,EAAE,YAAY,oBAAoB,QAAQ,iBAAiB,EAAE,CAAC;AAAA,QACnM;AAAA,QACA,oBAAoB,EAAE,WAAW,MAAM,WAAW,QAAQ,QAAQ,SAAS,UAAU,cAAc,OAAO,yBAAyB,CAAC;AAAA,QACpI,WAAW,MAAY;AAAA,UACrB,IAAI,YAAY;AAAA,YAAW;AAAA,UAC3B,YAAY;AAAA,UACZ,OAAO,QAAQ;AAAA,UACf,MAAM,cAAc,CAAC,UAA0B;AAAA,YAC7C,iBAAiB;AAAA,YACjB,IAAI,CAAC,YAAY;AAAA,cAAE,aAAa;AAAA,cAAM,OAAO,UAAU;AAAA,YAAG;AAAA,YAC1D,MAAM;AAAA;AAAA,UAER,kBAAkB,WAAW,MAAM;AAAA,YACjC,iBAAiB,IAAI,WAAU,EAAE,MAAM,gBAAgB,SAAS,sFAAsF,MAAM,EAAE,QAAQ,+BAA+B,mBAAmB,MAAM,EAAE,CAAC;AAAA,YACjO,IAAI,CAAC,YAAY;AAAA,cAAE,aAAa;AAAA,cAAM,OAAO,UAAU;AAAA,YAAG;AAAA,aACzD,qBAAqB,gBAAgB;AAAA,UACxC,SAAS,QAAQ,UAAU,MAAM,QAAQ,MAAM;AAAA,YAC7C,MAAM;AAAA,YACN,QAAQ,CAAC,QAAQ,KAAK,QAAQ,eAAe,QAAQ,SAAS,QAAQ,SAAS,KAAK;AAAA,UACtF,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW;AAAA,YACnB,aAAa,OAAO,MAAM,EAAE,IAAI,cAAc;AAAA,YAC9C,IAAI,CAAC;AAAA,cAAY,YAAY,IAAI,WAAU,EAAE,MAAM,+BAA+B,SAAS,mEAAmE,MAAM,EAAE,YAAY,oBAAoB,QAAQ,wBAAwB,mBAAmB,MAAM,EAAE,CAAC,CAAC;AAAA,aAClQ,WAAW;AAAA,UAGT,OAAO,KAAK,MAAG;AAAA,YAAG;AAAA,aAAW,MAAG;AAAA,YAAG;AAAA,WAAS;AAAA;AAAA,QAInD,WAAW,UAAU,MAAM,IAAI;AAAA,QAC/B,QAAQ,iBAAiB,SAAS,UAAU,EAAE,MAAM,KAAK,CAAC;AAAA,QAC1D,IAAI,QAAQ,YAAY;AAAA,UAAM,SAAS;AAAA,QACvC,OAAO,SAAS,KACd,OAAO,WAAW;AAAA,UAChB,WAAW;AAAA,UACX,QAAQ,oBAAoB,SAAS,QAAS;AAAA,UAC9C,IAAI,WAAW;AAAA,YAAW,MAAM;AAAA,UAChC,IAAI,mBAAmB;AAAA,YAAW,MAAM;AAAA,UACxC,IAAI,QAAQ,YAAY;AAAA,YAAM,MAAM,mBAAmB,EAAE,OAAO,UAAU,CAAC;AAAA,UAC3E,OAAO;AAAA,WAET,OAAO,UAAmB;AAAA,UACxB,WAAW;AAAA,UACX,QAAQ,oBAAoB,SAAS,QAAS;AAAA,UAC9C,IAAI,WAAW;AAAA,YAAW,MAAM,OAAO,KAAK,MAAG;AAAA,cAAG;AAAA,eAAW,MAAG;AAAA,cAAG;AAAA,aAAS;AAAA,UAG5E,MAAM,WAAW,iBAAiB,aAAY,MAAM,KAAK,WAAW,gBAAgB,EAAE,MAAM,CAAC;AAAA,UAC7F,IAAI,cAAc,mBAAmB,aAAa,aAAa;AAAA,YAAW,MAAM;AAAA,UAChF,IAAI,aAAa,cAAc,aAAa,SAAS;AAAA,YACnD,MAAM,YAAY,mBAAmB,EAAE,OAAO,WAAW,mBAAmB,MAAM,OAAO,MAAM,CAAC;AAAA,YAChG,MAAM,IAAI,WAAU,EAAE,MAAM,UAAU,MAAM,SAAS,UAAU,SAAS,MAAM,KAAK,UAAU,MAAM,UAAU,QAAQ,GAAG,OAAO,MAAM,CAAC;AAAA,UACxI;AAAA,UACA,MAAM;AAAA,SAEV;AAAA,OACD;AAAA,MACD,OAAO,UAAU,KACf,OAAO,WAAW;AAAA,QAAE,QAAQ,oBAAoB,SAAS,cAAc;AAAA,QAAG,IAAI,oBAAoB;AAAA,UAAW,aAAa,eAAe;AAAA,QAAG,IAAI,aAAa;AAAA,UAAW,QAAQ,oBAAoB,SAAS,QAAQ;AAAA,QAAG,MAAM,UAAU,MAAM,QAAQ,IAAI,CAAC;AAAA,QAAG,OAAO;AAAA,SACrQ,OAAO,UAAmB;AAAA,QACxB,QAAQ,oBAAoB,SAAS,cAAc;AAAA,QACnD,IAAI,oBAAoB;AAAA,UAAW,aAAa,eAAe;AAAA,QAC/D,IAAI,aAAa;AAAA,UAAW,QAAQ,oBAAoB,SAAS,QAAQ;AAAA,QACzE,MAAM,UAAU,MAAM,QAAQ,IAAI,CAAC,EAAE,KAAK,MAAG;AAAA,UAAG;AAAA,WAAW,MAAG;AAAA,UAAG;AAAA,SAAS;AAAA,QAC1E,IAAI;AAAA,UAAkB,MAAM,mBAAmB,EAAE,OAAO,0BAA0B,OAAO,MAAM,CAAC;AAAA,QAChG,MAAM;AAAA,OAEV;AAAA;AAAA,EAEJ;AAAA;;;AGzJK,MAAM,aAAa;AAAA,EAEf;AAAA,EAET,WAAW,CAAC,QAAmB;AAAA,IAC7B,KAAK,SAAS;AAAA;AAElB;AAiBO,SAAS,aAAa,CAAC,QAAkC;AAAA,EAC9D,QAAQ,UAAU;AAAA,EAClB,MAAM,OAAO,MAAM,QAAQ;AAAA,EAC3B,IAAI,OAAO,MAAM,IAAI;AAAA,IAAG,OAAO;AAAA,EAC/B,MAAM,OAAO,MAAM,eAAe;AAAA,EAClC,IAAI,OAAO,KAAK,OAAO;AAAA,IAAM,OAAO;AAAA,EACpC,OAAO,MAAM,YAAY;AAAA;AAI3B,SAAS,mBAAmB,CAAC,OAAyB;AAAA,EACpD,OACE,UAAU,QACV,UAAU,aACV,OAAO,UAAU,YACjB,YAAY,OAAO,KAAK,KACxB,iBAAiB;AAAA;AASd,SAAS,eAAe,CAAC,QAAmD;AAAA,EACjF,OAAO,OAAO,OAAO,IAAI,CAAC,UAAU;AAAA,IAClC,IAAI,iBAAiB;AAAA,MAAc,OAAO,cAAc,EAAE,QAAQ,MAAM,OAAO,CAAC;AAAA,IAChF,IAAI,iBAAiB;AAAA,MAAM,OAAO,cAAc,EAAE,MAAM,CAAC;AAAA,IACzD,IAAI,oBAAoB,KAAK;AAAA,MAAG,OAAO;AAAA,IACvC,OAAO,KAAK,UAAU,KAAK;AAAA,GAC5B;AAAA;AAQH,SAAS,aAAa,CAAC,QAA0C;AAAA,EAC/D,QAAQ,WAAW;AAAA,EACnB,OAAO,OAAO,KAAK,CAAC,UAAU,iBAAiB,IAAI,IAC/C,OAAO,IAAI,CAAC,UAAW,iBAAiB,OAAO,cAAc,EAAE,MAAM,CAAC,IAAI,KAAM,IAChF;AAAA;AAWC,SAAS,kBAAkB,CAAC,QAAmD;AAAA,EACpF,OAAO,OAAO,OAAO,IAAI,CAAC,UAAU;AAAA,IAClC,IAAI,iBAAiB;AAAA,MAAc,OAAO,cAAc,EAAE,QAAQ,MAAM,OAAO,CAAC;AAAA,IAChF,IAAI,MAAM,QAAQ,KAAK;AAAA,MAAG,OAAO,cAAc,EAAE,QAAQ,MAAM,CAAC;AAAA,IAChE,IAAI,iBAAiB;AAAA,MAAM,OAAO,cAAc,EAAE,MAAM,CAAC;AAAA,IACzD,IAAI,oBAAoB,KAAK;AAAA,MAAG,OAAO;AAAA,IACvC,OAAO,KAAK,UAAU,KAAK;AAAA,GAC5B;AAAA;;;ACnFH,sBAAS;AAcT,IAAM,mBAA6C,CAAC,QAAQ,qBAAqB,oBAAoB;AAErG,SAAS,eAAe,CAAC,OAAwC;AAAA,EAC/D,OAAO,OAAO,UAAU,YAAa,iBAAuC,SAAS,KAAK;AAAA;AAIrF,SAAS,mBAAmB,CAAC,QAA2C;AAAA,EAC7E,QAAQ,UAAU;AAAA,EAClB,IAAI,gBAAgB,KAAK;AAAA,IAAG,OAAO;AAAA,EACnC,MAAM,IAAI,WAAU;AAAA,IAClB,MAAM;AAAA,IACN,SAAS,gCAAgC,iBAAiB,IAAI,CAAC,SAAS,IAAI,OAAO,EAAE,KAAK,IAAI,eAAe,KAAK,UAAU,KAAK;AAAA,IACjI,MAAM,EAAE,eAAe,OAAO,SAAS,iBAAiB;AAAA,EAC1D,CAAC;AAAA;AAcI,SAAS,kBAAkB,CAAC,QAGP;AAAA,EAC1B,QAAQ,kBAAkB,kBAAkB;AAAA,EAC5C,IAAI,kBAAkB;AAAA,IAAQ,OAAO,EAAE,kBAAkB,SAAS,UAAU;AAAA,EAC5E,MAAM,UAAU,sBAAsB;AAAA,EAEtC,IAAI,qBAAqB,aAAa,CAAC,IAAI,SAAS,gBAAgB,GAAG;AAAA,IACrE,OAAO,EAAE,kBAAkB,SAAS,QAAQ;AAAA,EAC9C;AAAA,EACA,MAAM,MAAM,IAAI,IAAI,gBAAgB;AAAA,EACpC,MAAM,WAAW,IAAI,aAAa,IAAI,SAAS;AAAA,EAC/C,IAAI,aAAa,MAAM;AAAA,IAGrB,OAAO,EAAE,kBAAkB,SAAS,QAAQ;AAAA,EAC9C;AAAA,EACA,IAAI,8BAA8B,KAAK,QAAQ,GAAG;AAAA,IAEhD,OAAO,EAAE,kBAAkB,SAAS,UAAU;AAAA,EAChD;AAAA,EACA,MAAM,SAAS,SAAS,KAAK,EAAE,WAAW,IAAI,UAAU,GAAG,SAAS,KAAK,KAAK;AAAA,EAE9E,IAAI,aAAa,IAAI,WAAW,MAAM;AAAA,EACtC,OAAO,EAAE,kBAAkB,IAAI,SAAS,GAAG,SAAS,OAAO;AAAA;AAItD,SAAS,eAAe,CAAC,QAMJ;AAAA,EAC1B,QAAQ,KAAK,kBAAkB,sBAAsB;AAAA,EACrD,MAAM,YAAY,mBAAmB;AAAA,IACnC,kBAAkB,OAAO;AAAA,IACzB,eAAe,OAAO,iBAAiB;AAAA,EACzC,CAAC;AAAA,EACD,MAAM,SAAkC,EAAE,kBAAkB,UAAU,kBAAkB,IAAI;AAAA,EAC5F,IAAI,qBAAqB;AAAA,IAAW,OAAO,oBAAoB;AAAA,EAC/D,IAAI,sBAAsB;AAAA,IAAW,OAAO,0BAA0B;AAAA,EACtE,IAAI,UAAU,YAAY;AAAA,IAAW,OAAO,UAAU,UAAU;AAAA,EAChE,OAAO;AAAA;;;AClET,IAAM,wBAAwB;AAQ9B,IAAM,qBAAqB;AAuBpB,SAAS,wBAAwB,CAAC,QAAoE;AAAA,EAC3G,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,GAAG;AAAA,EAClC,MAAM,eAAe,KAAK,IAAI,GAAG,OAAO,gBAAgB,qBAAqB;AAAA,EAC7E,MAAM,eAAe,MAAM;AAAA,EAE3B,MAAM,QAAQ,IAAI;AAAA,EAElB,MAAM,YAAY,IAAI;AAAA,EACtB,IAAI,UAAU;AAAA,EAEd,OAAO;AAAA,IACL,OAAO,CAAC,YAAkD;AAAA,MACxD,QAAQ,SAAS;AAAA,MACjB,MAAM,WAAW,MAAM,IAAI,IAAI;AAAA,MAC/B,IAAI,aAAa;AAAA,QAAW,OAAO;AAAA,MAGnC,IAAI,MAAM,QAAQ;AAAA,QAAK;AAAA,MAEvB,MAAM,QAAQ,UAAU,IAAI,IAAI,KAAK,KAAK;AAAA,MAC1C,IAAI,OAAO,cAAc;AAAA,QAEvB,UAAU,OAAO,IAAI;AAAA,QACrB,UAAU,IAAI,MAAM,IAAI;AAAA,QACxB,OAAO,UAAU,OAAO,cAAc;AAAA,UACpC,MAAM,SAAS,UAAU,KAAK,EAAE,KAAK;AAAA,UACrC,IAAI,OAAO,SAAS;AAAA,YAAM;AAAA,UAC1B,UAAU,OAAO,OAAO,KAAK;AAAA,QAC/B;AAAA,QACA;AAAA,MACF;AAAA,MAEA,UAAU,OAAO,IAAI;AAAA,MACrB,MAAM,OAAO,WAAW;AAAA,MACxB,MAAM,IAAI,MAAM,IAAI;AAAA,MACpB,OAAO;AAAA;AAAA,IAGT,IAAI,GAAW;AAAA,MACb,OAAO,MAAM;AAAA;AAAA,IAGf,SAAS,GAAW;AAAA,MAClB,OAAO,UAAU;AAAA;AAAA,EAErB;AAAA;;;AC7GF,sBAAS;AAiGT;AA9FO,IAAM,sBAAgE,OAAO,OAAO;AAAA,EACzF,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,cAAc;AAChB,CAAC;AAMM,SAAS,cAAc,CAAC,QAAyE;AAAA,EACtG,QAAQ,gBAAgB,aAAa;AAAA,EACrC,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,mBAAmB;AAAA,IAAW,MAAM,KAAK,mBAAmB,oBAAoB,iBAAiB;AAAA,EACrG,IAAI,aAAa;AAAA,IAAM,MAAM,KAAK,WAAW;AAAA,EAC7C,OAAO,MAAM,WAAW,IAAI,UAAU,SAAS,MAAM,KAAK,IAAI;AAAA;AAezD,SAAS,wBAAwB,CAAC,QAI9B;AAAA,EACT,MAAM,QAAQ,eAAe,MAAM;AAAA,EACnC,OAAO,OAAO,YAAY,YAAY,QAAQ,GAAG,UAAU,0BAA0B,EAAE,SAAS,OAAO,QAAQ,CAAC;AAAA;AAW3G,SAAS,yBAAyB,CAAC,QAAqC;AAAA,EAC7E,QAAQ,YAAY;AAAA,EACpB,IAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAAG;AAAA,IAC7C,MAAM,IAAI,WAAU;AAAA,MAClB,MAAM;AAAA,MACN,SAAS,gFAAgF,OAAO,OAAO;AAAA,MACvG,MAAM,EAAE,QAAQ;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EACA,OAAO,iCAAiC;AAAA;AAQ1C,IAAM,mBAA2B;AACjC,IAAM,oBAA4B;AAmB3B,SAAS,6BAA6B,CAAC,QAAiD;AAAA,EAC7F,IAAI,iBAAiB,KAAK,OAAO,IAAI;AAAA,IAAG,OAAO;AAAA,EAC/C,IAAI,kBAAkB,KAAK,OAAO,IAAI;AAAA,IAAG,OAAO;AAAA,EAChD,OAAO;AAAA;;;ARwrCT;AAeA;AAAA;AAAA,6BAEE;AAAA;AAAA,4BAEA;AAAA;AAAA;AAAA;AAAA,iCAIA;AAAA;AAppCF,IAAM,2BAA2B;AAEjC,IAAM,SAAS,MAAc;AAAA,EAAG;AAAA;AAEhC,SAAS,WAAW,CAAC,QAA2D;AAAA,EAC9E,OAAO,MAAM,QAAQ,OAAO,MAAM,IAC7B,OAAO,OAAO,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,UAAU,EAAE,IACjD,OAAO;AAAA;AA2Bb,IAAM,uBAAgE,IAAI;AAS1E,SAAS,gBAAgB,CAAC,QAAqC;AAAA,EAC7D,QAAQ,UAAU;AAAA,EAClB,IAAI,EAAE,iBAAiB;AAAA,IAAY,OAAO;AAAA,EAC1C,MAAM,WAAW,MAAM,KAAK;AAAA,EAC5B,OAAO,OAAO,aAAa,YAAY,SAAS,WAAW,IAAI,KAAK,CAAC,SAAS,SAAS,OAAO,EAAE,SAAS,QAAQ;AAAA;AAQnH,SAAS,yBAAyB,CAAC,QAAuC;AAAA,EACxE,OAAO,IAAI,WAAU;AAAA,IACnB,MAAM;AAAA,IACN,SACE;AAAA,IACF,MAAM,EAAE,UAAU,UAAU,QAAQ,+BAA+B,SAAS,WAAW,OAAO,SAAS;AAAA,IACvG,OAAO,OAAO;AAAA,EAChB,CAAC;AAAA;AAQH,IAAM,+BAAoD,IAAI,IAAI,CAAC,MAAM,MAAM,MAAM,MAAM,IAAI,CAAC;AAchG,IAAM,4BAAiD,IAAI,IAAI,CAAC,SAAS,SAAS,OAAO,CAAC;AA6C1F,SAAS,oBAAoB,CAAC,QAAyD;AAAA,EACrF,MAAM,SAAS,OAAO;AAAA,EACtB,IAAI,oBAAoB;AAAA,EACxB,IAAI,SAAqD,MAAG;AAAA,IAAG;AAAA;AAAA,EAC/D,MAAM,aAAa,IAAI,QAAmC,CAAC,YAAY;AAAA,IAAE,SAAS;AAAA,GAAU;AAAA,EAC5F,MAAM,UAAU,MAAY,OAAO,OAAO;AAAA,EAC1C,MAAM,UAAU,MAAY;AAAA,IAAE,oBAAoB;AAAA,IAAM,OAAO,OAAO;AAAA;AAAA,EACtE,MAAM,QAAQ,MAAY,OAAO,KAAK;AAAA,EACtC,OAAO,GAAG,SAAS,OAAO;AAAA,EAC1B,OAAO,GAAG,SAAS,OAAO;AAAA,EAC1B,OAAO,GAAG,OAAO,KAAK;AAAA,EACtB,OAAO;AAAA,IACL,eAAe,SAAS,OAAO,YAAY;AAAA,MACzC,IAAI,EAAE,iBAAiB;AAAA,QAAY,OAAO;AAAA,MAC1C,MAAM,WAAW,MAAM,KAAK;AAAA,MAC5B,IAAI,OAAO,aAAa,YAAY,6BAA6B,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;AAAA,QAAG,OAAO;AAAA,MACnG,IAAI,SAAS,0BAA0B,IAAI,QAAQ;AAAA,QAAG,OAAO;AAAA,MAC7D,MAAM,QAAQ,MAAM;AAAA,MACpB,OAAO,UAAU,WAAW,CAAC,qBAAqB,OAAO,qBAAqB,MAAM;AAAA;AAAA,IAEtF,QAAQ,MAAM;AAAA,MACZ,OAAO,eAAe,SAAS,OAAO;AAAA,MACtC,OAAO,eAAe,SAAS,OAAO;AAAA,MACtC,OAAO,eAAe,OAAO,KAAK;AAAA;AAAA,EAEtC;AAAA;AAWF,SAAS,oBAAoB,CAAC,QAA+C;AAAA,EAC3E,MAAM,SAAS,OAAO;AAAA,EACtB,OAAO,OAAO,OAAO,OAAO,cAC1B,OAAO,OAAO,mBAAmB,cACjC,OAAO,OAAO,SAAS,cACvB,OAAO,OAAO,yBAAyB,cACvC,OAAO,aAAa;AAAA;AAGxB,SAAS,qBAAqB,CAAC,QAAsF;AAAA,EACnH,QAAQ,QAAQ,YAAY;AAAA,EAC5B,IAAI,OAAO,OAAO,OAAO;AAAA,IAAY,OAAO,MAAS;AAAA,MAAG;AAAA;AAAA,EACxD,MAAM,UAAU,CAAC,UAAyB;AAAA,IAExC,UAAU,KAAK;AAAA;AAAA,EAEjB,OAAO,GAAG,SAAS,OAAO;AAAA,EAC1B,OAAO,MAAY;AAAA,IACjB,OAAO,iBAAiB,SAAS,OAAO;AAAA;AAAA;AAkG5C,IAAM,UAAmB;AACzB,IAAM,WAAqB;AAO3B,IAAM,OAAqB,OAAO,OAAO,EAAE,UAAU,UAAU,MAAM,UAAU,SAAS,SAAS,CAAU;AAO3G,IAAM,cAA0D;AAgBhE,IAAM,UAAgC,OAAO,OAAO;AAAA,EAClD,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,aAAa,OAAO,OAAO,CAAC,UAAU,CAAU;AAClD,CAAU;AAKV,IAAM,oBAAoB;AAE1B,IAAM,iBAAiB;AAEvB,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAC5B,IAAM,WAAW;AACjB,IAAM,iBAAiB;AAOvB,IAAM,mBACJ,IAAG,MAAM;AAGX,IAAM,iBAAiB;AAYvB,SAAS,kBAAkB,CAAC,OAAwB;AAAA,EAClD,IAAI,eAAe,KAAK,KAAK,GAAG;AAAA,IAC9B,MAAM,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,QAAQ,KAAK,GAAG,EAAE,QAAQ,iBAAiB,IAAI,OAAO;AAAA,IACjG,MAAM,SAAS,IAAI,KAAK,GAAG;AAAA,IAC3B,IAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,CAAC;AAAA,MAAG,OAAO;AAAA,EAC9C;AAAA,EACA,OAAO,iBAAiB,eAAe,MAAM,EAAE,KAAK;AAAA;AAItD,SAAS,uBAAuB,CAAC,OAAwB;AAAA,EACvD,MAAM,cAAc,CAAC,YAA8B;AAAA,IACjD,IAAI,YAAY;AAAA,MAAM,OAAO;AAAA,IAC7B,IAAI,MAAM,QAAQ,OAAO;AAAA,MAAG,OAAO,QAAQ,IAAI,WAAW;AAAA,IAC1D,OAAO,mBAAmB,OAAO,OAAO,CAAC;AAAA;AAAA,EAE3C,OAAO,YAAY,iBAAiB,gBAAgB,MAAM,EAAE,KAAK,CAAC;AAAA;AAsBpE,IAAM,cAA6B;AAAA,EACjC,aAAa,CAAC,KAAa,QAAwD;AAAA,IACjF,IAAI,WAAW,aAAa,WAAW,QAAQ;AAAA,MAC7C,IAAI,QAAQ;AAAA,QAAmB,OAAO,iBAAiB,gBAAgB,MAAM;AAAA,MAC7E,IAAI,QAAQ,iBAAiB,QAAQ;AAAA,QAAU,OAAO;AAAA,MACtD,IAAI,QAAQ,uBAAuB,QAAQ;AAAA,QAAgB,OAAO;AAAA,IACpE;AAAA,IACA,OAAO,iBAAiB,KAAK,MAAM;AAAA;AAEvC;AAUA,SAAS,WAAW,CAAC,QAAwG;AAAA,EAC3H,MAAM,UAAyC,OAAO;AAAA,EACtD,IAAI,SAAS,SAAS,aAAa,QAAQ,SAAS;AAAA,IAAM,OAAO,EAAE,MAAM,QAAQ,KAAK;AAAA,EACtF,MAAM,mBAAmB,SAAS;AAAA,EAClC,IAAI,OAAO,qBAAqB,YAAY,iBAAiB,KAAK,MAAM;AAAA,IAAI,OAAO,EAAE,iBAAiB;AAAA,EACtG,MAAM,IAAI,WAAU;AAAA,IAClB,MAAM;AAAA,IACN,SACE,4GACA;AAAA,IACF,MAAM,EAAE,SAAS,MAAM,QAAQ,kBAAkB;AAAA,EACnD,CAAC;AAAA;AAwBI,SAAS,SAAS,CAAC,SAA4C;AAAA,EACpE,MAAM,UAAU,YAAY,EAAE,QAAQ,CAAC;AAAA,EACvC,MAAM,sBAAsB,QAAQ,iCAAiC,sBAAsB,UAAU,QAAQ,mBAAmB;AAAA,EAChI,MAAM,mBAAqE,wBAAwB,YAAY,oBAAoB;AAAA,EACnI,MAAM,cAAc,SAAS,sBAAsB;AAAA,EACnD,MAAM,eAAe,SAAS,gBAAgB;AAAA,EAC9C,MAAM,gBAAgB,SAAS,kBAAkB,YAAY,SAAS,oBAAoB,EAAE,OAAO,QAAQ,cAAc,CAAC;AAAA,EAC1H,IAAI,kBAAkB,UAAU,UAAU,SAAS;AAAA,IACjD,MAAM,IAAI,WAAU;AAAA,MAClB,MAAM;AAAA,MACN,SACE,mHACA;AAAA,MACF,MAAM,EAAE,cAAc;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,OAA0B;AAAA,EAC9B,IAAI,WAAW;AAAA,EAaf,IAAI,aAAsC;AAAA,EAE1C,IAAI,oBAAyC;AAAA,EAO7C,IAAI,mBAAmB;AAAA,EAEvB,SAAS,OAAO,GAAe;AAAA,IAC7B,IAAI,SAAS;AAAA,MAAM,OAAO;AAAA,IAE1B,IAAI,UAAU,SAAS;AAAA,MACrB,OAAO,QAAQ;AAAA,MACf,WAAW;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IAEA,MAAM,SAAS,gBAAgB;AAAA,MAC7B,kBAAkB,QAAQ;AAAA,MAC1B,KAAK,SAAS,OAAO;AAAA,SACjB,SAAS,qBAAqB,YAAY,CAAC,IAAI,EAAE,kBAAkB,QAAQ,iBAAiB;AAAA,SAC5F,SAAS,sBAAsB,YAAY,CAAC,IAAI,EAAE,mBAAmB,QAAQ,kBAAkB;AAAA,MACnG;AAAA,IACF,CAAC;AAAA,IAED,MAAM,WAAW,IAAG;AAAA,IACpB,OAAO,IAAI,SAAS,MAAM;AAAA,IAe1B,KAAK,KAAK,SAAS,MAAM,EAExB;AAAA,IACD,WAAW;AAAA,IACX,OAAO;AAAA;AAAA,EAUT,SAAS,iBAAiB,CAAC,QAAkD;AAAA,IAC3E,MAAM,WAAW,qBAAqB,IAAI,OAAO,IAAI;AAAA,IACrD,IAAI,aAAa;AAAA,MAAW,OAAO;AAAA,IACnC,MAAM,UAAU,yBAAyB,EAAE,KAAK,aAAa,CAAC;AAAA,IAC9D,qBAAqB,IAAI,OAAO,MAAM,OAAO;AAAA,IAC7C,OAAO;AAAA;AAAA,EAIT,SAAS,gBAAgB,CAAC,QAA4D;AAAA,IACpF,MAAM,SAAS,gBAAgB,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,IACxD,IAAI,CAAC;AAAA,MAAa,OAAO,EAAE,MAAM,OAAO,MAAM,QAAQ,OAAO,YAAY;AAAA,IAIzE,MAAM,OAAO,kBAAkB,EAAE,MAAM,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,IACjF,OAAO,SAAS,YACZ,EAAE,MAAM,OAAO,MAAM,QAAQ,OAAO,YAAY,IAChD,EAAE,MAAM,MAAM,OAAO,MAAM,QAAQ,OAAO,YAAY;AAAA;AAAA,EAG5D,SAAS,aAAa,CAAC,QAAgG;AAAA,IACrH,IAAI,OAAO,UAAU;AAAA,MAAW;AAAA,IAChC,MAAM,UAAU,MAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,SAAS,CAAC,OAAO,MAAM;AAAA,IAC7E,IAAI,QAAQ,WAAW;AAAA,MAAG,kBAAkB,EAAE,OAAO,OAAO,OAAO,OAAO,oBAAoB,EAAE,QAAQ,OAAO,OAAO,CAAC,EAAE,CAAC;AAAA,IAC1H,WAAW,UAAU;AAAA,MAAS,kBAAkB,EAAE,OAAO,OAAO,OAAO,OAAO,oBAAoB,EAAE,QAAQ,OAAO,QAAQ,OAAO,OAAO,UAAU,MAAM,OAAO,MAAM,QAAQ,YAAY,OAAO,KAAK,CAAC,EAAE,CAAC;AAAA;AAAA,EAG5M,eAAe,UAAU,CAAC,QAMa;AAAA,IACrC,MAAM,SAAS,iBAAiB,EAAE,MAAM,OAAO,MAAM,QAAQ,OAAO,OAAO,CAAC;AAAA,IAC5E,MAAM,SAAS,MAAM,UAAU,MAAM,OAAO,UAAU,MAAM,MAAM,CAAC;AAAA,IACnE,cAAc,EAAE,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,OAAO,CAAC;AAAA,IACpE,OAAO,YAAY,EAAE,OAAO,CAAC,EAAE;AAAA;AAAA,EAGjC,eAAe,gBAAgB,CAAC,QAMP;AAAA,IACvB,MAAM,SAAS,OAAO,WAAW,YAAY,YAAY,mBAAmB,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,IACrG,MAAM,SAAS,MAAM,UAAU,MAAM,OAAO,UAAU,MAAM,EAAE,MAAM,OAAO,MAAM,QAAQ,OAAO,YAAY,CAAC,CAAC;AAAA,IAC9G,cAAc,EAAE,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,OAAO,CAAC;AAAA,IACpE,MAAM,QAAQ,YAAY,EAAE,OAAO,CAAC;AAAA,IACpC,OAAO,EAAE,MAAM,MAAM,MAAM,cAAc,MAAM,YAAY,EAAE;AAAA;AAAA,EAU/D,SAAS,mBAAmB,CAAC,QAOT;AAAA,IAClB,QAAQ,UAAU,OAAO,QAAQ,UAAU,iBAAiB;AAAA,IAG5D,MAAM,YAAuB,EAAE,OAAO,CAAC,WAAW,SAAS,EAAE,QAAQ,YAAY,OAAO,QAAQ,UAAU,KAAK,EAAE,CAAC,EAAE;AAAA,IAGpH,MAAM,mBAAmB,CAAC,WAAmB,YAAmD;AAAA,MAC9F,IAAI,aAAa,QAAQ,SAAS,eAAe,EAAE,MAAM,UAAU,CAAC,MAAM;AAAA,QAAQ;AAAA,MAClF,OAAO,UAAU,MAAM,SAAS;AAAA,QAC9B,QAAQ,EAAE,MAAM,UAAU;AAAA,QAC1B,OAAO;AAAA,QACP,WAAW;AAAA,WACP,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,MACrC,CAAC,CAAC;AAAA;AAAA,IAGJ,OAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS;AAAA,MACT,kBAAkB,aAAa;AAAA,MAC/B,YAAY;AAAA,MACZ,YAAY,MAAsB;AAAA,QAAE,OAAO,aAAa,EAAE,OAAO,YAAY,CAAC;AAAA,QAAG,OAAO,eAAe,EAAE,MAAM,CAAC;AAAA;AAAA,WAE1G,QAAO,CAAC,YAAY;AAAA,QACtB,OAAO,iBAAiB,KAAK,YAAY,SAAS,aAAa,SAAS,UAAU,UAAU,KAAK,YAAY;AAAA,UAC7G,MAAM,OAAO,UAAU,EAAE,MAAM,CAAC;AAAA,UAChC,IAAI,SAAS;AAAA,YAAW,MAAM;AAAA,UAG9B,OAAO,aAAa,EAAE,OAAO,YAAY,CAAC;AAAA,UAC1C,qBAAoB,KAAK,YAAY,SAAS,aAAa,SAAS,UAAU,UAAU,OAAO,yBAAyB,CAAC;AAAA,UAGzH,MAAM,YAAuB,EAAE,OAAO,CAAC,WAAW,SAAS,EAAE,QAAQ,WAAW,WAAW,WAAW,iBAAiB,WAAW,WAAW,WAAW,WAAW,eAAe,WAAW,cAAc,CAAC,EAAE;AAAA,UAC9M,OAAO,WAAW,EAAE,WAAW,UAAU,WAAW,CAAC,EAClD,MAAM,CAAC,UAAmB,gCAAgC,EAAE,OAAO,MAAM,CAAC,CAAC;AAAA,UAC9E,CAAC;AAAA;AAAA,WAGC,cAAa,CAAC,YAAY;AAAA,QAC9B,MAAM,OAAO,UAAU,EAAE,MAAM,CAAC;AAAA,QAChC,IAAI,SAAS;AAAA,UAAW,MAAM;AAAA,QAC9B,OAAO,aAAa,EAAE,OAAO,YAAY,CAAC;AAAA,QAC1C,OAAO,iBAAiB,EAAE,WAAW,WAAW,UAAU,WAAW,CAAC,EACnE,MAAM,CAAC,UAAmB,gCAAgC,EAAE,OAAO,MAAM,CAAC,CAAC;AAAA;AAAA,WAG1E,YAAc,CAClB,IACA,eACY;AAAA,QACZ,+BAA+B,EAAE,SAAS,eAAe,UAAU,SAAS,CAAC;AAAA,QAC7E,OAAO,aAAa,EAAE,OAAO,SAAS,CAAC;AAAA,QACvC,OAAO,mBAAmB;AAAA,UACxB;AAAA,UACA,SAAS,CAAC,cAAc,iBAAiB,WAAW,KAAK;AAAA,UACzD,SAAS,CAAC,cAAsB,iBAAiB,WAAW,IAAI;AAAA,UAGhE,MAAM,CAAC,UAAU,GAAG,oBAAoB,EAAE,UAAU,OAAO,OAAO,QAAQ,UAAU,aAAa,CAAC,CAAC;AAAA,QACrG,CAAC;AAAA;AAAA,WAKG,QAAO,GAAkB;AAAA,QAC7B,OAAO,aAAa,EAAE,OAAO,YAAY,CAAC;AAAA,QAC1C,MAAM,4BAA4B,EAAE,QAAQ,UAAU,CAAC;AAAA;AAAA,WAGnD,WAAU,GAAkB;AAAA,QAChC,OAAO,aAAa,EAAE,OAAO,YAAY,CAAC;AAAA,QAC1C,MAAM,4BAA4B,EAAE,QAAQ,aAAa,CAAC;AAAA;AAAA,MAG5D,gBAAgB,CAAC,QAA4B;AAAA,QAC3C,OAAO,IAAI,aAAa,MAAM;AAAA;AAAA,IAElC;AAAA;AAAA,EAUF,SAAS,mBAAmB,GAAkB;AAAA,IAC5C,OAAO,mBAAmB;AAAA,MACxB,OAAO;AAAA,QACL,YAA2B;AAAA,UACzB,IAAI,eAAe;AAAA,YAAM;AAAA,UACzB,MAAM,UAAU;AAAA,UAChB,MAAM,UAAU;AAAA,UAChB,aAAa;AAAA,UACb,oBAAoB;AAAA,UAEpB,MAAM,UAAU,MAAM,QAAQ,MAAM,EAAE,MAAM,WAAW,CAAC,CAAC,EAAE,KACzD,MAAM;AAAA,YAAE,UAAU;AAAA,YAAG,QAAQ,QAAQ;AAAA,aACrC,CAAC,UAAmB;AAAA,YAAE,UAAU;AAAA,YAAG,QAAQ,QAAQ,IAAI;AAAA,YAAG,MAAM;AAAA,WAClE;AAAA;AAAA,QAEF,YAA2B;AAAA,UACzB,IAAI,SAAS,QAAQ,CAAC;AAAA,YAAU;AAAA,UAChC,MAAM,UAAU;AAAA,UAChB,MAAM,UAAU,MAAM,QAAQ,IAAI,CAAC;AAAA;AAAA,MAEvC;AAAA,IACF,CAAC;AAAA;AAAA,EAGH,MAAM,YAAY,2BAA2B,EAAE,QAAQ,MAAM,SAAS,oBAAoB,CAAC;AAAA,EAG3F,MAAM,OAAiH;AAAA,SAC/G,YAAW,GAAG,OAAO;AAAA,MACzB,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,QAAQ,CAAC;AAAA,MACxD,IAAI;AAAA,MACJ,MAAM,UAAU,CAAC,UAAyB;AAAA,QAAE,oBAAoB;AAAA;AAAA,MAChE,OAAO,KAAK,SAAS,OAAO;AAAA,MAC5B,OAAO,iBAAiB;AAAA,QACtB;AAAA,QACA,SAAS,OAAO,UAAU;AAAA,UACxB,IAAI,sBAAsB;AAAA,YAAW,MAAM,iBAAiB,EAAE,OAAO,kBAAkB,CAAC;AAAA,UACxF,QAAQ,MAAM,iBAAiB,EAAE,WAAW,WAAW,MAAM,CAAC,GAAG;AAAA;AAAA,QAEnE,QAAQ,CAAC,UAAU,sBAAsB,aAAa,iBAAiB,EAAE,MAAM,CAAC;AAAA,QAChF,SAAS,YAAY;AAAA,UACnB,OAAO,iBAAiB,SAAS,OAAO;AAAA,UAExC,OAAO,QAAQ,IAAI;AAAA;AAAA,MAEvB,CAAC;AAAA;AAAA,SAGG,QAAO,CAAC,QAAQ;AAAA,MACpB,qBAAoB,KAAK,QAAQ,SAAS,kBAAkB,UAAU,SAAS,CAAC;AAAA,MAChF,IAAI,OAAO,WAAW,iBAAiB,WAAW;AAAA,QAGhD,MAAM,UAAU,EAAE,YAAY,WAAoB;AAAA,QAClD,OAAO,KAAK,YAAY,CAAC,OAAO,GAAG,QAAQ,MAAM,GAAG,OAAO;AAAA,MAC7D;AAAA,MAGA,MAAM,SAAS,iBAAiB,EAAE,MAAM,OAAO,MAAM,QAAQ,OAAO,OAAO,CAAC;AAAA,MAC5E,MAAM,WAAW,QAAQ;AAAA,MACzB,IAAI,CAAC,kBAAkB;AAAA,QACrB,MAAM,SAAS,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC;AAAA,QACvD,IAAI,qBAAqB,EAAE,OAAO,CAAC,KAAK,OAAO,cAAc,WAAW;AAAA,UACtE,MAAM,OAAO,qBAAqB,EAAE,OAAO,CAAC,IAAI,qBAAqB,EAAE,OAAO,CAAC,IAAI;AAAA,UACnF,IAAI,QAAQ;AAAA,UACZ,IAAI;AAAA,YACF,qBAAoB,KAAK,QAAQ,UAAU,UAAU,OAAO,yBAAyB,CAAC;AAAA,YACtF,MAAM,SAAS,MAAM,UAAU,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,YACzD,QAAQ;AAAA,YACR,OAAO,YAAY,EAAE,OAAO,CAAC,EAAE;AAAA,YAC/B,OAAO,OAAO;AAAA,YACd,QAAQ,MAAM,MAAM,cAAc,EAAE,OAAO,OAAO,OAAO,SAAS,UAAU,CAAC,KAAK;AAAA,YAClF,MAAM;AAAA,oBACN;AAAA,YACA,MAAM,OAAO;AAAA,YACb,OAAO,QAAQ,CAAC,KAAK;AAAA;AAAA,QAEzB;AAAA,QAEA,OAAO,QAAQ;AAAA,QACf,mBAAmB;AAAA,MACrB;AAAA,MACA,qBAAoB,KAAK,QAAQ,UAAU,UAAU,OAAO,yBAAyB,CAAC;AAAA,MACtF,OAAO,YAAY,EAAE,QAAQ,MAAM,UAAU,MAAM,SAAS,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA;AAAA,SAG1E,cAAa,CAAC,QAAQ;AAAA,MAC1B,MAAM,UAAU,8BAA8B,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,MACnE,IAAI,eAAe,MAAM;AAAA,QACvB,IAAI,YAAY,SAAS;AAAA,UACvB,MAAM,UAAU;AAAA,UAChB,MAAM,UAAU;AAAA,UAChB,aAAa;AAAA,UACb,oBAAoB;AAAA,UACpB,IAAI;AAAA,YACF,OAAO,MAAM,iBAAiB,EAAE,WAAW,YAAY,OAAO,CAAC;AAAA,oBAC/D;AAAA,YACA,UAAU;AAAA,YACV,QAAQ,QAAQ;AAAA;AAAA,QAEpB;AAAA,QAGA,OAAO,iBAAiB,EAAE,WAAW,eAAe,OAAO,CAAC;AAAA,MAC9D;AAAA,MACA,IAAI,YAAY,QAAQ;AAAA,QACtB,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,QAAQ,CAAC;AAAA,QACxD,MAAM,UAAU,sBAAsB,EAAE,OAAO,CAAC;AAAA,QAChD,IAAI;AAAA,UACF,MAAM,SAAS,MAAM,iBAAiB,EAAE,WAAW,WAAW,OAAO,CAAC;AAAA,UACtE,aAAa;AAAA,UACb,oBAAoB;AAAA,UACpB,OAAO;AAAA,UACP,OAAO,OAAO;AAAA,UACd,QAAQ;AAAA,UACR,OAAO,QAAQ;AAAA,UACf,MAAM;AAAA;AAAA,MAEV;AAAA,MACA,OAAO,iBAAiB,EAAE,WAAW,QAAQ,MAAM,OAAO,CAAC;AAAA;AAAA,SAGvD,YAAc,CAClB,IACA,oBACY;AAAA,MAGZ,4BAA4B;AAAA,QAC1B,SAAS;AAAA,QACT,SAAS,QAAQ;AAAA,QACjB,UAAU;AAAA,MACZ,CAAC;AAAA,MAGD,MAAM,iBAAiB;AAAA,QACrB,gBAAgB,oBAAoB;AAAA,QACpC,UAAU,kBAAkB,EAAE,SAAS,oBAAoB,UAAU,SAAS,CAAC;AAAA,QAC/E,SAAS,oBAAoB;AAAA,MAC/B;AAAA,MACA,MAAM,WAAW,yBAAyB,cAAc;AAAA,MACxD,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,QAAQ,CAAC;AAAA,MAQxD,IAAI;AAAA,MACJ,MAAM,UAAU,sBAAsB;AAAA,QACpC;AAAA,QACA,SAAS,CAAC,UAAU;AAAA,UAClB,IAAI,sBAAsB;AAAA,YAAW,oBAAoB,EAAE,MAAM;AAAA;AAAA,MAErE,CAAC;AAAA,MACD,MAAM,QAAQ,qBAAqB;AAAA,MAInC,MAAM,SAAS,uBAAuB,EAAE,SAAS,oBAAoB,UAAU,SAAS,CAAC;AAAA,MAQzF,MAAM,UAAU,cAAc,EAAE,OAAO,CAAC;AAAA,MACxC,MAAM,eAAe,YAAY,eAAe,YAAY,aAAa,eAAe,WAAW;AAAA,MACnG,MAAM,UAA6B,eAC/B,CAAC,eAAe,cAAc,GAAG,GAAI,eAAe,YAAY,YAAY,CAAC,IAAI,CAAC,0BAA0B,EAAE,SAAS,eAAe,QAAQ,CAAC,CAAC,CAAE,IAClJ,CAAC;AAAA,MACL,MAAM,WAAW,sBAAsB,EAAE,QAAQ,CAAC;AAAA,MAElD,IAAI;AAAA,MAEJ,IAAI,WAAiC;AAAA,MAErC,MAAM,kBAAkB,CAAC,mBAAoF;AAAA,QAC3G,QAAQ,QAAQ,YAAY;AAAA,QAC5B,MAAM,WAAW,MAAmC;AAAA,UAClD,qBAAoB,EAAE,WAAW,eAAe,WAAW,QAAQ,QAAQ,SAAS,kBAAkB,UAAU,UAAU,OAAO,yBAAyB,CAAC;AAAA,UAC3J,eAAe,OAAO;AAAA,UACtB,OAAO,aAAa,IAAI,EAAE,QAAQ,WAAW,eAAe,WAAW,eAAe,eAAe,eAAe,MAAM,MAAM,OAAO,MAAM,MAAM,EAAE,CAAC;AAAA;AAAA,QAExJ,IAAI,YAAY,QAAQ;AAAA,UAAuB,MAAM,wBAAwB,EAAE,OAAO,oBAAmB,EAAE,OAAO,UAAU,CAAC,EAAE,CAAC;AAAA,QAChI,IAAI,YAAY,QAAQ,mBAAmB;AAAA,UAAW,MAAM,wBAAwB,EAAE,OAAO,eAAe,CAAC;AAAA,QAC7G,IAAI;AAAA,QACJ,IAAI,YAAY,QAAQ,OAAO,YAAY,MAAM;AAAA,UAC/C,IAAI,MAAM,MAAM,aAAa;AAAA,YAAM,MAAM,MAAM,MAAM;AAAA,UACrD,OAAO,aAAa,EAAE,OAAO,eAAe,SAAS,YAAY,CAAC;AAAA,UAClE,MAAM,KAAK,wBAAwB,EAAE,OAAO,CAAC;AAAA,UAC7C,IAAI,OAAO;AAAA,YAAM,QAAQ,0BAA0B,EAAE,SAAS,GAAG,CAAC;AAAA,QACpE;AAAA,QACA,IAAI,CAAC,SAAS;AAAA,UAEZ,IAAI,UAAU;AAAA,YAAW,OAAO,SAAS;AAAA,UACzC,MAAM,QAAQ,eAAe,SAAS;AAAA,UACtC,OAAO,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC,EAAE,KAAK,MAAM;AAAA,YAG/D,OAAO,aAAa,EAAE,MAAM,CAAC;AAAA,YAC7B,OAAO,SAAS;AAAA,WACjB;AAAA,QACH;AAAA,QAGA,MAAM,iBAAiB,YAAY,QAAQ,SAAS,WAAW;AAAA,QAC/D,IAAI,CAAC,kBAAkB,UAAU;AAAA,UAAW,OAAO,SAAS;AAAA,QAG5D,MAAM,YAAyB,eAAe,cAAc,OACxD,CAAC,EAAE,MAAM,OAAO,MAAM,QAAQ,mBAAmB,EAAE,QAAQ,OAAO,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC,IACnF,CAAC;AAAA,QACL,MAAM,iBAAiB,CAAC,SAAS,OAAO;AAAA,QACxC,MAAM,UAAU,iBAAiB,CAAC,GAAG,SAAS,KAAK,CAAC,IAAI,CAAC;AAAA,QACzD,IAAI,UAAU;AAAA,UAAW,QAAQ,KAAK,KAAK;AAAA,QAC3C,MAAM,eAAe,iBAAiB,QAAQ,SAAS;AAAA,QAGvD,qBAAoB,EAAE,WAAW,eAAe,WAAW,QAAQ,QAAQ,SAAS,kBAAkB,UAAU,UAAU,OAAO,yBAAyB,CAAC;AAAA,QAC3J,IAAI,UAAU,SAAS;AAAA,UAAG,eAAe,OAAO;AAAA,QAChD,MAAM,QAAQ,SAAS,EAAE,QAAQ,OAAO,CAAC,GAAG,QAAQ,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,SAAS,GAAG,OAAO,YAAY,CAAC,EAC/G,KAAK,CAAC,YAA8D;AAAA,UACnE,MAAM,UAAU,kBAAkB,EAAE,QAAQ,CAAC;AAAA,UAC7C,IAAI,YAAY,WAAW;AAAA,YAGzB,IAAI,QAAQ,QAAQ,gBAAiB,QAAQ,mBAAmB,aAAa,eAAe,GAAI;AAAA,cAE9F,iBAAiB,iBAAiB,EAAE,OAAO,QAAQ,MAAM,CAAC;AAAA,YAC5D;AAAA,YACA,MAAM,QAAQ;AAAA,UAChB;AAAA,UACA,IAAI,UAAU,WAAW;AAAA,YAAG,OAAO,SAAS;AAAA,UAC5C,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE;AAAA,UAChC,OAAO,MAAM,WAAW,OAAO,KAAK,SAAS,EAAE,MAAM,CAAC,GAAG,UAAU,EAAE;AAAA,SACtE;AAAA,QACH,IAAI,OAAO,YAAY,QAAQ,gBAAgB;AAAA,UAI7C,MAAM,UAAyB,MAAM,KAAK,QAAQ,MAAM,EAAE,KAAK,MAAM;AAAA,YACnE,IAAI,aAAa;AAAA,cAAS,WAAW;AAAA,WACtC;AAAA,UACD,WAAW;AAAA,QACb;AAAA,QACA,OAAO;AAAA;AAAA,MAET,MAAM,OAAO,CAAC,mBAAoF;AAAA,QAChG,IAAI;AAAA,UACF,OAAO,gBAAgB,cAAc;AAAA,UACrC,OAAO,OAAO;AAAA,UACd,OAAO,QAAQ,OAAO,KAAK;AAAA;AAAA;AAAA,MAO/B,IAAI,OAAyB,QAAQ,QAAQ;AAAA,MAC7C,MAAM,mBAAmB,IAAI;AAAA,MAC7B,IAAI,oBAAoB;AAAA,MACxB,MAAM,WAAgC,CAAC,mBAAmB;AAAA,QACxD,IAAI,eAAe,WAAW,iBAAiB,aAAa,CAAC,mBAAmB;AAAA,UAG9E,oBAAoB;AAAA,UACpB,OAAO,QAAQ,WAAW,CAAC,GAAG,gBAAgB,CAAC;AAAA,QACjD;AAAA,QACA,IAAI,OAAO,YAAY,QAAQ,CAAC,mBAAmB;AAAA,UAIjD,MAAM,UAAU,aAAa,OACzB,KAAK,cAAc,IACnB,SAAS,KAAK,MAAM,KAAK,cAAc,CAAC;AAAA,UAC5C,iBAAiB,IAAI,OAAO;AAAA,UACvB,QAAQ,KAAK,MAAM,iBAAiB,OAAO,OAAO,GAAG,MAAM,iBAAiB,OAAO,OAAO,CAAC;AAAA,UAChG,OAAO;AAAA,QACT;AAAA,QACA,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK,cAAc,CAAC;AAAA,QAEpD,OAAO,QAAQ,KAAK,QAAQ,MAAM;AAAA,QAClC,OAAO;AAAA;AAAA,MAGT,IAAI,UAAU;AAAA,MACd,IAAI,WAAW;AAAA,MACf,IAAI,wBAAwB;AAAA,MAC5B,MAAM,eAAe,yBAAyB,EAAE,kBAAkB,qBAAqB,QAAQ,QAAQ,WAAW,MAAM;AAAA,QACtH,IAAI,CAAC,UAAU;AAAA,UAAE,WAAW;AAAA,UAAM,OAAO,QAAQ,IAAI;AAAA,QAAG;AAAA,SACvD,SAAS,MAAM;AAAA,QAChB,UAAU;AAAA,QACV,wBAAwB;AAAA,QAGxB,uBAAuB,EAAE,OAAO,OAAO,oBAAmB,EAAE,OAAO,UAAU,CAAC,EAAE,CAAC;AAAA,QACjF,CAAC;AAAA,MAEH,IAAI,iBAAiC;AAAA,MACrC,IAAI;AAAA,QACF,IAAI,CAAC;AAAA,UAAc,MAAM,UAAU,MAAM,OAAO,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC;AAAA,QACzE,MAAM,SAAS,MAAM,aAAa;AAAA,UAChC;AAAA,UACA,MAAM,MAAM,GAAG,oBAAoB,EAAE,UAAU,OAAO,QAAQ,UAAU,UAAU,WAAW,MAAM,aAAa,CAAC,CAAC;AAAA,QACpH,CAAC;AAAA,QAED,OAAO,aAAa,EAAE,OAAO,SAAS,CAAC;AAAA,QACvC,IAAI,CAAC,SAAS,OAAO,GAAG;AAAA,UAGtB,OAAO,MAAM,EAAE,SAAS,YAAY,CAAC;AAAA,UACrC,OAAO;AAAA,QACT;AAAA,QAKA,IAAI,mBAAmB;AAAA,UAAW,MAAM,wBAAwB,EAAE,OAAO,eAAe,CAAC;AAAA,QACzF,MAAM,SAAS,MAAM,UAAU,MAAM,SAAS;AAAA,UAC5C,QAAQ,EAAE,MAAM,SAAS;AAAA,UACzB,OAAO;AAAA,UACP,WAAW;AAAA,UACX,MAAM,MAAM;AAAA,YAGV,IAAI,sBAAsB;AAAA,cAAW,MAAM,iBAAiB,EAAE,OAAO,kBAAkB,MAAM,CAAC;AAAA,YAC9F,iBAAiB;AAAA;AAAA,QAErB,CAAC,CAAC,EACC,MAAM,CAAC,UAAmB;AAAA,UAKzB,IAAI,mBAAmB;AAAA,YAAY,MAAM;AAAA,UAKzC,IAAI,CAAC,iBAAiB,EAAE,OAAO,MAAM,CAAC,GAAG;AAAA,YACvC,iBAAiB;AAAA,YACjB,MAAM;AAAA,UACR;AAAA,UACA,iBAAiB;AAAA,UACjB,UAAU;AAAA,UACV,OAAO,MAAM,EAAE,SAAS,UAAU,CAAC;AAAA,UACnC,MAAM,0BAA0B,EAAE,MAAM,CAAC;AAAA,SAC1C;AAAA,QAGH,IAAI,YAAY,EAAE,QAAQ,OAAO,CAAC,EAAE,YAAY,YAAY;AAAA,UAC1D,iBAAiB;AAAA,UACjB,MAAM,wBAAwB,EAAE,OAAO,MAAM,MAAM,SAAS,MAAM,CAAC;AAAA,QACrE;AAAA,QACA,OAAO,MAAM,EAAE,SAAS,YAAY,CAAC;AAAA,QACrC,OAAO;AAAA,QACP,OAAO,OAAO;AAAA,QACd,IAAI,+BAA+B,KAAK,KAAK,MAAM,MAAM,aAAa,MAAM;AAAA,UAG1E,UAAU;AAAA,UACV,OAAO,MAAM,EAAE,SAAS,UAAU,CAAC;AAAA,QACrC;AAAA,QACA,IAAI,WAA4B;AAAA,QAChC,IAAI;AAAA,UAEF,IAAI,SAAS,OAAO,GAAG;AAAA,YACrB,MAAM,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,GAAG,SAAS,KAAK,CAAC;AAAA,YAC9D,WAAW;AAAA,UACb;AAAA,UACA,MAAM;AAAA,UAMN,UAAU;AAAA,UACV,WAAW;AAAA;AAAA,QAGb,MAAM,wBAAwB;AAAA,UAC5B;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,UACR;AAAA,UACA,YAAY,UAAU,cAAc;AAAA,QACtC,CAAC;AAAA,gBACD;AAAA,QACA,QAAQ;AAAA,QACR,IAAI,CAAC;AAAA,UAAU,OAAO,QAAQ,OAAO;AAAA;AAAA;AAAA,SAInC,QAAO,GAAkB;AAAA,MAC7B,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,QAAQ,CAAC;AAAA,MACxD,OAAO,QAAQ;AAAA;AAAA,EAEnB;AAAA,EAEA,OAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA,YAAY;AAAA,IACZ,oBAAoB;AAAA,IAEpB,WAAW,GAAG,OAAO;AAAA,MACnB,OAAO,UAAU,MAAM,EAAE,KAAK,MAAM,KAAK,YAAY,EAAE,KAAK,CAAC,aAAa,UAAU,WAAW,EAAE,KAAK,MAAM,IAAI,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAAA;AAAA,IAGnI,OAAO,CAAC,QAAQ;AAAA,MACd,OAAO,UAAU,MAAM,EAAE,KAAK,MAAM,OAAO,cAAc,YAAY,KAAK,QAAQ,MAAM,IAAI,iBAAiB,KAAK,QAAQ,SAAS,kBAAkB,UAAU,UAAU,KAAK,MAAM,KAAK,QAAQ,MAAM,EAAE,CAAC,EAAE,CAAC;AAAA;AAAA,IAG/M,aAAa,CAAC,QAAQ;AAAA,MACpB,OAAO,UAAU,MAAM,EAAE,KAAK,MAAM,KAAK,cAAc,MAAM,EAAE,CAAC;AAAA;AAAA,IAGlE,WAAc,CACZ,IACA,oBACY;AAAA,MACZ,OAAO,UAAU,MAAM,EAAE,KAAK,MAAM,KAAK,YAAY,CAAC,cAAc,UAAU,WAAW,EAAE,KAAK,MAAM,GAAG,SAAS,EAAE,CAAC,GAAG,kBAAkB,EAAE,CAAC;AAAA;AAAA,IAG/I,OAAO,GAAkB;AAAA,MACvB,OAAO,UAAU,MAAM,EAAE,KAAK,MAAM,KAAK,QAAQ,EAAE,CAAC;AAAA;AAAA,IAStD,UAAU,GAAkB;AAAA,MAC1B,OAAO,UAAU,WAAW;AAAA;AAAA,IAG9B,gBAAgB,CAAC,QAA4B;AAAA,MAC3C,OAAO,IAAI,aAAa,MAAM;AAAA;AAAA,EAElC;AAAA;",
|
|
16
|
+
"debugId": "3DBA8F0584DC025664756E2164756E21",
|
|
15
17
|
"names": []
|
|
16
18
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from "@vibeorm/runtime";
|
|
2
|
+
import type { PgQueryConfig, PgQueryResultsLike, PgPoolClientLike } from "./index.ts";
|
|
3
|
+
type Execution = Parameters<DatabaseAdapter["execute"]>[0];
|
|
4
|
+
type ControlClient = {
|
|
5
|
+
connect(): Promise<void>;
|
|
6
|
+
query(config: PgQueryConfig): Promise<PgQueryResultsLike>;
|
|
7
|
+
end(): Promise<void>;
|
|
8
|
+
on(event: "error", listener: (error: unknown) => void): unknown;
|
|
9
|
+
};
|
|
10
|
+
/** One pinned transaction's running-read transport. Original and control settlement are both awaited. */
|
|
11
|
+
export type PgReadCancellation = {
|
|
12
|
+
readonly support: "before-dispatch" | "running";
|
|
13
|
+
run(params: {
|
|
14
|
+
config: PgQueryConfig;
|
|
15
|
+
execution?: Execution["execution"];
|
|
16
|
+
onReadControl?: Execution["onReadControl"];
|
|
17
|
+
send: () => Promise<PgQueryResultsLike>;
|
|
18
|
+
}): Promise<PgQueryResultsLike>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Official-driver control SQL seam. The target must already be in a transaction,
|
|
22
|
+
* held exclusively until this run settles; every cancel attempt discards it.
|
|
23
|
+
* PostgreSQL documents pg_cancel_backend as signal dispatch, not SQL settlement:
|
|
24
|
+
* https://www.postgresql.org/docs/18/functions-admin.html#FUNCTIONS-ADMIN-SIGNAL
|
|
25
|
+
*/
|
|
26
|
+
export declare function createPgReadCancellation(params: {
|
|
27
|
+
connectionString?: string;
|
|
28
|
+
target: Pick<PgPoolClientLike, "query" | "pipeline" | "connectionParameters">;
|
|
29
|
+
discard: () => void;
|
|
30
|
+
/** Destroy exactly the held target on failed/unsettled cancellation, never a pool-wide close. */
|
|
31
|
+
terminate: () => void;
|
|
32
|
+
/** Hermetic driver injection; production uses the installed official pg.Client. */
|
|
33
|
+
createControl?: (params: {
|
|
34
|
+
connectionString: string;
|
|
35
|
+
}) => ControlClient;
|
|
36
|
+
}): PgReadCancellation;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=read-cancellation.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeorm/adapter-pg",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "node-postgres adapter for VibeORM v2 — Bun or Node.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"orm",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"bun": ">=1.2.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@vibeorm/runtime": "
|
|
49
|
-
"@vibeorm/schema": "
|
|
48
|
+
"@vibeorm/runtime": "4.1.0",
|
|
49
|
+
"@vibeorm/schema": "4.1.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"pg": ">=8"
|