@vibeorm/adapter-pg 4.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,12 +47,37 @@ export declare function placePlanCacheMode(params: {
47
47
  connectionString: string | undefined;
48
48
  planCacheMode: PlanCacheMode;
49
49
  }): StartupOptionsPlacement;
50
+ /**
51
+ * Validate an `idleTimeout` option value (AW-04): integer milliseconds from
52
+ * 0 through 2,147,483,647. pg-pool passes it to `setTimeout`; larger values
53
+ * overflow Node's signed 32-bit timer range and become a 1 ms timeout.
54
+ *
55
+ * @throws VibeError `VIBE_CONFIG` (`meta.reason: "invalid-idle-timeout"`).
56
+ */
57
+ export declare function assertIdleTimeout(params: {
58
+ value: unknown;
59
+ }): number;
60
+ /**
61
+ * Validate a `min` option value (V7): an integer from 0 through the pool's
62
+ * `max`. pg-pool never idle-closes a connection while the pool holds `min` or
63
+ * fewer; it does not open them in advance (`adapter.connect()` does).
64
+ *
65
+ * @throws VibeError `VIBE_CONFIG` (`meta.reason: "invalid-pool-min"`).
66
+ */
67
+ export declare function assertPoolMin(params: {
68
+ value: unknown;
69
+ max: number;
70
+ }): number;
50
71
  /** Build the `pg.Pool` constructor config for an adapter-owned pool. */
51
72
  export declare function buildPoolConfig(params: {
52
73
  connectionString: string | undefined;
53
74
  max: number;
75
+ /** Connections pg-pool keeps open however long they idle (pg-pool's `min`). Absent = pg-pool's 0. */
76
+ min?: number;
54
77
  statementTimeout?: number;
55
78
  connectionTimeout?: number;
79
+ /** Milliseconds an idle pooled connection stays open (pg-pool's `idleTimeoutMillis`; `0` = never closed). Absent = pg-pool's 10 000. */
80
+ idleTimeout?: number;
56
81
  planCacheMode?: PlanCacheMode;
57
82
  }): Record<string, unknown>;
58
83
  //# sourceMappingURL=pool-config.d.ts.map
@@ -20,12 +20,21 @@
20
20
  * `max` per connection, permanently. Statements still bind their parameters;
21
21
  * only the server-side plan cache is skipped.
22
22
  *
23
- * 2. **Adapters sharing one pool must not collide** (F18). Each adapter used to
23
+ * 2. **One name never means two texts** (F18, EPIC H). Each adapter used to
24
24
  * start its own counter at zero, so two adapters on one pool both sent
25
25
  * `vibeorm_0` for different SQL and postgres refused the second
26
- * ("Prepared statements must be unique"). Names are therefore allocated by a
27
- * registry shared per pool, which also stops N adapters from preparing the
28
- * same text N times.
26
+ * ("Prepared statements must be unique"). Worse, two INDEPENDENT registries
27
+ * (two pools, two processes) whose sessions reach one backend — a pooler
28
+ * that switches backends — could execute another registry's `vibeorm_0`
29
+ * and return that statement's rows without any error. A name is therefore
30
+ * derived from the text itself: `vibeorm_` + the SHA-256 of the exact
31
+ * statement text, base32 (see {@link statementNameFor}). Equal names mean
32
+ * equal texts in every registry, process and adapter version, so the worst
33
+ * a foreign backend can do is refuse a name (26000/42P05), never run another
34
+ * text under it. The registry is still shared per pool, which keeps one
35
+ * budget per pool and stops N adapters from preparing the same text N times.
36
+ * Only the text is hashed: node-postgres sends no parameter types with a
37
+ * named Parse, and result parsers live on the client, not the backend.
29
38
  *
30
39
  * 3. **A one-off text must not spend the budget.** Because names never evict
31
40
  * (property 1), allocating on FIRST sight hands the whole budget to whatever
@@ -38,6 +47,16 @@
38
47
  * text or exceed the budget. A hot text loses exactly one prepared
39
48
  * execution; a one-off text costs the server nothing.
40
49
  */
50
+ /**
51
+ * The content-addressed prepared-statement name of `text`: `vibeorm_` + the
52
+ * SHA-256 of its UTF-8 bytes in lowercase base32 without padding — 52
53
+ * characters, 60 bytes in total, within PostgreSQL's 63-byte identifier limit
54
+ * (NAMEDATALEN − 1), so the server never truncates two names into one. The same
55
+ * text yields the same name in every registry, process and version.
56
+ */
57
+ export declare function statementNameFor(params: {
58
+ text: string;
59
+ }): string;
41
60
  /** Registry mapping SQL text to the prepared-statement name `pg` should send. */
42
61
  export type StatementNameCache = {
43
62
  /**
@@ -55,7 +74,7 @@ export type StatementNameCache = {
55
74
  };
56
75
  /**
57
76
  * Create a statement-name registry that allocates at most `max` names
58
- * (`vibeorm_0`, `vibeorm_1`, …), each on the `promoteAfter`-th sighting of its
77
+ * ({@link statementNameFor} of the text), each on the `promoteAfter`-th sighting of its
59
78
  * text (default 2). Texts seen fewer times, and texts arriving after the budget
60
79
  * is spent, get no name and run unprepared, so the server's prepared-statement
61
80
  * retention stays bounded and a stream of one-off texts cannot consume it.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Char-code decoding of PostgreSQL's PLAIN timestamp wire text (performance
3
+ * round 3, AW-03).
4
+ *
5
+ * node-postgres hands every `timestamptz` cell to pg-types' parser
6
+ * (postgres-date: three regular expressions, `parseInt` per field, a split)
7
+ * and every `timestamp` cell to this adapter's `parseZoneLessAsUtc` (a regular
8
+ * expression, two `replace` calls and a template string before `new Date`).
9
+ * The text PostgreSQL prints for the common case has a fixed layout, so one
10
+ * pass over its character codes yields the same instant 2.5–3× cheaper.
11
+ *
12
+ * This function only ACCEPTS or DECLINES: it returns `undefined` for every
13
+ * text outside the plain form, and the caller then runs today's parser
14
+ * unchanged. It never adds a coercion of its own — an accepted text decodes
15
+ * to exactly the instant today's parser returns (differential test
16
+ * `tests/timestamp-text.test.ts`).
17
+ */
18
+ /**
19
+ * Decode `YYYY-MM-DD HH:MM:SS[.f{1,6}]` — followed by `±HH` or `±HH:MM` when
20
+ * `zoned`, by nothing otherwise — as a `Date`: zone-less text is read as UTC
21
+ * wall-clock time, zoned text at its offset. Fraction digits beyond the third
22
+ * are truncated (as postgres-date and `parseZoneLessAsUtc` both do).
23
+ *
24
+ * Returns `undefined` — "use today's parser" — for anything else: years
25
+ * before 1000 (`Date.UTC` maps 0–99 to 1900–1999), BC dates, `infinity`,
26
+ * offsets with seconds (historic local mean time), date-only text, 7+
27
+ * fraction digits, and out-of-range fields (month 13, February 30, hour 24,
28
+ * second 60), so the fallback keeps every such text's current decoding.
29
+ */
30
+ export declare function parsePlainTimestampText(params: {
31
+ readonly text: string;
32
+ readonly zoned: boolean;
33
+ }): Date | undefined;
34
+ //# sourceMappingURL=timestamp-text.d.ts.map
@@ -66,5 +66,27 @@ export type RawTransactionControl = "open" | "close" | null;
66
66
  export declare function classifyRawTransactionControl(params: {
67
67
  text: string;
68
68
  }): RawTransactionControl;
69
+ /**
70
+ * Whether raw text WITHOUT bound values is PROVABLY one statement whose result
71
+ * and errors are the same in the extended protocol (a one-Sync batch) as in the
72
+ * simple protocol node-postgres uses for it — so it may ride a batch behind
73
+ * pending control (`BEGIN`, `SAVEPOINT`) like any bound statement.
74
+ *
75
+ * The proof is lexical and conservative:
76
+ * - no `;` anywhere (PostgreSQL's grammar splits statements only on `;`, so a
77
+ * non-blank text without one is ONE statement or a syntax error — the same
78
+ * error in both protocols; a `;` inside a string literal is refused too);
79
+ * - no `$` (a `$1` without values fails with a different SQLSTATE in each
80
+ * protocol; dollar quoting is refused with it);
81
+ * - no comment (`--`, `/*`) and not blank (an empty query has no statement);
82
+ * - not transaction control (`BEGIN`, `COMMIT`, `ROLLBACK`, `SAVEPOINT`,
83
+ * `RELEASE`, `PREPARE TRANSACTION`, …) and not `COPY` (its stdin refusal
84
+ * text differs inside a batch).
85
+ * Anything else keeps today's path: it goes alone, after its control was
86
+ * confirmed.
87
+ */
88
+ export declare function isSingleStatementText(params: {
89
+ text: string;
90
+ }): boolean;
69
91
  export { refuseNestedTransactionOptions } from "@vibeorm/runtime/adapter-kit";
70
92
  //# sourceMappingURL=transaction-sql.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeorm/adapter-pg",
3
- "version": "4.2.0",
3
+ "version": "5.0.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": "4.2.0",
49
- "@vibeorm/schema": "4.2.0"
48
+ "@vibeorm/runtime": "5.0.0",
49
+ "@vibeorm/schema": "5.0.0"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "pg": ">=8"