@vibeorm/adapter-pglite 2.0.0-alpha.1

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 ADDED
@@ -0,0 +1,89 @@
1
+ # @vibeorm/adapter-pglite
2
+
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
+
5
+ This adapter runs VibeORM on [PGlite](https://pglite.dev/), real PostgreSQL compiled to WASM and running in-process. There is no server and no Docker, which makes it the zero-setup default for tests and demos; `@electric-sql/pglite` is a regular dependency, so nothing extra to install.
6
+
7
+ ```bash
8
+ bun add @vibeorm/adapter-pglite@alpha
9
+ ```
10
+
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
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { VibeClient } from "./generated/vibeorm/index.js";
17
+ import { pgliteAdapter } from "@vibeorm/adapter-pglite";
18
+
19
+ // no dataDir → a fresh in-memory database
20
+ const db = VibeClient({ adapter: pgliteAdapter() });
21
+
22
+ const users = await db.user.findMany({ where: { email: { contains: "@example.com" } }, take: 10 });
23
+
24
+ await db.$disconnect();
25
+ ```
26
+
27
+ ```ts
28
+ const persistent = pgliteAdapter({ dataDir: "./.pglite" }); // persisted on disk
29
+ const shared = pgliteAdapter({ pglite: existingInstance }); // reuse a warm instance
30
+ ```
31
+
32
+ `disconnect()` closes an instance the adapter created, and a later call opens a fresh one. With no `dataDir` that fresh instance is a new, empty database.
33
+
34
+ ## Options
35
+
36
+ `pgliteAdapter(options?: PgliteAdapterOptions)`:
37
+
38
+ | Option | Type | Default | Meaning |
39
+ |---|---|---|---|
40
+ | `dataDir` | `string` | — (in-memory) | Directory to persist the database in. PGlite's own prefixes (`idb://`, `memory://`) work here too |
41
+ | `pglite` | `PGliteInterface` | — | An already-created PGlite instance to run on. The adapter never closes an instance it did not create, so `disconnect()` is a no-op then |
42
+
43
+ There is no pool size and no connection URL: PGlite is a single embedded connection.
44
+
45
+ ## Runtime support
46
+
47
+ | Runtime | Supported |
48
+ |---|---|
49
+ | Bun ≥ 1.2 | yes |
50
+ | Node | yes |
51
+
52
+ PGlite is WASM, so it needs no native build and no database server on either runtime.
53
+
54
+ ## Capabilities
55
+
56
+ PGlite speaks the postgres dialect, 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).
57
+
58
+ Two behaviours are specific to running in-process, and both are enforced rather than silent:
59
+
60
+ - **Transaction `timeout` is refused.** PGlite accepts `SET LOCAL statement_timeout` and even reports it back through `SHOW`, but the single-threaded WASM build has no timer interrupt and never cancels a running statement. Passing `timeout` to `$transaction` therefore throws `VIBE_UNSUPPORTED_CAPABILITY` instead of becoming a no-op. `isolationLevel` is applied for real. Use a PostgreSQL server adapter where the option is genuinely enforced.
61
+ - **One connection, one queue.** Every operation is serialized through a single operation queue, and a whole transaction holds that queue until it commits or rolls back. Calling the root client from inside a transaction callback would queue behind the transaction it is running in, so it is refused with `VIBE_TRANSACTION`; use the `tx` client the callback receives.
62
+
63
+ Nested transactions become savepoints on the open transaction, named from a counter shared across the whole top-level transaction.
64
+
65
+ ## Error mapping
66
+
67
+ PGlite runs the real PostgreSQL error protocol in-process, so its errors carry the same SQLSTATE plus `constraint` / `table` / `column`, which end up in `meta`; the driver error is attached as `cause`. Table (`PGLITE_SQLSTATE_ERROR_CODES`, frozen and mirrored by tests):
68
+
69
+ | SQLSTATE | Condition | VibeError code | `meta.reason` |
70
+ |---|---|---|---|
71
+ | `23505` | unique_violation | `VIBE_UNIQUE_VIOLATION` | |
72
+ | `23503` | foreign_key_violation | `VIBE_FK_VIOLATION` | |
73
+ | `23502` | not_null_violation | `VIBE_VALIDATION` | |
74
+ | `23514` | check_violation | `VIBE_VALIDATION` | |
75
+ | `22P02` | invalid_text_representation | `VIBE_VALIDATION` | |
76
+ | `40001` | serialization_failure | `VIBE_TRANSACTION` | |
77
+ | `40P01` | deadlock_detected | `VIBE_TRANSACTION` | |
78
+ | `57014` | query_canceled | `VIBE_TRANSACTION` | `"timeout"` |
79
+ | anything else, including PGlite's own failures (closed database, WASM init) | | `VIBE_ADAPTER` | |
80
+
81
+ A transaction callback's own error propagates untouched; PGlite has already rolled the transaction back by then.
82
+
83
+ ## Links
84
+
85
+ - [Documentation](https://github.com/vibeorm/vibeorm/tree/master/docs)
86
+ - [Getting started](https://github.com/vibeorm/vibeorm/blob/master/docs/getting-started.md)
87
+ - [Dialect capability tables](https://github.com/vibeorm/vibeorm/blob/master/docs/dialects.md)
88
+ - [Repository and issues](https://github.com/vibeorm/vibeorm)
89
+ - MIT licensed
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Driver-error mapping for PGlite.
3
+ *
4
+ * PGlite runs the real PostgreSQL error protocol in-process, so its errors
5
+ * carry the same SQLSTATE (`code`) plus `constraint` / `table` / `column` as a
6
+ * server would. Constitution rule 5: only `VibeError` escapes the adapter.
7
+ */
8
+ import { VibeError, type VibeErrorCode } from "@vibeorm/schema";
9
+ /**
10
+ * PostgreSQL SQLSTATE → stable VibeError code. Frozen and mirrored by tests;
11
+ * anything absent from this table maps to `VIBE_ADAPTER`.
12
+ */
13
+ export declare const PGLITE_SQLSTATE_ERROR_CODES: Readonly<Record<string, VibeErrorCode>>;
14
+ /** SQLSTATEs that additionally explain themselves through `meta.reason`. */
15
+ export declare const PGLITE_SQLSTATE_REASONS: Readonly<Record<string, string>>;
16
+ /**
17
+ * Extract the SQLSTATE from a PGlite error. Failures raised by PGlite itself
18
+ * (closed database, WASM init) carry no SQLSTATE and yield `undefined`.
19
+ */
20
+ export declare function pgliteErrorSqlstate(params: {
21
+ error: unknown;
22
+ }): string | undefined;
23
+ /**
24
+ * Map any error thrown by PGlite onto a `VibeError`.
25
+ *
26
+ * A `VibeError` passes through unchanged, so an error raised deeper inside the
27
+ * ORM (or by a nested adapter call) is never re-wrapped.
28
+ */
29
+ export declare function mapPgliteDriverError(params: {
30
+ error: unknown;
31
+ }): VibeError;
32
+ /** Run a driver call, mapping anything it throws onto a `VibeError`. */
33
+ export declare function runMapped<T>(fn: () => Promise<T>): Promise<T>;
34
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIhE;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqB9E,CAAC;AAEH,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnE,CAAC;AAYH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAKlF;AAID;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CA0B1E;AAED,wEAAwE;AACxE,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMnE"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @vibeorm/adapter-pglite — PGlite (in-process PostgreSQL, WASM) adapter for
3
+ * VibeORM v2.
4
+ *
5
+ * PGlite is a *single* embedded connection, so this adapter serializes every
6
+ * operation through one queue — including whole transactions, which hold the
7
+ * queue until they commit or roll back. That makes concurrent client calls safe
8
+ * without a pool, and makes an in-memory instance an honest postgres target for
9
+ * tests (no Docker).
10
+ */
11
+ import type { DatabaseAdapter } from "@vibeorm/runtime";
12
+ import type { PGliteInterface } from "@electric-sql/pglite";
13
+ /** Options for {@link pgliteAdapter}. */
14
+ export type PgliteAdapterOptions = {
15
+ /**
16
+ * Directory to persist the database in. Omit for a fresh in-memory database —
17
+ * the usual choice for tests. Prefixes PGlite understands (`idb://`,
18
+ * `memory://`) work here too.
19
+ */
20
+ readonly dataDir?: string;
21
+ /**
22
+ * An already-created PGlite instance to run on (sharing one database across
23
+ * adapters, or reusing a warm instance between test files). The adapter never
24
+ * closes an instance it did not create, so `disconnect()` is a no-op then.
25
+ */
26
+ readonly pglite?: PGliteInterface;
27
+ };
28
+ /**
29
+ * Create a VibeORM adapter backed by PGlite.
30
+ *
31
+ * @example In-memory database (tests)
32
+ * ```ts
33
+ * const adapter = pgliteAdapter();
34
+ * await adapter.executeUnsafe({ text: `CREATE TABLE "User" ("id" serial primary key)` });
35
+ * ```
36
+ *
37
+ * @example Persistent database, or a shared instance
38
+ * ```ts
39
+ * const adapter = pgliteAdapter({ dataDir: "./.pglite" });
40
+ * const shared = pgliteAdapter({ pglite: existingInstance });
41
+ * ```
42
+ *
43
+ * `disconnect()` closes an instance the adapter created; a later call opens a
44
+ * fresh one (lazy reconnect, matching the pool-backed adapters). With no
45
+ * `dataDir` that fresh instance is a new, empty in-memory database.
46
+ */
47
+ export declare function pgliteAdapter(options?: PgliteAdapterOptions): DatabaseAdapter;
48
+ export { PGLITE_SQLSTATE_ERROR_CODES, PGLITE_SQLSTATE_REASONS, mapPgliteDriverError, pgliteErrorSqlstate, } from "./errors.ts";
49
+ export { PgliteArrayParam, serializeParams, serializeParamsRaw, toPgArrayLiteral } from "./params.ts";
50
+ export { createOperationQueue } from "./queue.ts";
51
+ export type { OperationQueue } from "./queue.ts";
52
+ export { createSavepointCounter, nextSavepointName } from "./savepoints.ts";
53
+ export type { SavepointCounter } from "./savepoints.ts";
54
+ export { ISOLATION_LEVEL_SQL, refuseNestedTransactionOptions, setIsolationLevelStatement, } from "./transaction-sql.ts";
55
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAiD,MAAM,kBAAkB,CAAC;AAEvG,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,sBAAsB,CAAC;AASzE,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAkCF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,eAAe,CAiO7E;AAID,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,358 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/adapter-pglite/src/index.ts
5
+ import { AsyncLocalStorage } from "async_hooks";
6
+ import { VibeError as VibeError4 } from "@vibeorm/schema";
7
+
8
+ // packages/adapter-pglite/src/errors.ts
9
+ import { VibeError } from "@vibeorm/schema";
10
+ var PGLITE_SQLSTATE_ERROR_CODES = Object.freeze({
11
+ "23505": "VIBE_UNIQUE_VIOLATION",
12
+ "23503": "VIBE_FK_VIOLATION",
13
+ "23502": "VIBE_VALIDATION",
14
+ "23514": "VIBE_VALIDATION",
15
+ "22P02": "VIBE_VALIDATION",
16
+ "40001": "VIBE_TRANSACTION",
17
+ "40P01": "VIBE_TRANSACTION",
18
+ "57014": "VIBE_TRANSACTION"
19
+ });
20
+ var PGLITE_SQLSTATE_REASONS = Object.freeze({
21
+ "57014": "timeout"
22
+ });
23
+ var SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
24
+ function readStringField(params) {
25
+ const value = params.source[params.key];
26
+ return typeof value === "string" && value.length > 0 ? value : undefined;
27
+ }
28
+ function pgliteErrorSqlstate(params) {
29
+ const { error } = params;
30
+ if (error === null || typeof error !== "object")
31
+ return;
32
+ const code = readStringField({ source: error, key: "code" });
33
+ return code !== undefined && SQLSTATE_PATTERN.test(code) ? code : undefined;
34
+ }
35
+ function mapPgliteDriverError(params) {
36
+ const { error } = params;
37
+ if (error instanceof VibeError)
38
+ return error;
39
+ const source = error !== null && typeof error === "object" ? error : {};
40
+ const sqlstate = pgliteErrorSqlstate({ error });
41
+ const code = (sqlstate === undefined ? undefined : PGLITE_SQLSTATE_ERROR_CODES[sqlstate]) ?? "VIBE_ADAPTER";
42
+ const meta = {};
43
+ if (sqlstate !== undefined)
44
+ meta.sqlstate = sqlstate;
45
+ const constraint = readStringField({ source, key: "constraint" });
46
+ if (constraint !== undefined)
47
+ meta.constraint = constraint;
48
+ const table = readStringField({ source, key: "table" });
49
+ if (table !== undefined)
50
+ meta.table = table;
51
+ const column = readStringField({ source, key: "column" });
52
+ if (column !== undefined)
53
+ meta.column = column;
54
+ const reason = sqlstate === undefined ? undefined : PGLITE_SQLSTATE_REASONS[sqlstate];
55
+ if (reason !== undefined)
56
+ meta.reason = reason;
57
+ const message = readStringField({ source, key: "message" }) ?? (typeof error === "string" ? error : "unknown pglite driver error");
58
+ return new VibeError({ code, message, meta, cause: error });
59
+ }
60
+ async function runMapped(fn) {
61
+ try {
62
+ return await fn();
63
+ } catch (error) {
64
+ throw mapPgliteDriverError({ error });
65
+ }
66
+ }
67
+
68
+ // packages/adapter-pglite/src/params.ts
69
+ import { VibeError as VibeError2 } from "@vibeorm/schema";
70
+
71
+ class PgliteArrayParam {
72
+ values;
73
+ constructor(values) {
74
+ this.values = values;
75
+ }
76
+ }
77
+ function quoteElement(value) {
78
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
79
+ }
80
+ function toPgArrayLiteral(values) {
81
+ const elements = values.map((value) => {
82
+ if (value === null || value === undefined)
83
+ return "NULL";
84
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") {
85
+ return String(value);
86
+ }
87
+ if (value instanceof Date)
88
+ return quoteElement(value.toISOString());
89
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
90
+ throw new VibeError2({
91
+ code: "VIBE_VALIDATION",
92
+ message: "binary values are not supported inside a postgres array literal",
93
+ meta: { driver: "pglite" }
94
+ });
95
+ }
96
+ if (typeof value === "string")
97
+ return quoteElement(value);
98
+ if (typeof value === "object")
99
+ return quoteElement(JSON.stringify(value));
100
+ return quoteElement(String(value));
101
+ });
102
+ return `{${elements.join(",")}}`;
103
+ }
104
+ function isDriverNativeValue(value) {
105
+ return value === null || value === undefined || typeof value !== "object" || value instanceof Date || ArrayBuffer.isView(value) || value instanceof ArrayBuffer;
106
+ }
107
+ function serializeParams(params) {
108
+ return params.values.map((value) => {
109
+ if (value instanceof PgliteArrayParam)
110
+ return toPgArrayLiteral(value.values);
111
+ if (isDriverNativeValue(value))
112
+ return value;
113
+ return JSON.stringify(value);
114
+ });
115
+ }
116
+ function serializeParamsRaw(params) {
117
+ return params.values.map((value) => {
118
+ if (value instanceof PgliteArrayParam)
119
+ return toPgArrayLiteral(value.values);
120
+ if (Array.isArray(value))
121
+ return value;
122
+ if (isDriverNativeValue(value))
123
+ return value;
124
+ return JSON.stringify(value);
125
+ });
126
+ }
127
+
128
+ // packages/adapter-pglite/src/queue.ts
129
+ function createOperationQueue() {
130
+ let tail = Promise.resolve();
131
+ return {
132
+ run(job) {
133
+ const result = tail.then(job, job);
134
+ tail = result.then(() => {
135
+ return;
136
+ }, () => {
137
+ return;
138
+ });
139
+ return result;
140
+ }
141
+ };
142
+ }
143
+
144
+ // packages/adapter-pglite/src/savepoints.ts
145
+ function createSavepointCounter() {
146
+ return { n: 0 };
147
+ }
148
+ function nextSavepointName(params) {
149
+ return `vibeorm_sp_${params.counter.n++}`;
150
+ }
151
+
152
+ // packages/adapter-pglite/src/transaction-sql.ts
153
+ import { VibeError as VibeError3 } from "@vibeorm/schema";
154
+ var ISOLATION_LEVEL_SQL = Object.freeze({
155
+ ReadCommitted: "READ COMMITTED",
156
+ RepeatableRead: "REPEATABLE READ",
157
+ Serializable: "SERIALIZABLE"
158
+ });
159
+ function setIsolationLevelStatement(params) {
160
+ return `SET TRANSACTION ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[params.isolationLevel]}`;
161
+ }
162
+ function refuseNestedTransactionOptions(params) {
163
+ const { options, provider } = params;
164
+ if (options === undefined)
165
+ return;
166
+ const passed = [];
167
+ if (options.isolationLevel !== undefined)
168
+ passed.push("isolationLevel");
169
+ if (options.timeout !== undefined)
170
+ passed.push("timeout");
171
+ if (passed.length === 0)
172
+ return;
173
+ const meta = { provider, nested: true };
174
+ if (options.isolationLevel !== undefined)
175
+ meta.isolationLevel = options.isolationLevel;
176
+ if (options.timeout !== undefined)
177
+ meta.timeout = options.timeout;
178
+ throw new VibeError3({
179
+ code: "VIBE_VALIDATION",
180
+ message: `nested transactions run as savepoints and cannot honor ${passed.join(" or ")} \u2014 a savepoint cannot change the isolation level of the transaction it joins, and a nested timeout is not enforceable; pass options on the top-level $transaction`,
181
+ meta
182
+ });
183
+ }
184
+
185
+ // packages/adapter-pglite/src/index.ts
186
+ class CallbackError extends Error {
187
+ inner;
188
+ constructor(inner) {
189
+ super("vibeorm: transaction callback failed");
190
+ this.name = "CallbackError";
191
+ this.inner = inner;
192
+ }
193
+ }
194
+ var DIALECT = "postgres";
195
+ var PROVIDER = "pglite";
196
+ var WIRE = Object.freeze({ dateTime: "native", json: "parsed", numbers: "native" });
197
+ function pgliteAdapter(options) {
198
+ const queue = createOperationQueue();
199
+ const ownsInstance = options?.pglite === undefined;
200
+ const txContext = new AsyncLocalStorage;
201
+ function assertNotInsideTransaction() {
202
+ if (txContext.getStore() === true) {
203
+ throw new VibeError4({
204
+ code: "VIBE_TRANSACTION",
205
+ message: "root client used inside a transaction callback \u2014 use the tx client the callback receives; the root connection would deadlock behind the open transaction",
206
+ meta: { provider: PROVIDER }
207
+ });
208
+ }
209
+ }
210
+ let instance = options?.pglite ?? null;
211
+ let pending = null;
212
+ async function getInstance() {
213
+ if (instance !== null)
214
+ return instance;
215
+ if (pending === null) {
216
+ pending = runMapped(async () => {
217
+ const { PGlite } = await import("@electric-sql/pglite");
218
+ const created = new PGlite(options?.dataDir);
219
+ await created.waitReady;
220
+ return created;
221
+ });
222
+ }
223
+ try {
224
+ instance = await pending;
225
+ return instance;
226
+ } catch (error) {
227
+ pending = null;
228
+ throw error;
229
+ }
230
+ }
231
+ async function runExecute(params) {
232
+ const values = serializeParams({ values: params.values });
233
+ const result = await runMapped(() => params.queryable.query(params.text, values));
234
+ return result.rows;
235
+ }
236
+ async function runExecuteUnsafe(params) {
237
+ if (params.values === undefined) {
238
+ const results = await runMapped(() => params.queryable.exec(params.text));
239
+ const last = results.at(-1);
240
+ return { rows: last?.rows ?? [], affectedRows: last?.affectedRows ?? 0 };
241
+ }
242
+ const values = serializeParamsRaw({ values: params.values });
243
+ const result = await runMapped(() => params.queryable.query(params.text, values));
244
+ return { rows: result.rows, affectedRows: result.affectedRows ?? 0 };
245
+ }
246
+ function createTransactionAdapter(params) {
247
+ const { tx, savepointCounter } = params;
248
+ return {
249
+ dialect: DIALECT,
250
+ provider: PROVIDER,
251
+ wire: WIRE,
252
+ async execute(execParams) {
253
+ return runExecute({ queryable: tx, ...execParams });
254
+ },
255
+ async executeUnsafe(execParams) {
256
+ return runExecuteUnsafe({ queryable: tx, ...execParams });
257
+ },
258
+ async transaction(fn, nestedOptions) {
259
+ refuseNestedTransactionOptions({ options: nestedOptions, provider: PROVIDER });
260
+ const savepoint = nextSavepointName({ counter: savepointCounter });
261
+ await runMapped(() => tx.query(`SAVEPOINT ${savepoint}`));
262
+ try {
263
+ const result = await fn(createTransactionAdapter({ tx, savepointCounter }));
264
+ await runMapped(() => tx.query(`RELEASE SAVEPOINT ${savepoint}`));
265
+ return result;
266
+ } catch (error) {
267
+ try {
268
+ await tx.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);
269
+ } catch {}
270
+ throw error;
271
+ }
272
+ },
273
+ async connect() {},
274
+ async disconnect() {},
275
+ formatArrayParam(values) {
276
+ return new PgliteArrayParam(values);
277
+ }
278
+ };
279
+ }
280
+ return {
281
+ dialect: DIALECT,
282
+ provider: PROVIDER,
283
+ wire: WIRE,
284
+ async execute(params) {
285
+ assertNotInsideTransaction();
286
+ const db = await getInstance();
287
+ return queue.run(() => runExecute({ queryable: db, ...params }));
288
+ },
289
+ async executeUnsafe(params) {
290
+ assertNotInsideTransaction();
291
+ const db = await getInstance();
292
+ return queue.run(() => runExecuteUnsafe({ queryable: db, ...params }));
293
+ },
294
+ async transaction(fn, transactionOptions) {
295
+ assertNotInsideTransaction();
296
+ if (transactionOptions?.timeout !== undefined) {
297
+ throw new VibeError4({
298
+ code: "VIBE_UNSUPPORTED_CAPABILITY",
299
+ message: "pglite cannot enforce a transaction timeout: the single-threaded WASM build accepts statement_timeout but never cancels a running statement \u2014 remove the option, or use a postgres server adapter where it is genuinely enforced",
300
+ meta: { provider: PROVIDER, feature: "transactionTimeout", timeout: transactionOptions.timeout }
301
+ });
302
+ }
303
+ const db = await getInstance();
304
+ const savepointCounter = createSavepointCounter();
305
+ return queue.run(async () => {
306
+ try {
307
+ return await db.transaction(async (tx) => {
308
+ if (transactionOptions?.isolationLevel !== undefined) {
309
+ await tx.query(setIsolationLevelStatement({ isolationLevel: transactionOptions.isolationLevel }));
310
+ }
311
+ try {
312
+ return await txContext.run(true, () => fn(createTransactionAdapter({ tx, savepointCounter })));
313
+ } catch (error) {
314
+ throw new CallbackError(error);
315
+ }
316
+ });
317
+ } catch (error) {
318
+ if (error instanceof CallbackError)
319
+ throw error.inner;
320
+ throw mapPgliteDriverError({ error });
321
+ }
322
+ });
323
+ },
324
+ async connect() {
325
+ await getInstance();
326
+ },
327
+ async disconnect() {
328
+ if (!ownsInstance || instance === null)
329
+ return;
330
+ const closing = instance;
331
+ instance = null;
332
+ pending = null;
333
+ await queue.run(() => runMapped(() => closing.close()));
334
+ },
335
+ formatArrayParam(values) {
336
+ return new PgliteArrayParam(values);
337
+ }
338
+ };
339
+ }
340
+ export {
341
+ toPgArrayLiteral,
342
+ setIsolationLevelStatement,
343
+ serializeParamsRaw,
344
+ serializeParams,
345
+ refuseNestedTransactionOptions,
346
+ pgliteErrorSqlstate,
347
+ pgliteAdapter,
348
+ nextSavepointName,
349
+ mapPgliteDriverError,
350
+ createSavepointCounter,
351
+ createOperationQueue,
352
+ PgliteArrayParam,
353
+ PGLITE_SQLSTATE_REASONS,
354
+ PGLITE_SQLSTATE_ERROR_CODES,
355
+ ISOLATION_LEVEL_SQL
356
+ };
357
+
358
+ //# debugId=7A12A0ECD3D2E7B564756E2164756E21
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/errors.ts", "../src/params.ts", "../src/queue.ts", "../src/savepoints.ts", "../src/transaction-sql.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @vibeorm/adapter-pglite — PGlite (in-process PostgreSQL, WASM) adapter for\n * VibeORM v2.\n *\n * PGlite is a *single* embedded connection, so this adapter serializes every\n * operation through one queue — including whole transactions, which hold the\n * queue until they commit or roll back. That makes concurrent client calls safe\n * without a pool, and makes an in-memory instance an honest postgres target for\n * tests (no Docker).\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { VibeError } from \"@vibeorm/schema\";\nimport type { DatabaseAdapter, QueryResult, TransactionOptions, WireFidelity } from \"@vibeorm/runtime\";\nimport type { Dialect, Provider } from \"@vibeorm/schema\";\nimport type { PGliteInterface, Transaction } from \"@electric-sql/pglite\";\nimport { mapPgliteDriverError, runMapped } from \"./errors.ts\";\nimport { PgliteArrayParam, serializeParams, serializeParamsRaw } from \"./params.ts\";\nimport { createOperationQueue } from \"./queue.ts\";\nimport { createSavepointCounter, nextSavepointName, type SavepointCounter } from \"./savepoints.ts\";\nimport { refuseNestedTransactionOptions, setIsolationLevelStatement } from \"./transaction-sql.ts\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\n/** Options for {@link pgliteAdapter}. */\nexport type PgliteAdapterOptions = {\n /**\n * Directory to persist the database in. Omit for a fresh in-memory database —\n * the usual choice for tests. Prefixes PGlite understands (`idb://`,\n * `memory://`) work here too.\n */\n readonly dataDir?: string;\n /**\n * An already-created PGlite instance to run on (sharing one database across\n * adapters, or reusing a warm instance between test files). The adapter never\n * closes an instance it did not create, so `disconnect()` is a no-op then.\n */\n readonly pglite?: PGliteInterface;\n};\n\n// ─── Internals ────────────────────────────────────────────────────\n\n/** Anything that runs SQL: the PGlite instance itself or an open transaction. */\ntype Queryable = Pick<Transaction, \"query\" | \"exec\">;\n\n/**\n * Wrapper that marks an error as \"thrown by the user's transaction callback\",\n * so it can be rethrown untouched instead of being mapped as a driver failure\n * after PGlite has passed it back out of `transaction()`.\n */\nclass CallbackError extends Error {\n readonly inner: unknown;\n\n constructor(inner: unknown) {\n super(\"vibeorm: transaction callback failed\");\n this.name = \"CallbackError\";\n this.inner = inner;\n }\n}\n\nconst DIALECT: Dialect = \"postgres\";\nconst PROVIDER: Provider = \"pglite\";\n\n/**\n * pglite wire (board #30, live-verified on both execute paths): timestamptz →\n * `Date`, json/jsonb → parsed values, int4/float8 → numbers. (int8 arrives as\n * a native `bigint` here — the BigInt codec passes it through; not declared.)\n */\nconst WIRE: WireFidelity = Object.freeze({ dateTime: \"native\", json: \"parsed\", numbers: \"native\" } as const);\n\n// ─── Adapter ──────────────────────────────────────────────────────\n\n/**\n * Create a VibeORM adapter backed by PGlite.\n *\n * @example In-memory database (tests)\n * ```ts\n * const adapter = pgliteAdapter();\n * await adapter.executeUnsafe({ text: `CREATE TABLE \"User\" (\"id\" serial primary key)` });\n * ```\n *\n * @example Persistent database, or a shared instance\n * ```ts\n * const adapter = pgliteAdapter({ dataDir: \"./.pglite\" });\n * const shared = pgliteAdapter({ pglite: existingInstance });\n * ```\n *\n * `disconnect()` closes an instance the adapter created; a later call opens a\n * fresh one (lazy reconnect, matching the pool-backed adapters). With no\n * `dataDir` that fresh instance is a new, empty in-memory database.\n */\nexport function pgliteAdapter(options?: PgliteAdapterOptions): DatabaseAdapter {\n const queue = createOperationQueue();\n const ownsInstance = options?.pglite === undefined;\n\n /**\n * Deadlock guard (review F6): PGlite is a single connection, and a whole\n * transaction holds the operation queue — so a ROOT-adapter call issued from\n * inside a `transaction()` callback would queue behind the very transaction\n * it is running in and hang forever. The callback runs inside this context;\n * root entry points refuse instead of deadlocking. The tx-bound adapter\n * bypasses the queue and stays fully usable.\n */\n const txContext = new AsyncLocalStorage<true>();\n\n function assertNotInsideTransaction(): void {\n if (txContext.getStore() === true) {\n throw new VibeError({\n code: \"VIBE_TRANSACTION\",\n message:\n \"root client used inside a transaction callback — use the tx client the callback receives; the root connection would deadlock behind the open transaction\",\n meta: { provider: PROVIDER },\n });\n }\n }\n\n let instance: PGliteInterface | null = options?.pglite ?? null;\n let pending: Promise<PGliteInterface> | null = null;\n\n async function getInstance(): Promise<PGliteInterface> {\n if (instance !== null) return instance;\n if (pending === null) {\n pending = runMapped(async () => {\n const { PGlite } = await import(\"@electric-sql/pglite\");\n const created = new PGlite(options?.dataDir);\n await created.waitReady;\n return created;\n });\n }\n try {\n instance = await pending;\n return instance;\n } catch (error) {\n // Let the next caller retry instead of caching the failure forever.\n pending = null;\n throw error;\n }\n }\n\n async function runExecute(params: {\n queryable: Queryable;\n text: string;\n values: unknown[];\n }): Promise<Record<string, unknown>[]> {\n const values = serializeParams({ values: params.values });\n const result = await runMapped(() =>\n params.queryable.query<Record<string, unknown>>(params.text, values),\n );\n return result.rows;\n }\n\n /**\n * Raw path. Without parameters the statement goes through `exec()` (simple\n * protocol) so multi-statement DDL — what @vibeorm/migrate emits — runs as\n * one call; with parameters it goes through the extended protocol.\n */\n async function runExecuteUnsafe(params: {\n queryable: Queryable;\n text: string;\n values?: unknown[];\n }): Promise<QueryResult> {\n if (params.values === undefined) {\n const results = await runMapped(() => params.queryable.exec(params.text));\n const last = results.at(-1);\n return { rows: last?.rows ?? [], affectedRows: last?.affectedRows ?? 0 };\n }\n const values = serializeParamsRaw({ values: params.values });\n const result = await runMapped(() =>\n params.queryable.query<Record<string, unknown>>(params.text, values),\n );\n return { rows: result.rows, affectedRows: result.affectedRows ?? 0 };\n }\n\n /**\n * Adapter bound to an open transaction. It never re-enters the queue — the\n * enclosing `transaction()` call already owns the connection — and nested\n * `transaction()` calls become savepoints named from the counter shared\n * across the whole top-level transaction.\n */\n function createTransactionAdapter(params: {\n tx: Transaction;\n savepointCounter: SavepointCounter;\n }): DatabaseAdapter {\n const { tx, savepointCounter } = params;\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n\n async execute(execParams) {\n return runExecute({ queryable: tx, ...execParams });\n },\n\n async executeUnsafe(execParams) {\n return runExecuteUnsafe({ queryable: tx, ...execParams });\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 const savepoint = nextSavepointName({ counter: savepointCounter });\n await runMapped(() => tx.query(`SAVEPOINT ${savepoint}`));\n try {\n const result = await fn(createTransactionAdapter({ tx, savepointCounter }));\n await runMapped(() => tx.query(`RELEASE SAVEPOINT ${savepoint}`));\n return result;\n } catch (error) {\n // Best effort: never mask the original failure with a rollback failure.\n try {\n await tx.query(`ROLLBACK TO SAVEPOINT ${savepoint}`);\n } catch {\n /* rollback is best-effort */\n }\n throw error;\n }\n },\n\n async connect(): Promise<void> {\n // Already connected — this adapter is bound to an open transaction.\n },\n\n async disconnect(): Promise<void> {\n // The database outlives the transaction; closing here would be wrong.\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n }\n\n return {\n dialect: DIALECT,\n provider: PROVIDER,\n wire: WIRE,\n\n async execute(params) {\n assertNotInsideTransaction();\n const db = await getInstance();\n return queue.run(() => runExecute({ queryable: db, ...params }));\n },\n\n async executeUnsafe(params) {\n assertNotInsideTransaction();\n const db = await getInstance();\n return queue.run(() => runExecuteUnsafe({ queryable: db, ...params }));\n },\n\n async transaction<T>(\n fn: (txAdapter: DatabaseAdapter) => Promise<T>,\n transactionOptions?: TransactionOptions,\n ): Promise<T> {\n assertNotInsideTransaction();\n // Dialect honesty (constitution rule 4, board #2): PGlite ACCEPTS\n // `SET LOCAL statement_timeout` and even reports it back via SHOW, but\n // never FIRES it — the single-threaded WASM build has no timer interrupt\n // to cancel a running statement (live-verified: `pg_sleep(2)` under a\n // 100ms timeout runs to completion). A timeout the engine cannot enforce\n // is refused at the earliest knowable moment instead of becoming a\n // silent no-op; the postgres SERVER adapters honor the same option for\n // real (SQLSTATE 57014 → VIBE_TRANSACTION, meta.reason \"timeout\").\n if (transactionOptions?.timeout !== undefined) {\n throw new VibeError({\n code: \"VIBE_UNSUPPORTED_CAPABILITY\",\n message:\n \"pglite cannot enforce a transaction timeout: the single-threaded WASM build accepts statement_timeout but never cancels a running statement — remove the option, or use a postgres server adapter where it is genuinely enforced\",\n meta: { provider: PROVIDER, feature: \"transactionTimeout\", timeout: transactionOptions.timeout },\n });\n }\n const db = await getInstance();\n const savepointCounter = createSavepointCounter();\n\n return queue.run(async () => {\n try {\n return await db.transaction(async (tx) => {\n if (transactionOptions?.isolationLevel !== undefined) {\n await tx.query(\n setIsolationLevelStatement({ isolationLevel: transactionOptions.isolationLevel }),\n );\n }\n try {\n return await txContext.run(true, () =>\n fn(createTransactionAdapter({ tx, savepointCounter })),\n );\n } catch (error) {\n throw new CallbackError(error);\n }\n });\n } catch (error) {\n // The callback's own failure propagates untouched; PGlite has already\n // rolled the transaction back by the time it reaches us.\n if (error instanceof CallbackError) throw error.inner;\n throw mapPgliteDriverError({ error });\n }\n });\n },\n\n async connect(): Promise<void> {\n await getInstance();\n },\n\n async disconnect(): Promise<void> {\n if (!ownsInstance || instance === null) return;\n const closing = instance;\n instance = null;\n pending = null;\n await queue.run(() => runMapped(() => closing.close()));\n },\n\n formatArrayParam(values: unknown[]): unknown {\n return new PgliteArrayParam(values);\n },\n };\n}\n\n// ─── Re-exports (driver-error table, markers, helpers) ────────────\n\nexport {\n PGLITE_SQLSTATE_ERROR_CODES,\n PGLITE_SQLSTATE_REASONS,\n mapPgliteDriverError,\n pgliteErrorSqlstate,\n} from \"./errors.ts\";\nexport { PgliteArrayParam, serializeParams, serializeParamsRaw, toPgArrayLiteral } from \"./params.ts\";\nexport { createOperationQueue } from \"./queue.ts\";\nexport type { OperationQueue } from \"./queue.ts\";\nexport { createSavepointCounter, nextSavepointName } from \"./savepoints.ts\";\nexport type { SavepointCounter } from \"./savepoints.ts\";\nexport {\n ISOLATION_LEVEL_SQL,\n refuseNestedTransactionOptions,\n setIsolationLevelStatement,\n} from \"./transaction-sql.ts\";\n",
6
+ "/**\n * Driver-error mapping for PGlite.\n *\n * PGlite runs the real PostgreSQL error protocol in-process, so its errors\n * carry the same SQLSTATE (`code`) plus `constraint` / `table` / `column` as a\n * server would. Constitution rule 5: only `VibeError` escapes the adapter.\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 PGLITE_SQLSTATE_ERROR_CODES: Readonly<Record<string, VibeErrorCode>> = Object.freeze({\n /** unique_violation */\n \"23505\": \"VIBE_UNIQUE_VIOLATION\",\n /** foreign_key_violation */\n \"23503\": \"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 /** 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\n/** SQLSTATEs that additionally explain themselves through `meta.reason`. */\nexport const PGLITE_SQLSTATE_REASONS: Readonly<Record<string, string>> = Object.freeze({\n \"57014\": \"timeout\",\n});\n\n// ─── Reading driver errors ────────────────────────────────────────\n\n/** A PostgreSQL SQLSTATE is exactly five digits/uppercase letters (e.g. `40P01`). */\nconst SQLSTATE_PATTERN = /^[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 PGlite error. Failures raised by PGlite itself\n * (closed database, WASM init) carry no SQLSTATE and yield `undefined`.\n */\nexport function pgliteErrorSqlstate(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 PGlite 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 mapPgliteDriverError(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 = pgliteErrorSqlstate({ error });\n const code: VibeErrorCode =\n (sqlstate === undefined ? undefined : PGLITE_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 : PGLITE_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 pglite 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 mapPgliteDriverError({ error });\n }\n}\n",
7
+ "/**\n * Parameter serialization for PGlite.\n *\n * PGlite takes JS arrays natively for the array types it knows, but it picks a\n * parameter serializer from the BACKEND-INFERRED type OID and falls back to\n * `String(value)` for anything else — so a scalar list bound against a column\n * of a custom type (a native `enum`, i.e. every `Role[]` / `role IN (…)` query)\n * reached the server as `ADMIN,MODERATOR` and was rejected with `malformed\n * array literal`. ORM-path scalar lists are therefore rendered as PostgreSQL\n * array literals here, exactly like the bun:sql adapter does\n * (`@vibeorm/adapter-bun`'s `toPgArrayLiteral`); the server then coerces the\n * literal to whichever array type the statement needs (`text[]`, `int[]`,\n * `timestamptz[]`, `\"Role\"[]`).\n *\n * The ORM path and the raw path still split the same way (LEARNINGS.md): the\n * ORM path cannot tell a `jsonb` value that happens to be an array from a\n * scalar-list parameter, so it JSON-stringifies plain arrays and only treats\n * values tagged with {@link PgliteArrayParam} as scalar lists; the raw path\n * passes plain JS arrays straight through to the driver.\n */\n\nimport { VibeError } from \"@vibeorm/schema\";\n\n// ─── Scalar-list marker ───────────────────────────────────────────\n\n/**\n * Opaque token returned by `pgliteAdapter().formatArrayParam` marking a value\n * as a PostgreSQL array parameter (rather than a JSON value that happens to be\n * an array). The wrapped array is rendered as an array literal on the way out.\n */\nexport class PgliteArrayParam {\n /** The untouched scalar-list values. */\n readonly values: unknown[];\n\n constructor(values: unknown[]) {\n this.values = values;\n }\n}\n\n// ─── PostgreSQL array literals ────────────────────────────────────\n\n/** Quote and escape one array element: `\"` and `\\` are the special characters. */\nfunction quoteElement(value: string): string {\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * Render a JS array as a PostgreSQL array literal string — the same element\n * rules as `@vibeorm/adapter-bun`'s `toPgArrayLiteral` (kept per-adapter: an\n * array literal is driver transport, not SQL text):\n * - `null` / `undefined` → unquoted `NULL`\n * - `number` / `bigint` / `boolean` → unquoted primitive\n * - `Date` → quoted ISO-8601 (PostgreSQL cannot parse `String(date)`)\n * - `string` → quoted, with `\"` and `\\` escaped (commas, braces and quotes\n * inside an element are safe)\n * - any other object, **including a nested array** → quoted `JSON.stringify`,\n * which is what a `Json[]` scalar list needs\n * - binary (`Uint8Array` / `ArrayBuffer`) → unsupported, throws\n *\n * @throws VibeError `VIBE_VALIDATION` for binary elements.\n */\nexport function toPgArrayLiteral(values: readonly unknown[]): string {\n const elements = values.map((value) => {\n if (value === null || value === undefined) return \"NULL\";\n if (typeof value === \"number\" || typeof value === \"bigint\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (value instanceof Date) return quoteElement(value.toISOString());\n if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: \"binary values are not supported inside a postgres array literal\",\n meta: { driver: \"pglite\" },\n });\n }\n if (typeof value === \"string\") return quoteElement(value);\n if (typeof value === \"object\") return quoteElement(JSON.stringify(value));\n return quoteElement(String(value));\n });\n return `{${elements.join(\",\")}}`;\n}\n\n// ─── Serialization ────────────────────────────────────────────────\n\n/** True for values PGlite must receive verbatim (dates, binary, primitives, null). */\nfunction isDriverNativeValue(value: unknown): boolean {\n return (\n value === null ||\n value === undefined ||\n typeof value !== \"object\" ||\n value instanceof Date ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n );\n}\n\n/**\n * ORM hot path: render scalar-list markers as array literals, JSON-stringify\n * plain objects **and** plain arrays (they can only be `json`/`jsonb` values at\n * this point).\n */\nexport function serializeParams(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgliteArrayParam) return toPgArrayLiteral(value.values);\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\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 */\nexport function serializeParamsRaw(params: { values: readonly unknown[] }): unknown[] {\n return params.values.map((value) => {\n if (value instanceof PgliteArrayParam) return toPgArrayLiteral(value.values);\n if (Array.isArray(value)) return value;\n if (isDriverNativeValue(value)) return value;\n return JSON.stringify(value);\n });\n}\n",
8
+ "/**\n * Operation queue for PGlite.\n *\n * PGlite is a single embedded PostgreSQL connection: two overlapping\n * operations would interleave on the same wire, and an interleaved statement\n * inside a transaction would silently join that transaction. Every adapter\n * entry point therefore runs through one serial chain — including whole\n * `transaction()` calls, which occupy the chain until they commit or roll back.\n */\n\n/** A serial chain: jobs run one at a time, in the order they were queued. */\nexport type OperationQueue = {\n /** Queue `job`, resolving with its result once every earlier job has settled. */\n run<T>(job: () => Promise<T>): Promise<T>;\n};\n\n/** Create an empty operation queue. */\nexport function createOperationQueue(): OperationQueue {\n let tail: Promise<unknown> = Promise.resolve();\n\n return {\n run<T>(job: () => Promise<T>): Promise<T> {\n // Run after the previous job settles, whether it resolved or rejected.\n const result = tail.then(job, job);\n // Keep the chain alive: a rejected job must not reject every later job.\n tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n },\n };\n}\n",
9
+ "/**\n * Savepoint naming for nested transactions.\n *\n * v1 lesson (LEARNINGS.md): the counter is shared **by reference** across every\n * adapter created for one top-level transaction, so sibling and deeply nested\n * savepoints can never collide on a name.\n */\n\n/** Mutable counter shared by reference across one top-level transaction. */\nexport type SavepointCounter = { n: number };\n\n/** Fresh counter — one per top-level transaction. */\nexport function createSavepointCounter(): SavepointCounter {\n return { n: 0 };\n}\n\n/**\n * Allocate the next savepoint name (`vibeorm_sp_0`, `vibeorm_sp_1`, …) and\n * advance the shared counter. The name is an identifier by construction, so it\n * is safe to interpolate into `SAVEPOINT` / `RELEASE` / `ROLLBACK TO` SQL.\n */\nexport function nextSavepointName(params: { counter: SavepointCounter }): string {\n return `vibeorm_sp_${params.counter.n++}`;\n}\n",
10
+ "/**\n * Transaction-control SQL text (postgres dialect, PGlite flavour).\n *\n * PGlite issues its own `BEGIN` inside `pglite.transaction(cb)`, so isolation\n * is set with `SET TRANSACTION …` as the first statement of the callback rather\n * than as a `BEGIN` clause.\n */\n\nimport type { IsolationLevel, TransactionOptions } 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 * `SET TRANSACTION ISOLATION LEVEL <level>` — valid only as the first statement\n * of an open transaction, which is where the adapter issues it.\n */\nexport function setIsolationLevelStatement(params: { isolationLevel: IsolationLevel }): string {\n return `SET TRANSACTION ISOLATION LEVEL ${ISOLATION_LEVEL_SQL[params.isolationLevel]}`;\n}\n\n// NOTE (board #2): this module deliberately has NO statementTimeoutStatement.\n// PGlite accepts `SET LOCAL statement_timeout` and reports it back via SHOW,\n// but the single-threaded WASM build never fires it (live-verified:\n// `pg_sleep(2)` under a 100ms timeout runs to completion) — so the adapter\n// refuses the `timeout` option outright with VIBE_UNSUPPORTED_CAPABILITY\n// instead of rendering SQL that silently does nothing.\n\n// ─── Nested transaction options (the cross-adapter contract) ──────\n\n/**\n * Refuse options on a NESTED `transaction()` call — the same contract on every\n * adapter (see the `DatabaseAdapter.transaction` JSDoc in @vibeorm/runtime):\n * a nested transaction is a SAVEPOINT, and a savepoint can neither change the\n * isolation level of the transaction it joins nor enforce its own timeout.\n * Silently dropping the option (the pre-#3 behaviour) hid exactly that.\n *\n * @throws VibeError `VIBE_VALIDATION` when `isolationLevel` or `timeout` is\n * present (an `undefined` or empty options object is accepted).\n */\nexport function refuseNestedTransactionOptions(params: {\n options?: TransactionOptions;\n provider: string;\n}): void {\n const { options, provider } = params;\n if (options === undefined) return;\n\n const passed: string[] = [];\n if (options.isolationLevel !== undefined) passed.push(\"isolationLevel\");\n if (options.timeout !== undefined) passed.push(\"timeout\");\n if (passed.length === 0) return;\n\n const meta: Record<string, unknown> = { provider, nested: true };\n if (options.isolationLevel !== undefined) meta.isolationLevel = options.isolationLevel;\n if (options.timeout !== undefined) meta.timeout = options.timeout;\n\n throw new VibeError({\n code: \"VIBE_VALIDATION\",\n message: `nested transactions run as savepoints and cannot honor ${passed.join(\" or \")} — a savepoint cannot change the isolation level of the transaction it joins, and a nested timeout is not enforceable; pass options on the top-level $transaction`,\n meta,\n });\n}\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "7A12A0ECD3D2E7B564756E2164756E21",
14
+ "names": []
15
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Parameter serialization for PGlite.
3
+ *
4
+ * PGlite takes JS arrays natively for the array types it knows, but it picks a
5
+ * parameter serializer from the BACKEND-INFERRED type OID and falls back to
6
+ * `String(value)` for anything else — so a scalar list bound against a column
7
+ * of a custom type (a native `enum`, i.e. every `Role[]` / `role IN (…)` query)
8
+ * reached the server as `ADMIN,MODERATOR` and was rejected with `malformed
9
+ * array literal`. ORM-path scalar lists are therefore rendered as PostgreSQL
10
+ * array literals here, exactly like the bun:sql adapter does
11
+ * (`@vibeorm/adapter-bun`'s `toPgArrayLiteral`); the server then coerces the
12
+ * literal to whichever array type the statement needs (`text[]`, `int[]`,
13
+ * `timestamptz[]`, `"Role"[]`).
14
+ *
15
+ * The ORM path and the raw path still split the same way (LEARNINGS.md): the
16
+ * ORM path cannot tell a `jsonb` value that happens to be an array from a
17
+ * scalar-list parameter, so it JSON-stringifies plain arrays and only treats
18
+ * values tagged with {@link PgliteArrayParam} as scalar lists; the raw path
19
+ * passes plain JS arrays straight through to the driver.
20
+ */
21
+ /**
22
+ * Opaque token returned by `pgliteAdapter().formatArrayParam` marking a value
23
+ * as a PostgreSQL array parameter (rather than a JSON value that happens to be
24
+ * an array). The wrapped array is rendered as an array literal on the way out.
25
+ */
26
+ export declare class PgliteArrayParam {
27
+ /** The untouched scalar-list values. */
28
+ readonly values: unknown[];
29
+ constructor(values: unknown[]);
30
+ }
31
+ /**
32
+ * Render a JS array as a PostgreSQL array literal string — the same element
33
+ * rules as `@vibeorm/adapter-bun`'s `toPgArrayLiteral` (kept per-adapter: an
34
+ * array literal is driver transport, not SQL text):
35
+ * - `null` / `undefined` → unquoted `NULL`
36
+ * - `number` / `bigint` / `boolean` → unquoted primitive
37
+ * - `Date` → quoted ISO-8601 (PostgreSQL cannot parse `String(date)`)
38
+ * - `string` → quoted, with `"` and `\` escaped (commas, braces and quotes
39
+ * inside an element are safe)
40
+ * - any other object, **including a nested array** → quoted `JSON.stringify`,
41
+ * which is what a `Json[]` scalar list needs
42
+ * - binary (`Uint8Array` / `ArrayBuffer`) → unsupported, throws
43
+ *
44
+ * @throws VibeError `VIBE_VALIDATION` for binary elements.
45
+ */
46
+ export declare function toPgArrayLiteral(values: readonly unknown[]): string;
47
+ /**
48
+ * ORM hot path: render scalar-list markers as array literals, JSON-stringify
49
+ * plain objects **and** plain arrays (they can only be `json`/`jsonb` values at
50
+ * this point).
51
+ */
52
+ export declare function serializeParams(params: {
53
+ values: readonly unknown[];
54
+ }): unknown[];
55
+ /**
56
+ * Raw path (`$queryRaw*` / `$executeRaw*` / DDL): identical to
57
+ * {@link serializeParams} except that plain JS arrays pass straight through, so
58
+ * `db.$queryRawUnsafe('… WHERE "id" = ANY($1)', [1, 2, 3])` matches rows.
59
+ */
60
+ export declare function serializeParamsRaw(params: {
61
+ values: readonly unknown[];
62
+ }): unknown[];
63
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBAEf,MAAM,EAAE,OAAO,EAAE;CAG9B;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,CAmBnE;AAgBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAAG,OAAO,EAAE,CAMjF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,GAAG,OAAO,EAAE,CAOpF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Operation queue for PGlite.
3
+ *
4
+ * PGlite is a single embedded PostgreSQL connection: two overlapping
5
+ * operations would interleave on the same wire, and an interleaved statement
6
+ * inside a transaction would silently join that transaction. Every adapter
7
+ * entry point therefore runs through one serial chain — including whole
8
+ * `transaction()` calls, which occupy the chain until they commit or roll back.
9
+ */
10
+ /** A serial chain: jobs run one at a time, in the order they were queued. */
11
+ export type OperationQueue = {
12
+ /** Queue `job`, resolving with its result once every earlier job has settled. */
13
+ run<T>(job: () => Promise<T>): Promise<T>;
14
+ };
15
+ /** Create an empty operation queue. */
16
+ export declare function createOperationQueue(): OperationQueue;
17
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG;IAC3B,iFAAiF;IACjF,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC3C,CAAC;AAEF,uCAAuC;AACvC,wBAAgB,oBAAoB,IAAI,cAAc,CAerD"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Savepoint naming for nested transactions.
3
+ *
4
+ * v1 lesson (LEARNINGS.md): the counter is shared **by reference** across every
5
+ * adapter created for one top-level transaction, so sibling and deeply nested
6
+ * savepoints can never collide on a name.
7
+ */
8
+ /** Mutable counter shared by reference across one top-level transaction. */
9
+ export type SavepointCounter = {
10
+ n: number;
11
+ };
12
+ /** Fresh counter — one per top-level transaction. */
13
+ export declare function createSavepointCounter(): SavepointCounter;
14
+ /**
15
+ * Allocate the next savepoint name (`vibeorm_sp_0`, `vibeorm_sp_1`, …) and
16
+ * advance the shared counter. The name is an identifier by construction, so it
17
+ * is safe to interpolate into `SAVEPOINT` / `RELEASE` / `ROLLBACK TO` SQL.
18
+ */
19
+ export declare function nextSavepointName(params: {
20
+ counter: SavepointCounter;
21
+ }): string;
22
+ //# sourceMappingURL=savepoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"savepoints.d.ts","sourceRoot":"","sources":["../src/savepoints.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,qDAAqD;AACrD,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,gBAAgB,CAAA;CAAE,GAAG,MAAM,CAE/E"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Transaction-control SQL text (postgres dialect, PGlite flavour).
3
+ *
4
+ * PGlite issues its own `BEGIN` inside `pglite.transaction(cb)`, so isolation
5
+ * is set with `SET TRANSACTION …` as the first statement of the callback rather
6
+ * than as a `BEGIN` clause.
7
+ */
8
+ import type { IsolationLevel, TransactionOptions } from "@vibeorm/runtime";
9
+ /** VibeORM isolation level → PostgreSQL isolation level keyword. */
10
+ export declare const ISOLATION_LEVEL_SQL: Readonly<Record<IsolationLevel, string>>;
11
+ /**
12
+ * `SET TRANSACTION ISOLATION LEVEL <level>` — valid only as the first statement
13
+ * of an open transaction, which is where the adapter issues it.
14
+ */
15
+ export declare function setIsolationLevelStatement(params: {
16
+ isolationLevel: IsolationLevel;
17
+ }): string;
18
+ /**
19
+ * Refuse options on a NESTED `transaction()` call — the same contract on every
20
+ * adapter (see the `DatabaseAdapter.transaction` JSDoc in @vibeorm/runtime):
21
+ * a nested transaction is a SAVEPOINT, and a savepoint can neither change the
22
+ * isolation level of the transaction it joins nor enforce its own timeout.
23
+ * Silently dropping the option (the pre-#3 behaviour) hid exactly that.
24
+ *
25
+ * @throws VibeError `VIBE_VALIDATION` when `isolationLevel` or `timeout` is
26
+ * present (an `undefined` or empty options object is accepted).
27
+ */
28
+ export declare function refuseNestedTransactionOptions(params: {
29
+ options?: TransactionOptions;
30
+ provider: string;
31
+ }): void;
32
+ //# sourceMappingURL=transaction-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-sql.d.ts","sourceRoot":"","sources":["../src/transaction-sql.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3E,oEAAoE;AACpE,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAIvE,CAAC;AAEH;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IAAE,cAAc,EAAE,cAAc,CAAA;CAAE,GAAG,MAAM,CAE7F;AAWD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE;IACrD,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,IAAI,CAkBP"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@vibeorm/adapter-pglite",
3
+ "version": "2.0.0-alpha.1",
4
+ "description": "PGlite (in-process Postgres) adapter for VibeORM v2",
5
+ "keywords": [
6
+ "orm",
7
+ "typescript",
8
+ "sql",
9
+ "database",
10
+ "bun",
11
+ "type-safe",
12
+ "pglite",
13
+ "postgres",
14
+ "postgresql",
15
+ "embedded",
16
+ "in-process",
17
+ "adapter"
18
+ ],
19
+ "homepage": "https://github.com/vibeorm/vibeorm/tree/master/packages/adapter-pglite#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/vibeorm/vibeorm/issues"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/vibeorm/vibeorm.git",
26
+ "directory": "packages/adapter-pglite"
27
+ },
28
+ "license": "MIT",
29
+ "author": "VibeORM contributors",
30
+ "type": "module",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md"
40
+ ],
41
+ "engines": {
42
+ "bun": ">=1.2.0"
43
+ },
44
+ "dependencies": {
45
+ "@electric-sql/pglite": "^0.3.0",
46
+ "@vibeorm/runtime": "2.0.0-alpha.1",
47
+ "@vibeorm/schema": "2.0.0-alpha.1"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ }
52
+ }