@vibeorm/adapter-bun 2.4.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VibeORM contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -16,8 +16,10 @@ bun add @vibeorm/adapter-bun
16
16
  import { VibeClient } from "./generated/vibeorm/index.js";
17
17
  import { bunAdapter } from "@vibeorm/adapter-bun";
18
18
 
19
- // url defaults to process.env.DATABASE_URL, then bun's PG* env vars
20
- const db = VibeClient({ adapter: bunAdapter({ url: process.env.DATABASE_URL, max: 10 }) });
19
+ // The adapter reads no environment variable: pass the address you mean.
20
+ const url = process.env.DATABASE_URL;
21
+ if (!url) throw new Error("DATABASE_URL is not set");
22
+ const db = VibeClient({ adapter: bunAdapter({ url, max: 10 }) });
21
23
 
22
24
  const users = await db.user.findMany({ where: { email: { contains: "@example.com" } }, take: 10 });
23
25
 
@@ -26,54 +28,70 @@ await db.$disconnect();
26
28
 
27
29
  ## Options
28
30
 
29
- `bunAdapter(options?: BunAdapterOptions)`:
31
+ `bunAdapter(options: BunAdapterOptions)` — `url` is required. Without it, the factory throws `VIBE_CONFIG` (`meta.reason: "missing-address"`) before any driver instance or connection exists. The adapter never reads `DATABASE_URL` and always hands bun:sql this URL, so bun:sql's own `POSTGRES_URL` / `DATABASE_URL` / `PG*` lookup never chooses the server. A database named in the URL path is pinned explicitly, because bun:sql would otherwise let `PGDATABASE` replace it. Give a complete URL: bun:sql fills parts a URL leaves out (user, database, …) from the `PG*` variables.
30
32
 
31
33
  | Option | Type | Default | Meaning |
32
34
  |---|---|---|---|
33
- | `url` | `string` | `process.env.DATABASE_URL`, then bun's `PG*` env vars | PostgreSQL connection URL |
35
+ | `url` | `string` | — (required) | PostgreSQL connection URL |
34
36
  | `max` | `number` | `10` | Maximum pooled connections |
35
37
  | `statementTimeout` | `number` (ms) | — | Pool-wide `statement_timeout`, sent as a startup parameter so every connection inherits it; a per-transaction `timeout` overrides it |
36
- | `connectionTimeout` | `number` (ms) | — | Wait for a connection to be established. bun:sql expects seconds; the adapter converts |
37
- | `preparedStatements` | `boolean` | `false` | Run ORM-path queries as tagged templates, creating named prepared statements. On node-postgres the same switch measured −17…−25 % on reads (`@vibeorm/adapter-pg` README, "Prepared statements: the measured trade"); off by default because named statements break behind transaction-mode poolers without prepared-statement support |
38
- | `stmtCacheMax` | `number` | `1000` | Maximum entries in the synthetic template cache (LRU) |
38
+ | `connectionTimeout` | `number` (ms) | URL `connect_timeout`, else none | Wait for a connection to be established. bun:sql expects seconds; the adapter converts |
39
+ | `preparedStatements` | `boolean` | `false` | Use bun:sql's **named** prepared statements and run ORM-path queries as tagged templates. Off, every statement is an unnamed statement with text results — the node-postgres model. Read "Statements and values" before you turn it on |
40
+ | `stmtCacheMax` | `number` | `1000` | Maximum entries in the synthetic template cache (LRU); used only with `preparedStatements: true` |
39
41
  | `planCacheMode` | `"auto" \| "force_custom_plan" \| "force_generic_plan"` | `"force_custom_plan"` | PostgreSQL `plan_cache_mode` for every connection |
40
42
 
41
- `planCacheMode` and `statementTimeout` merge into one `options` startup parameter, with adapter settings taking precedence over matching URL settings. Other URL settings are preserved. With PG* environment variables and no URL, the adapter sends the same startup options through Bun's `connection` constructor option. Client-side libpq options such as `connect_timeout` must never go in the URL — bun:sql forwards unrecognised URL parameters to the server, which rejects them. (The same forwarding is how a server setting reaches every connection: `?jit=off` on the URL disables JIT compilation for the pool — see `docs/dialect-notes.md` "PostgreSQL JIT and statistics".)
43
+ `planCacheMode` and `statementTimeout` merge into one `options` startup parameter, with adapter settings taking precedence over matching URL settings. Other URL settings are preserved. bun:sql forwards unrecognised URL parameters to the server, which rejects a client-side libpq option. The adapter therefore takes libpq's `connect_timeout` (whole seconds) out of the URL and uses it as `connectionTimeout`, so a URL written for node-postgres or `psql` connects here too; an explicit `connectionTimeout` option wins. Other client-side libpq options must not go in the URL. (The same forwarding is how a server setting reaches every connection: `?jit=off` on the URL disables JIT compilation for the pool — see `docs/dialect-notes.md` "PostgreSQL JIT and statistics".)
42
44
 
43
45
  `connectionTimeout` does **not** bound waiting for a busy pool connection. The adapter therefore declares `budgets.acquireTimeout: false`. Newer Bun versions support cancellable explicit reservations, but implicit query and transaction checkout does not expose the same guarantee across supported versions. There is no timer race that leaves an abandoned checkout running.
44
46
 
45
47
  `Date` parameters are spelled as UTC ISO text on both paths: bun:sql's own serializer for a `date`-typed parameter is `Date#toString()`, which the server rejects in every host time zone. Zone-less columns (`@db.Timestamp(n)`, `@db.Date`) therefore hold the UTC wall-clock on write — the contract shared with node-postgres, PGlite and Prisma. On read, bun:sql is UTC from **Bun 1.4.1**; earlier Bun versions parse zone-less timestamp text as host-local time (1.3.x: scalars on parameterless statements and every array element; 1.4.0: array elements), and bun:sql has no per-query parser hook the adapter could use to correct it. On Bun < 1.4.1 keep the process on `TZ=UTC` when the schema has zone-less columns, or upgrade (`docs/dialect-notes.md` "DateTime without time zone on postgres").
46
48
 
49
+ ## Statements and values
50
+
51
+ By default the adapter runs bun:sql with `prepare: false`: every statement is an **unnamed** prepared statement, the server plans it from scratch, and results come back in PostgreSQL's text format. That is how node-postgres works, and it is what keeps the two postgres adapters interchangeable. Parameters are spelled the way node-postgres spells them: numbers, bigints and booleans as text, `Date` as UTC ISO text, plain objects as `JSON.stringify` text. bun:sql would otherwise bind a JS number as `int4` or `float8` and let the server cast from that type — `0.1` into an `int4` slot was stored as `0`, `0.30000000000000004` into `numeric` became `0.3`, and `WHERE "real_column" = 0.1` matched nothing. With text the server reads the literal as the slot's own type, exactly or with SQLSTATE `22P02`. `tests/node-postgres-parity.live.test.ts` pins each of these against a live server.
52
+
53
+ `preparedStatements: true` switches bun:sql to **named** statements: one per SQL text per connection, pipelined, with results in the binary format for some types. On the repository benchmark (Bun 1.4.3, PostgreSQL 18, small scale, 27 scenarios, three interleaved rounds) the default mode, named mode and adapter-pg were within 3 % of each other (geometric mean of the median p50s); single rounds varied by up to 10 %, so there is no measured speed reason to turn named statements on where the database is local. Across a network, named mode saves round trips because Bun pipelines only named statements: transaction control (`BEGIN`, `SAVEPOINT`, a deadline bound) rides with the next read, and concurrent statements inside one transaction share a round trip (a one-read `$transaction` costs 2 round trips instead of 3; `tests/round-trip-wire.live.test.ts` pins both modes). Named mode also changes behaviour:
54
+
55
+ - After DDL that changes a statement's result columns (`ALTER COLUMN … TYPE`, or `ADD COLUMN` under `SELECT *`), that statement fails with SQLSTATE `0A000` ("cached plan must not change result type") on every connection that prepared it, until the pool is replaced. Run migrations with the application stopped, or restart it after. node-postgres named statements behave the same way.
56
+ - `int4[]` and `float4[]` values with a NULL element or more than one dimension are refused by bun:sql (`ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET`, `ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET`).
57
+ - A string bound to a raw `::jsonb` slot is JSON-encoded again (see "Raw parameters").
58
+ - A plain object bound to a raw slot that is not `json`/`jsonb` (for example `$1::text`) arrives as `[object Object]`; the default mode and node-postgres send its `JSON.stringify` text.
59
+
47
60
  ## Raw parameters
48
61
 
49
- Trusted parameterless SQL scripts remain supported. The raw result contains the final command's rows and affected-row count; preceding commands still execute once. Empty final DDL results remain empty. The first ORM query observes Bun's JSON wire format. A failed or unrecognized observation rejects that operation before its user statement is sent; a later call can probe again on a healthy connection.
62
+ Trusted parameterless SQL scripts remain supported. The raw result contains the final command's rows and affected-row count; preceding commands still execute once. Empty final DDL results remain empty. The first ORM query observes Bun's JSON wire format, on the connection it already holds. A failed or unrecognized observation rejects that operation before its user statement is sent; a later call can probe again on a healthy connection.
63
+
64
+ Raw parameters follow node-postgres: a JS **array** becomes a PostgreSQL array literal (for `= ANY($1)`), any other plain object is sent as `JSON.stringify` text, and a string is sent as text, so the server parses JSON text bound to a `::jsonb` slot. With `preparedStatements: true`, bun:sql types each parameter by the OID the server infers for its slot and JSON-encodes **whatever JavaScript value** is bound to a `json`/`jsonb` slot, so pre-encoded text becomes a JSON *string*. Measured on Bun 1.4.3 / PostgreSQL 18 (`tests/raw-jsonb.live.test.ts` here and in `@vibeorm/adapter-pg`):
50
65
 
51
- bun:sql types every parameter by the OID the server infers for its slot, and for a `json`/`jsonb` slot it JSON-encodes **whatever JavaScript value is bound** — an object becomes the document, and a string that already holds JSON text becomes a JSON *string* (double-encoded). node-postgres sends a string as text regardless of the slot, so the same raw statement stores the document there. Measured on Bun 1.4.3 / PostgreSQL 18 (`tests/raw-jsonb.live.test.ts` here and in `@vibeorm/adapter-pg`):
66
+ | `$executeRaw` … `VALUES (${value}::jsonb)` | adapter-bun (default) | adapter-bun, `preparedStatements: true` | adapter-pg |
67
+ |---|---|---|---|
68
+ | `{ a: 1 }` (JS object) | `object` | `object` | `object` |
69
+ | `'{"a":1}'` (pre-encoded text) | `object` | **`string`** | `object` |
70
+ | `'{"a":1}'` through `${value}::text::jsonb` | `object` | `object` | `object` |
52
71
 
53
- | `$executeRaw` … `VALUES (${value}::jsonb)` | adapter-bun stores | adapter-pg stores |
54
- |---|---|---|
55
- | `{ a: 1 }` (JS object) | `object` | `object` |
56
- | `'{"a":1}'` (pre-encoded text) | **`string`** | `object` |
57
- | `'{"a":1}'` through `${value}::text::jsonb` | `object` | `object` |
72
+ `::text::jsonb` is the spelling that is correct in every mode, and it is what the ORM path renders for every `Json` field (`jsonParamCast` in the postgres dialect). A JSON array bound into a json column from raw SQL needs `JSON.stringify` plus the `::text::jsonb` spelling, because a raw JS array is an array literal.
58
73
 
59
- Bind the object itself, or spell the slot `::text::jsonb` when you bind pre-encoded text — that is the portable form, and it is what the ORM path renders for every `Json` field (`jsonParamCast` in the postgres dialect), which is why `Json` columns behave identically on both adapters. A JS **array** on the raw path is rendered as a PostgreSQL array literal (for `= ANY($1)`), so a JSON array bound into a json column from raw SQL needs `JSON.stringify` plus the `::text::jsonb` spelling.
74
+ Raw results are the driver's own values. bun:sql returns some types differently from node-postgres in raw queries: `uuid[]` and `citext[]` as the array-literal string (`"{…}"`), `interval`, `point` and `circle` as PostgreSQL's text (`"1 day 02:00:00"`, `"(1,2)"`; node-postgres returns objects), and `xid` as a number. The ORM path decodes list fields the same way on both adapters. One difference reaches the ORM path too: bun:sql reads a date or timestamp before year 1 (`BC`) as `Invalid Date`, where node-postgres returns the date.
60
75
 
61
76
  ## Session lifetime on `$disconnect()`
62
77
 
63
- `disconnect()` rolls back an open raw transaction, releases its reserved connection and closes the pool with bun:sql's `close()`, which waits for in-flight statements to finish. The promise resolves once the client sockets are shut; PostgreSQL removes the sessions from `pg_stat_activity` a few milliseconds later (0–5 ms measured on Bun 1.4.3, the same window node-postgres' `pool.end()` has). `tests/session-lifetime.live.test.ts` pins that every session the adapter can hold — pool, options-transaction connection, raw-transaction session — is gone after `disconnect()`, with a bounded poll.
78
+ `disconnect()` ends the adapter for good: every later call refuses with `VIBE_ADAPTER_CLOSED`, and only a new adapter connects again. It first waits for admitted work (statements, whole transaction and `withSession` callbacks, whose private session pools close on their own), then rolls back an open raw transaction, releases its reserved connection and closes the pool with bun:sql's `close()`. The promise resolves once the client sockets are shut; PostgreSQL removes the sessions from `pg_stat_activity` a few milliseconds later (0–5 ms measured on Bun 1.4.3, the same window node-postgres' `pool.end()` has). `tests/session-lifetime.live.test.ts` pins that every session the adapter can hold — pool, options-transaction connection, raw-transaction session — is gone after `disconnect()`, with a bounded poll.
64
79
 
65
80
  Two things that are NOT the adapter's doing but look like a leak:
66
81
 
67
82
  - A `DROP DATABASE` issued from another connection in the same millisecond can still see a closing session and refuse with SQLSTATE 55006. Poll `pg_stat_activity` for your `application_name` first, or use `DROP DATABASE … WITH (FORCE)` (PostgreSQL 13+).
68
- - bun:sql never closes idle connections by default (`idleTimeout: 0`), whereas node-postgres reaps an idle client after 10 s. An adapter you never `disconnect()` therefore holds its sessions for the life of the process on bun:sql, where node-postgres would have quietly dropped them.
83
+ - bun:sql never closes idle connections by default (`idleTimeout: 0`), whereas node-postgres reaps an idle client after 10 s. An adapter you never `disconnect()` therefore holds its sessions for the life of the process on bun:sql, where node-postgres would have quietly dropped them. The adapter does not offer bun:sql's `idleTimeout`: measured on Bun 1.4.1 and 1.4.3, it also closes a reserved connection and a connection inside a transaction that waits between two statements ("Idle timeout reached"), which node-postgres never does.
69
84
 
70
85
  ## Runtime support
71
86
 
72
87
  | Runtime | Supported |
73
88
  |---|---|
74
- | Bun ≥ 1.2 | yes |
89
+ | Bun ≥ 1.4.1 | yes |
90
+ | Bun < 1.4.1 | no — `bunAdapter()` throws `VIBE_CONFIG` |
75
91
  | Node | no — `bun:sql` is a Bun built-in |
76
92
 
93
+ Older bun:sql releases have data bugs the adapter cannot work around: on 1.3.14 a `COPY … TO STDOUT` hangs the connection, timestamps without a time zone are read in host-local time, `±infinity` dates become `Invalid Date`, and named statements could deliver one query's rows to another (fixed in Bun 1.4). 1.4.1, 1.4.2 and 1.4.3 pass the same probes. The repository's CI runs on the minimum version.
94
+
77
95
  On Node, use [`@vibeorm/adapter-pg`](https://github.com/vibeorm/vibeorm/tree/master/packages/adapter-pg) instead; it speaks the same postgres dialect with the same semantics.
78
96
 
79
97
  ## Capabilities
@@ -82,16 +100,17 @@ This is a postgres-dialect adapter, so the full capability set is available: `RE
82
100
 
83
101
  ## Transactions
84
102
 
85
- A top-level `transaction()` without options uses `sql.begin()` and lets bun:sql drive `BEGIN`/`COMMIT`. With `isolationLevel`, `timeout` or `deadline` the adapter reserves one connection and issues the statements itself, because `begin()` takes no such options.
86
-
87
- Nested transactions become savepoints, chosen by probing the handle:
103
+ Every top-level `transaction()`, with or without options, runs on a reserved connection (`sql.reserve()`). The adapter sends its control (`BEGIN`, `COMMIT`, `SAVEPOINT`, `RELEASE`, the deadline bound) itself, as extended-protocol statements.
88
104
 
89
- - Modern bun:sql exposes `savepoint(fn)` on the in-transaction handle, and the adapter uses it. `begin()` is never called inside a transaction — it throws there ("use savepoint() instead").
90
- - Reserved connections (and older bun:sql builds) expose no `savepoint` helper, so the adapter issues `SAVEPOINT` / `RELEASE SAVEPOINT` / `ROLLBACK TO SAVEPOINT` SQL by hand, with a name counter shared across the whole top-level transaction.
105
+ Nested transactions become savepoints: the adapter issues `SAVEPOINT` / `RELEASE SAVEPOINT` / `ROLLBACK TO SAVEPOINT` SQL by hand on the reserved connection, with a name counter shared across the whole top-level transaction.
91
106
 
92
107
  Nested handles share the root budget and expire when their own callback finishes. Deadline-bearing statements are serialized on the connection, so each checks the remaining time after earlier work completes. Server-cancellation mode installs a fresh `statement_timeout` before each adapter statement and before COMMIT. This does not cancel JavaScript callbacks, network waits or pool acquisition. Trusted raw SQL can change server settings, and a multi-command script applies PostgreSQL's timeout separately to its commands; it is not a hard wall-clock limit for the whole script. Use individual adapter calls when the total deadline must be checked between commands.
93
108
 
94
- If native savepoint recovery cannot be confirmed, the whole transaction is poisoned. Catching that error cannot permit further statements or a successful root commit. A lost COMMIT reply or failed rollback reports `meta.outcome: "unknown"`; no query or transaction callback is replayed. A reserved connection whose root rollback fails is closed rather than returned to the pool.
109
+ If native savepoint recovery cannot be confirmed, the whole transaction is poisoned. Catching that error cannot permit further statements or a successful root commit. A lost COMMIT reply throws `VIBE_TRANSACTION` with `meta.outcome: "unknown"`. So does a COMMIT on a connection that was already dead before it (for example a backend terminated while the callback was still running): bun:sql gives no signal that tells "refused before sending" from "lost in flight", so this adapter cannot report that case as not committed, where adapter-pg can. When the callback fails and the cleanup ROLLBACK fails too (for example the backend was terminated), COMMIT was never sent: the callback's own error is thrown, like node-postgres, the transaction is not committed, and `transactionOutcomeOf({ error })` from `@vibeorm/runtime` reports `{ commit: "not-committed", commitSent: false, rollback: "failed", connection: "discarded" }`; a later use of the handle reports `meta.outcome: "not-committed"`. A deadline that refuses COMMIT stays the deadline error in that case too. No query or transaction callback is replayed. A reserved connection whose root rollback fails is closed rather than returned to the pool. A COMMIT the server answers with an error (a deferred unique or foreign-key constraint, a serialization failure) is a definite rollback and throws that error's own code, for example `VIBE_UNIQUE_VIOLATION`, exactly like node-postgres.
110
+
111
+ A statement with more than 65,535 parameters is refused by bun:sql before it reaches the server; node-postgres sends it and the server aborts the transaction. Inside a transaction the adapter treats that refusal as an abort too, so a callback that catches it cannot commit the transaction's other writes.
112
+
113
+ Two transaction differences from adapter-pg remain by design: a nested-transaction handle used after its own callback has returned is refused here (`VIBE_TRANSACTION`, `reason: "expired"`), and `withSession` runs on a private one-connection pool, so session settings it changes never reach the shared pool.
95
114
 
96
115
  Raw transaction control (`BEGIN` / `COMMIT` / `ROLLBACK` through the raw path, as `@vibeorm/migrate` does) reserves one connection for the duration: bun:sql refuses transaction control through `sql.unsafe()` on a pooled instance (`ERR_POSTGRES_UNSAFE_TRANSACTION`). Concurrent raw transactions on a single adapter are unsupported.
97
116
 
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Refuse a Bun whose bun:sql the adapter does not support (see
3
+ * {@link MINIMUM_BUN_VERSION}), at construction — before any statement can
4
+ * meet one of that driver's known data bugs.
5
+ *
6
+ * @throws VibeError `VIBE_CONFIG`.
7
+ */
8
+ export declare function refuseUnsupportedBun(params: {
9
+ version: string;
10
+ }): void;
11
+ //# sourceMappingURL=bun-version.d.ts.map
package/dist/config.d.ts CHANGED
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Oldest Bun whose bun:sql the adapter accepts. Older releases — measured on
3
+ * 1.3.14 with every adapter fix in place — hang after `COPY … TO STDOUT`,
4
+ * read zone-less timestamps as host-local time and turn ±infinity dates into
5
+ * `Invalid Date`; 1.4.1, 1.4.2 and 1.4.3 pass the same probes.
6
+ */
7
+ export declare const MINIMUM_BUN_VERSION: string;
8
+ /**
9
+ * PostgreSQL's limit on bound parameters in one statement (the Bind message
10
+ * carries a 16-bit count).
11
+ */
12
+ export declare const MAX_BIND_PARAMETERS: number;
1
13
  /** Migration-only driver instance; never force-close the application's pool. */
2
14
  export declare const SESSION_CONNECTION: Readonly<{
3
15
  max: number;
@@ -8,9 +8,10 @@
8
8
  *
9
9
  * v1 lesson (LEARNINGS.md): bun:sql forwards *unrecognised* URL parameters to
10
10
  * the server as runtime configuration, so a libpq client-side option such as
11
- * `connect_timeout` must never be put in the URL (PostgreSQL rejects it as an
12
- * unrecognised configuration parameter). Connection timeouts use bun:sql's
13
- * `connectionTimeout` constructor option instead.
11
+ * `connect_timeout` reaches PostgreSQL, which rejects it as an unrecognised
12
+ * configuration parameter (42704). A URL written for libpq or node-postgres
13
+ * still works here: {@link takeConnectTimeout} removes `connect_timeout` and
14
+ * hands its seconds to bun:sql's `connectionTimeout` constructor option.
14
15
  */
15
16
  /**
16
17
  * PostgreSQL plan-cache behaviour for the implicit prepared statements bun:sql
@@ -29,6 +30,20 @@ export declare function buildStartupOptions(params: {
29
30
  planCacheMode: PlanCacheMode;
30
31
  statementTimeout?: number;
31
32
  }): string | undefined;
33
+ /**
34
+ * Remove libpq's client-side `connect_timeout` (whole seconds) from a URL and
35
+ * return its value, so the URL bun:sql forwards to the server no longer carries
36
+ * it. `0` or a negative value means "no limit" in libpq and yields
37
+ * `undefined`. A URL bun cannot parse is returned unchanged.
38
+ *
39
+ * @throws VibeError `VIBE_VALIDATION` when the value is not an integer.
40
+ */
41
+ export declare function takeConnectTimeout(params: {
42
+ url: string;
43
+ }): {
44
+ url: string;
45
+ seconds?: number;
46
+ };
32
47
  /** Merge startup settings into one `options` key; adapter settings take precedence. */
33
48
  export declare function applyStartupOptions(params: {
34
49
  url: string;
package/dist/driver.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import { SQL } from "bun";
2
- /** Internal constructor boundary; keeps the driver double out of the public adapter API. */
2
+ /**
3
+ * Internal constructor boundary; keeps the driver double out of the public
4
+ * adapter API. Always the URL form: bun:sql's options-only form fills the
5
+ * whole connection from the environment.
6
+ */
3
7
  export declare function createBunSql(params: {
4
- url?: string;
8
+ url: string;
5
9
  options: Record<string, unknown>;
6
10
  }): SQL;
7
11
  //# sourceMappingURL=driver.d.ts.map
package/dist/index.d.ts CHANGED
@@ -10,8 +10,16 @@ import type { DatabaseAdapter } from "@vibeorm/runtime";
10
10
  import { type PlanCacheMode } from "./connection-url.ts";
11
11
  /** Options for {@link bunAdapter}. */
12
12
  export type BunAdapterOptions = {
13
- /** PostgreSQL connection URL. Defaults to `process.env.DATABASE_URL`, then bun's `PG*` env vars. */
14
- readonly url?: string;
13
+ /**
14
+ * PostgreSQL connection URL — required. The adapter never reads
15
+ * `DATABASE_URL` (or any other environment variable) itself, and always
16
+ * hands bun:sql this URL, so bun's own environment lookup (`POSTGRES_URL`,
17
+ * `DATABASE_URL`, `PGHOST`, …) never chooses the server. A database named in
18
+ * the URL path is pinned explicitly, because bun:sql would otherwise let
19
+ * `PGDATABASE` replace it. Give a complete URL: bun:sql fills parts the URL
20
+ * omits (user, database, …) from the `PG*` variables.
21
+ */
22
+ readonly url: string;
15
23
  /** Maximum pooled connections (default 10). */
16
24
  readonly max?: number;
17
25
  /**
@@ -22,15 +30,26 @@ export type BunAdapterOptions = {
22
30
  readonly statementTimeout?: number;
23
31
  /**
24
32
  * Milliseconds to wait for a connection to be established. Passed to bun:sql
25
- * as `connectionTimeout` (which expects **seconds**; converted here).
33
+ * as `connectionTimeout` (which expects **seconds**; converted here). Wins
34
+ * over a libpq `connect_timeout` in the URL, which is otherwise honoured.
26
35
  */
27
36
  readonly connectionTimeout?: number;
28
37
  /**
29
- * Execute ORM-path queries as tagged templates, creating named prepared
30
- * statements (default false). Saves planning time but lets PostgreSQL fall
31
- * back to a generic plan after ~5 executions — leave off unless profiling
32
- * says otherwise. bun:sql's `prepare: false` constructor option is never set
33
- * (it triggers a known bun:sql performance regression).
38
+ * Use bun:sql's NAMED prepared statements (default false).
39
+ *
40
+ * Off (the default) runs every statement as an unnamed statement with text
41
+ * results — bun:sql's `prepare: false`, the node-postgres model: values,
42
+ * parameter typing and DDL behaviour match `@vibeorm/adapter-pg`.
43
+ *
44
+ * On, bun:sql keeps one named statement per SQL text per connection,
45
+ * pipelines, and reads results in its binary format, with these driver
46
+ * consequences (README "Statements and values"): a statement whose result
47
+ * type a later DDL change alters fails with SQLSTATE 0A000 on that
48
+ * connection until the pool is replaced (node-postgres named statements
49
+ * behave the same); `int4[]`/`float4[]` values with NULL elements or more
50
+ * than one dimension are refused; a JSON text bound into a raw `::jsonb`
51
+ * slot is stored as a JSON string. ORM-path queries also run as tagged
52
+ * templates.
34
53
  */
35
54
  readonly preparedStatements?: boolean;
36
55
  /** Maximum entries in the synthetic template cache (default 1000, LRU). */
@@ -41,14 +60,17 @@ export type BunAdapterOptions = {
41
60
  /**
42
61
  * Create a VibeORM adapter backed by bun:sql.
43
62
  *
63
+ * Requires an explicit `url`; without one the factory throws `VIBE_CONFIG`
64
+ * before any driver instance or connection exists.
65
+ *
44
66
  * @example
45
67
  * ```ts
46
- * const adapter = bunAdapter({ url: process.env.DATABASE_URL, max: 10 });
68
+ * const adapter = bunAdapter({ url: "postgresql://app:secret@db.internal:5432/app", max: 10 });
47
69
  * await adapter.connect();
48
70
  * const rows = await adapter.execute({ text: 'SELECT * FROM "User" WHERE "id" = $1', values: [1] });
49
71
  * ```
50
72
  */
51
- export declare function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter;
73
+ export declare function bunAdapter(options: BunAdapterOptions): DatabaseAdapter;
52
74
  export { BUN_SQLSTATE_ERROR_CODES, BUN_SQLSTATE_REASONS, bunErrorSqlstate, mapBunDriverError, } from "./errors.ts";
53
75
  export { serializeParams, serializeParamsRaw, toPgArrayLiteral } from "./params.ts";
54
76
  export { applyStartupOptions, buildStartupOptions } from "./connection-url.ts";