@vibeorm/adapter-bun 1.3.0 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,78 +1,91 @@
1
1
  # @vibeorm/adapter-bun
2
2
 
3
- Bun-native database adapter for VibeORM using Bun's built-in `bun:sql` driver.
3
+ > Part of **[VibeORM](https://github.com/vibeorm/vibeorm)** — a type-safe TypeScript ORM for Bun and Node. Prisma-schema or TypeScript-DSL input, a canonical schema IR, a generated client, and a dialect-aware SQL layer over PostgreSQL, PGlite, SQLite and MySQL.
4
4
 
5
- ## Installation
5
+ This adapter runs VibeORM on Bun's built-in `bun:sql` PostgreSQL driver, including its internal connection pool. It has no external driver dependency and runs on Bun only.
6
6
 
7
7
  ```bash
8
- bun add @vibeorm/adapter-bun
8
+ bun add @vibeorm/adapter-bun@alpha
9
9
  ```
10
10
 
11
- ## Usage
11
+ > **Pre-release.** `2.0.0-alpha.x`, published under the `alpha` dist-tag. `npm i vibeorm` still resolves to the stable v1 line.
12
12
 
13
- The generated client uses this adapter by default. You can also create one manually:
13
+ ## Usage
14
14
 
15
15
  ```ts
16
+ import { VibeClient } from "./generated/vibeorm/index.js";
16
17
  import { bunAdapter } from "@vibeorm/adapter-bun";
17
18
 
18
- const adapter = bunAdapter({
19
- url: "postgres://user:pass@localhost:5432/mydb",
20
- max: 20,
21
- });
22
- ```
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 }) });
23
21
 
24
- Then pass it to the client:
25
-
26
- ```ts
27
- import { VibeClient } from "./generated/vibeorm/index.ts";
22
+ const users = await db.user.findMany({ where: { email: { contains: "@example.com" } }, take: 10 });
28
23
 
29
- const db = VibeClient({
30
- adapter: bunAdapter({ url: "postgres://..." }),
31
- });
24
+ await db.$disconnect();
32
25
  ```
33
26
 
34
27
  ## Options
35
28
 
36
- ```ts
37
- type BunAdapterOptions = {
38
- url?: string; // Connection string (defaults to DATABASE_URL env)
39
- max?: number; // Pool size (default: 10)
40
- statementTimeout?: number; // Default query timeout in ms (default: none)
41
- connectionTimeout?: number; // Max time to establish a connection in ms (default: none)
42
- preparedStatements?: boolean; // Enable prepared statement caching (default: false)
43
- stmtCacheMax?: number; // Max cached prepared statements (default: 1000)
44
- planCacheMode?: string; // PostgreSQL plan_cache_mode (default: "force_custom_plan")
45
- };
46
- ```
29
+ `bunAdapter(options?: BunAdapterOptions)`:
47
30
 
48
- ### Statement timeout
31
+ | Option | Type | Default | Meaning |
32
+ |---|---|---|---|
33
+ | `url` | `string` | `process.env.DATABASE_URL`, then bun's `PG*` env vars | PostgreSQL connection URL |
34
+ | `max` | `number` | `10` | Maximum pooled connections |
35
+ | `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. PostgreSQL may fall back to a generic plan after ~5 executions — leave off unless profiling says otherwise |
38
+ | `stmtCacheMax` | `number` | `1000` | Maximum entries in the synthetic template cache (LRU) |
39
+ | `planCacheMode` | `"auto" \| "force_custom_plan" \| "force_generic_plan"` | `"force_custom_plan"` | PostgreSQL `plan_cache_mode` for every connection |
49
40
 
50
- Set a default timeout for all queries. Any query exceeding this duration is cancelled by PostgreSQL (SQLSTATE `57014`), surfaced as a `VibeTransientError` with code `STATEMENT_TIMEOUT`.
41
+ `planCacheMode` and `statementTimeout` travel in the `options` startup parameter appended to the connection URL, so they apply to every pooled connection without a per-query round trip. 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.
51
42
 
52
- ```ts
53
- const adapter = bunAdapter({
54
- url: "postgres://...",
55
- statementTimeout: 30000, // 30 seconds
56
- });
57
- ```
43
+ ## Runtime support
58
44
 
59
- Injected as a PostgreSQL connection-level startup parameter (`-c statement_timeout=N`), so every query on every pooled connection inherits it automatically with zero per-query overhead.
45
+ | Runtime | Supported |
46
+ |---|---|
47
+ | Bun ≥ 1.2 | yes |
48
+ | Node | no — `bun:sql` is a Bun built-in |
60
49
 
61
- Transaction-level `timeout` (via `db.$transaction(fn, { timeout })`) overrides this default for that transaction using `SET LOCAL statement_timeout`.
50
+ 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.
62
51
 
63
- ### Connection timeout
52
+ ## Capabilities
64
53
 
65
- Limit how long initial TCP connection establishment can take:
54
+ This is a postgres-dialect adapter, so the full capability set is available: `RETURNING`, scalar array columns, native enums, `ILIKE`, `DISTINCT ON`, partial indexes, transactional DDL, `createManyAndReturn`, a real JSON column type, native `NULLS FIRST`/`NULLS LAST`, and the `"join"` relation strategy (`LEFT JOIN LATERAL` plus JSON aggregation). The generated capability table lives in [docs/dialects.md](https://github.com/vibeorm/vibeorm/blob/master/docs/dialects.md).
66
55
 
67
- ```ts
68
- const adapter = bunAdapter({
69
- url: "postgres://...",
70
- connectionTimeout: 5000, // 5 seconds
71
- });
72
- ```
56
+ ## Transactions
57
+
58
+ A top-level `transaction()` without options uses `sql.begin()` and lets bun:sql drive `BEGIN`/`COMMIT`. With `isolationLevel` or `timeout` the adapter reserves one connection and issues the statements itself, because `begin()` takes no such options.
59
+
60
+ Nested transactions become savepoints, chosen by probing the handle:
61
+
62
+ - 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").
63
+ - 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.
64
+
65
+ 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.
66
+
67
+ ## Error mapping
68
+
69
+ Every driver failure becomes a `VibeError` with the driver error as `cause` and `meta` carrying `sqlstate` plus `constraint` / `table` / `column` when reported. bun:sql puts the SQLSTATE on `code` or on `errno` depending on the Bun version; both are read. A non-SQLSTATE `ERR_POSTGRES_*` identifier is kept as `meta.driverCode`. Table (`BUN_SQLSTATE_ERROR_CODES`, frozen and mirrored by tests):
70
+
71
+ | SQLSTATE | Condition | VibeError code | `meta.reason` |
72
+ |---|---|---|---|
73
+ | `23505` | unique_violation | `VIBE_UNIQUE_VIOLATION` | |
74
+ | `23503` | foreign_key_violation | `VIBE_FK_VIOLATION` | |
75
+ | `23502` | not_null_violation | `VIBE_VALIDATION` | |
76
+ | `23514` | check_violation | `VIBE_VALIDATION` | |
77
+ | `22P02` | invalid_text_representation | `VIBE_VALIDATION` | |
78
+ | `40001` | serialization_failure | `VIBE_TRANSACTION` | |
79
+ | `40P01` | deadlock_detected | `VIBE_TRANSACTION` | |
80
+ | `57014` | query_canceled (statement_timeout) | `VIBE_TRANSACTION` | `"timeout"` |
81
+ | anything else, including every `ERR_POSTGRES_*` connection or auth failure | | `VIBE_ADAPTER` | |
73
82
 
74
- Injected as `connect_timeout` in the PostgreSQL connection URL (converted to seconds).
83
+ A transaction callback's own error is rethrown untouched instead of being mapped as a driver failure.
75
84
 
76
- ## License
85
+ ## Links
77
86
 
78
- [MIT](../../LICENSE)
87
+ - [Documentation](https://github.com/vibeorm/vibeorm/tree/master/docs)
88
+ - [Getting started](https://github.com/vibeorm/vibeorm/blob/master/docs/getting-started.md)
89
+ - [Dialect capability tables](https://github.com/vibeorm/vibeorm/blob/master/docs/dialects.md)
90
+ - [Repository and issues](https://github.com/vibeorm/vibeorm)
91
+ - MIT licensed
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Connection-URL handling for bun:sql.
3
+ *
4
+ * Per-connection PostgreSQL settings (`plan_cache_mode`, `statement_timeout`)
5
+ * travel in the `options` startup parameter, which the server parses for
6
+ * `-c key=value` pairs — so every connection in bun's internal pool inherits
7
+ * them without a per-query round trip.
8
+ *
9
+ * v1 lesson (LEARNINGS.md): bun:sql forwards *unrecognised* URL parameters to
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.
14
+ */
15
+ /**
16
+ * PostgreSQL plan-cache behaviour for the implicit prepared statements bun:sql
17
+ * creates. `force_custom_plan` replans with the real parameter values (≈0.1 ms
18
+ * per query) and keeps index selection optimal on range/LIKE/skewed filters —
19
+ * `auto` lets PostgreSQL switch to a generic plan after ~5 executions.
20
+ */
21
+ export type PlanCacheMode = "auto" | "force_custom_plan" | "force_generic_plan";
22
+ /**
23
+ * Build the `options` startup string, or `undefined` when nothing needs setting.
24
+ *
25
+ * @throws VibeError `VIBE_VALIDATION` when `statementTimeout` is not a
26
+ * non-negative integer — the value ends up inside the startup string verbatim.
27
+ */
28
+ export declare function buildStartupOptions(params: {
29
+ planCacheMode: PlanCacheMode;
30
+ statementTimeout?: number;
31
+ }): string | undefined;
32
+ /** Append the `options` startup parameter to a connection URL. */
33
+ export declare function applyStartupOptions(params: {
34
+ url: string;
35
+ startupOptions: string;
36
+ }): string;
37
+ //# sourceMappingURL=connection-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-url.d.ts","sourceRoot":"","sources":["../src/connection-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,mBAAmB,GAAG,oBAAoB,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GAAG,MAAM,GAAG,SAAS,CAerB;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAI3F"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Driver-error mapping for `bun:sql`.
3
+ *
4
+ * bun:sql raises `SQL.PostgresError` for server failures and for client-side
5
+ * problems alike. Server failures carry the PostgreSQL SQLSTATE — depending on
6
+ * the Bun version on `code` or on `errno` — while client-side failures use a
7
+ * `ERR_POSTGRES_*` identifier in `code`. Both shapes are read here, so the
8
+ * mapping is stable across Bun versions. Constitution rule 5: only `VibeError`
9
+ * escapes the adapter.
10
+ */
11
+ import { VibeError, type VibeErrorCode } from "@vibeorm/schema";
12
+ /**
13
+ * PostgreSQL SQLSTATE → stable VibeError code. Frozen and mirrored by tests;
14
+ * anything absent from this table (including every `ERR_POSTGRES_*` connection
15
+ * or auth failure) maps to `VIBE_ADAPTER`.
16
+ */
17
+ export declare const BUN_SQLSTATE_ERROR_CODES: Readonly<Record<string, VibeErrorCode>>;
18
+ /** SQLSTATEs that additionally explain themselves through `meta.reason`. */
19
+ export declare const BUN_SQLSTATE_REASONS: Readonly<Record<string, string>>;
20
+ /**
21
+ * Extract the SQLSTATE from a bun:sql error, accepting it on either `code` or
22
+ * `errno`. `ERR_POSTGRES_*` identifiers are not SQLSTATEs and yield `undefined`.
23
+ */
24
+ export declare function bunErrorSqlstate(params: {
25
+ error: unknown;
26
+ }): string | undefined;
27
+ /**
28
+ * Map any error thrown by bun:sql onto a `VibeError`.
29
+ *
30
+ * A `VibeError` passes through unchanged, so an error raised deeper inside the
31
+ * ORM (or by a nested adapter call) is never re-wrapped.
32
+ */
33
+ export declare function mapBunDriverError(params: {
34
+ error: unknown;
35
+ }): VibeError;
36
+ /** Run a driver call, mapping anything it throws onto a `VibeError`. */
37
+ export declare function runMapped<T>(fn: () => Promise<T>): Promise<T>;
38
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIhE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqB3E,CAAC;AAEH,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEhE,CAAC;AAYH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAS/E;AAID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CA8BvE;AAED,wEAAwE;AACxE,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMnE"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @vibeorm/adapter-bun — bun:sql (PostgreSQL) adapter for VibeORM v2.
3
+ *
4
+ * Uses Bun's built-in SQL driver and its internal connection pool. Owns
5
+ * optional prepared statements (synthetic tagged templates), transactions
6
+ * (nested → savepoints), array-literal parameter formatting and driver-error
7
+ * mapping. Only `VibeError` ever escapes.
8
+ */
9
+ import type { DatabaseAdapter } from "@vibeorm/runtime";
10
+ import { type PlanCacheMode } from "./connection-url.ts";
11
+ /** Options for {@link bunAdapter}. */
12
+ export type BunAdapterOptions = {
13
+ /** PostgreSQL connection URL. Defaults to `process.env.DATABASE_URL`, then bun's `PG*` env vars. */
14
+ readonly url?: string;
15
+ /** Maximum pooled connections (default 10). */
16
+ readonly max?: number;
17
+ /**
18
+ * Pool-wide `statement_timeout` in milliseconds, sent as a startup parameter
19
+ * so every connection inherits it. A per-transaction `timeout` overrides it
20
+ * for that transaction.
21
+ */
22
+ readonly statementTimeout?: number;
23
+ /**
24
+ * Milliseconds to wait for a connection to be established. Passed to bun:sql
25
+ * as `connectionTimeout` (which expects **seconds**; converted here).
26
+ */
27
+ readonly connectionTimeout?: number;
28
+ /**
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).
34
+ */
35
+ readonly preparedStatements?: boolean;
36
+ /** Maximum entries in the synthetic template cache (default 1000, LRU). */
37
+ readonly stmtCacheMax?: number;
38
+ /** PostgreSQL `plan_cache_mode` for every connection (default `force_custom_plan`). */
39
+ readonly planCacheMode?: PlanCacheMode;
40
+ };
41
+ /**
42
+ * Create a VibeORM adapter backed by bun:sql.
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const adapter = bunAdapter({ url: process.env.DATABASE_URL, max: 10 });
47
+ * await adapter.connect();
48
+ * const rows = await adapter.execute({ text: 'SELECT * FROM "User" WHERE "id" = $1', values: [1] });
49
+ * ```
50
+ */
51
+ export declare function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter;
52
+ export { BUN_SQLSTATE_ERROR_CODES, BUN_SQLSTATE_REASONS, bunErrorSqlstate, mapBunDriverError, } from "./errors.ts";
53
+ export { serializeParams, serializeParamsRaw, toPgArrayLiteral } from "./params.ts";
54
+ export { applyStartupOptions, buildStartupOptions } from "./connection-url.ts";
55
+ export type { PlanCacheMode } from "./connection-url.ts";
56
+ export { createSavepointCounter, nextSavepointName } from "./savepoints.ts";
57
+ export type { SavepointCounter } from "./savepoints.ts";
58
+ export { createTemplateCache, templateChunks } from "./template-cache.ts";
59
+ export type { TemplateCache } from "./template-cache.ts";
60
+ export { ISOLATION_LEVEL_SQL, beginStatement, classifyRawTransactionControl, refuseNestedTransactionOptions, statementTimeoutStatement, } from "./transaction-sql.ts";
61
+ export type { RawTransactionControl } from "./transaction-sql.ts";
62
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAiD,MAAM,kBAAkB,CAAC;AAEvG,OAAO,EAA4C,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAwEnG,sCAAsC;AACtC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oGAAoG;IACpG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;CACxC,CAAC;AAsBF;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,eAAe,CA2TvE;AAgBD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,6BAA6B,EAC7B,8BAA8B,EAC9B,yBAAyB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC"}