@vibeorm/adapter-bun 1.1.4 → 1.1.5

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.
Files changed (3) hide show
  1. package/README.md +34 -4
  2. package/package.json +2 -2
  3. package/src/index.ts +68 -11
package/README.md CHANGED
@@ -35,14 +35,44 @@ const db = VibeClient({
35
35
 
36
36
  ```ts
37
37
  type BunAdapterOptions = {
38
- url?: string; // Connection string (defaults to DATABASE_URL env)
39
- max?: number; // Pool size (default: 20)
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)
40
42
  preparedStatements?: boolean; // Enable prepared statement caching (default: false)
41
- stmtCacheMax?: number; // Max cached prepared statements (default: 1000)
42
- planCacheMode?: string; // PostgreSQL plan_cache_mode (default: "force_custom_plan")
43
+ stmtCacheMax?: number; // Max cached prepared statements (default: 1000)
44
+ planCacheMode?: string; // PostgreSQL plan_cache_mode (default: "force_custom_plan")
43
45
  };
44
46
  ```
45
47
 
48
+ ### Statement timeout
49
+
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`.
51
+
52
+ ```ts
53
+ const adapter = bunAdapter({
54
+ url: "postgres://...",
55
+ statementTimeout: 30000, // 30 seconds
56
+ });
57
+ ```
58
+
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.
60
+
61
+ Transaction-level `timeout` (via `db.$transaction(fn, { timeout })`) overrides this default for that transaction using `SET LOCAL statement_timeout`.
62
+
63
+ ### Connection timeout
64
+
65
+ Limit how long initial TCP connection establishment can take:
66
+
67
+ ```ts
68
+ const adapter = bunAdapter({
69
+ url: "postgres://...",
70
+ connectionTimeout: 5000, // 5 seconds
71
+ });
72
+ ```
73
+
74
+ Injected as `connect_timeout` in the PostgreSQL connection URL (converted to seconds).
75
+
46
76
  ## License
47
77
 
48
78
  [MIT](../../LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeorm/adapter-bun",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "Bun-native database adapter for VibeORM using bun:sql",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -33,7 +33,7 @@
33
33
  "bun": ">=1.1.0"
34
34
  },
35
35
  "dependencies": {
36
- "@vibeorm/runtime": "1.1.4"
36
+ "@vibeorm/runtime": "1.1.5"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
package/src/index.ts CHANGED
@@ -13,6 +13,32 @@ export type BunAdapterOptions = {
13
13
  url?: string;
14
14
  /** Maximum number of connections in the pool (default: 10). */
15
15
  max?: number;
16
+ /**
17
+ * Default statement timeout in milliseconds for all queries (default: none).
18
+ *
19
+ * Sets PostgreSQL's `statement_timeout` as a connection-level startup parameter,
20
+ * so every query on every connection inherits it automatically with zero per-query
21
+ * overhead. Queries exceeding this duration are cancelled by PostgreSQL with
22
+ * SQLSTATE 57014, which VibeORM surfaces as a `VibeTransientError` with code
23
+ * `STATEMENT_TIMEOUT`.
24
+ *
25
+ * Transaction-level `timeout` (via `$transaction` options) overrides this default
26
+ * for the duration of that transaction using `SET LOCAL statement_timeout`.
27
+ *
28
+ * Recommended: Set to a value appropriate for your workload (e.g. 30000 for
29
+ * general use, 250–1000 for latency-sensitive OLTP services).
30
+ */
31
+ statementTimeout?: number;
32
+ /**
33
+ * Maximum time in milliseconds to wait for a connection from the pool (default: none).
34
+ *
35
+ * When set, connection acquisition that exceeds this duration will throw.
36
+ * Prevents indefinite blocking when the pool is exhausted under load.
37
+ *
38
+ * Injected as `connect_timeout` in the PostgreSQL connection URL. Note that
39
+ * bun:sql uses this for initial TCP connection establishment, not pool checkout.
40
+ */
41
+ connectionTimeout?: number;
16
42
  /**
17
43
  * Whether to use named prepared statements for read queries (default: false).
18
44
  *
@@ -123,19 +149,52 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
123
149
  }
124
150
 
125
151
  /**
126
- * Resolve the connection URL, injecting plan_cache_mode if needed.
152
+ * Resolve the connection URL, injecting startup parameters as needed.
127
153
  * Falls back to DATABASE_URL env var when no explicit URL is provided.
154
+ *
155
+ * Injects via the PostgreSQL `options` startup parameter:
156
+ * - plan_cache_mode (unless "auto")
157
+ * - statement_timeout (if configured)
158
+ *
159
+ * Injects as a URL parameter:
160
+ * - connect_timeout (if configured, converted to seconds for PG)
128
161
  */
129
162
  function resolveConnectionUrl(): string | undefined {
130
- if (PLAN_CACHE_MODE === "auto") return options?.url;
163
+ const STATEMENT_TIMEOUT = options?.statementTimeout;
164
+ const CONNECTION_TIMEOUT = options?.connectionTimeout;
165
+
166
+ // Build startup options string (for -c parameters)
167
+ const startupParts: string[] = [];
168
+ if (PLAN_CACHE_MODE !== "auto") {
169
+ startupParts.push(`-c plan_cache_mode=${PLAN_CACHE_MODE}`);
170
+ }
171
+ if (STATEMENT_TIMEOUT !== undefined) {
172
+ startupParts.push(`-c statement_timeout=${Number(STATEMENT_TIMEOUT)}`);
173
+ }
174
+
175
+ const needsUrlMutation = startupParts.length > 0 || CONNECTION_TIMEOUT !== undefined;
176
+ if (!needsUrlMutation) return options?.url;
131
177
 
132
178
  const baseUrl = options?.url ?? process.env.DATABASE_URL;
133
179
  if (!baseUrl) return undefined;
134
180
 
135
- return appendStartupOption({
136
- url: baseUrl,
137
- option: `-c plan_cache_mode=${PLAN_CACHE_MODE}`,
138
- });
181
+ let url = baseUrl;
182
+
183
+ // Inject startup options (-c params)
184
+ if (startupParts.length > 0) {
185
+ url = appendStartupOption({
186
+ url,
187
+ option: startupParts.join(" "),
188
+ });
189
+ }
190
+
191
+ // Inject connect_timeout as a URL parameter (PG uses seconds)
192
+ if (CONNECTION_TIMEOUT !== undefined) {
193
+ const separator = url.includes("?") ? "&" : "?";
194
+ url = `${url}${separator}connect_timeout=${Math.ceil(CONNECTION_TIMEOUT / 1000)}`;
195
+ }
196
+
197
+ return url;
139
198
  }
140
199
 
141
200
  function getSql(): SqlInstance {
@@ -159,12 +218,10 @@ export function bunAdapter(options?: BunAdapterOptions): DatabaseAdapter {
159
218
  const connectionUrl = resolveConnectionUrl();
160
219
  if (connectionUrl) {
161
220
  sqlInstance = new SQL(connectionUrl, sqlOptions) as unknown as SqlInstance;
162
- } else if (options?.url) {
163
- // URL provided but planCacheMode is "auto" — use URL as-is
164
- sqlInstance = new SQL(options.url, sqlOptions) as unknown as SqlInstance;
165
221
  } else {
166
- // No URL — bun:sql will use PG* env vars. plan_cache_mode cannot be
167
- // injected via startup options in this case (would need SET on connect).
222
+ // No URL resolved — bun:sql will use PG* env vars.
223
+ // Startup parameters (plan_cache_mode, statement_timeout) cannot be
224
+ // injected via URL options in this case (would need SET on connect).
168
225
  sqlInstance = new SQL(sqlOptions) as unknown as SqlInstance;
169
226
  }
170
227