@prisma-next/driver-postgres 0.3.0-dev.7 → 0.3.0-dev.70

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.
@@ -0,0 +1,339 @@
1
+ import { i as postgresDriverDescriptorMeta, n as isPostgresError, r as normalizePgError, t as isAlreadyConnectedError } from "./normalize-error-BU6yV-XB.mjs";
2
+ import { Pool } from "pg";
3
+ import Cursor from "pg-cursor";
4
+
5
+ //#region src/callback-to-promise.ts
6
+ function callbackToPromise(fn) {
7
+ return new Promise((resolve, reject) => {
8
+ fn((err, result) => {
9
+ if (err) {
10
+ reject(err);
11
+ return;
12
+ }
13
+ resolve(result);
14
+ });
15
+ });
16
+ }
17
+
18
+ //#endregion
19
+ //#region src/postgres-driver.ts
20
+ const DEFAULT_BATCH_SIZE = 100;
21
+ var AsyncMutex = class {
22
+ #queue = Promise.resolve();
23
+ async lock() {
24
+ const previous = this.#queue;
25
+ let releaseLock;
26
+ this.#queue = new Promise((resolve) => {
27
+ releaseLock = resolve;
28
+ });
29
+ await previous;
30
+ return () => {
31
+ releaseLock?.();
32
+ releaseLock = void 0;
33
+ };
34
+ }
35
+ };
36
+ var PostgresQueryable = class {
37
+ options;
38
+ constructor(options) {
39
+ this.options = options;
40
+ }
41
+ async *execute(request) {
42
+ const client = await this.acquireClient();
43
+ try {
44
+ if (!this.options.cursorDisabled) try {
45
+ for await (const row of this.executeWithCursor(client, request.sql, request.params, this.options.cursorBatchSize)) yield row;
46
+ return;
47
+ } catch (cursorError) {
48
+ if (!(cursorError instanceof Error)) throw cursorError;
49
+ if (isPostgresError(cursorError)) throw normalizePgError(cursorError);
50
+ }
51
+ for await (const row of this.executeBuffered(client, request.sql, request.params)) yield row;
52
+ } catch (error) {
53
+ throw normalizePgError(error);
54
+ } finally {
55
+ await this.releaseClient(client);
56
+ }
57
+ }
58
+ async explain(request) {
59
+ const text = `EXPLAIN (FORMAT JSON) ${request.sql}`;
60
+ const client = await this.acquireClient();
61
+ try {
62
+ return { rows: (await client.query(text, request.params).catch(rethrowNormalizedError)).rows };
63
+ } finally {
64
+ await this.releaseClient(client);
65
+ }
66
+ }
67
+ async query(sql, params) {
68
+ const client = await this.acquireClient();
69
+ try {
70
+ return await client.query(sql, params).catch(rethrowNormalizedError);
71
+ } finally {
72
+ await this.releaseClient(client);
73
+ }
74
+ }
75
+ async *executeWithCursor(client, sql, params, cursorBatchSize) {
76
+ const cursor = client.query(new Cursor(sql, params));
77
+ try {
78
+ while (true) {
79
+ const rows = await readCursor(cursor, cursorBatchSize);
80
+ if (rows.length === 0) break;
81
+ for (const row of rows) yield row;
82
+ }
83
+ } finally {
84
+ await closeCursor(cursor);
85
+ }
86
+ }
87
+ async *executeBuffered(client, sql, params) {
88
+ const result = await client.query(sql, params);
89
+ for (const row of result.rows) yield row;
90
+ }
91
+ };
92
+ var PostgresConnectionImpl = class extends PostgresQueryable {
93
+ #connection;
94
+ #onRelease;
95
+ constructor(connection, options, onRelease) {
96
+ super(options);
97
+ this.#connection = connection;
98
+ this.#onRelease = onRelease;
99
+ }
100
+ acquireClient() {
101
+ return Promise.resolve(this.#connection);
102
+ }
103
+ releaseClient(_client) {
104
+ return Promise.resolve();
105
+ }
106
+ async beginTransaction() {
107
+ await this.#connection.query("BEGIN").catch(rethrowNormalizedError);
108
+ return new PostgresTransactionImpl(this.#connection, this.options);
109
+ }
110
+ async release() {
111
+ if ("release" in this.#connection) this.#connection.release();
112
+ this.#onRelease?.();
113
+ this.#onRelease = void 0;
114
+ }
115
+ };
116
+ var PostgresTransactionImpl = class extends PostgresQueryable {
117
+ #connection;
118
+ constructor(connection, options) {
119
+ super(options);
120
+ this.#connection = connection;
121
+ }
122
+ acquireClient() {
123
+ return Promise.resolve(this.#connection);
124
+ }
125
+ releaseClient(_client) {
126
+ return Promise.resolve();
127
+ }
128
+ async commit() {
129
+ await this.#connection.query("COMMIT").catch(rethrowNormalizedError);
130
+ }
131
+ async rollback() {
132
+ await this.#connection.query("ROLLBACK").catch(rethrowNormalizedError);
133
+ }
134
+ };
135
+ var PostgresPoolDriverImpl = class extends PostgresQueryable {
136
+ pool;
137
+ #closed = false;
138
+ constructor(options) {
139
+ super({
140
+ cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,
141
+ cursorDisabled: options.cursor?.disabled ?? false
142
+ });
143
+ this.pool = options.connect.pool;
144
+ }
145
+ get state() {
146
+ return this.#closed ? "closed" : "connected";
147
+ }
148
+ async connect(_binding) {}
149
+ async acquireConnection() {
150
+ return new PostgresConnectionImpl(await this.acquireClient(), this.options);
151
+ }
152
+ async close() {
153
+ if (this.#closed) return;
154
+ this.#closed = true;
155
+ await this.pool.end();
156
+ }
157
+ async acquireClient() {
158
+ return this.pool.connect();
159
+ }
160
+ async releaseClient(client) {
161
+ client.release();
162
+ }
163
+ };
164
+ var PostgresDirectDriverImpl = class extends PostgresQueryable {
165
+ directClient;
166
+ #connectionMutex = new AsyncMutex();
167
+ #closed = false;
168
+ #connected = false;
169
+ #connectPromise;
170
+ constructor(options) {
171
+ super({
172
+ cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,
173
+ cursorDisabled: options.cursor?.disabled ?? false
174
+ });
175
+ this.directClient = options.connect.client;
176
+ }
177
+ get state() {
178
+ return this.#closed ? "closed" : "connected";
179
+ }
180
+ async connect(_binding) {}
181
+ async acquireConnection() {
182
+ const releaseLease = await this.#connectionMutex.lock();
183
+ try {
184
+ return new PostgresConnectionImpl(await this.acquireClient(), this.options, releaseLease);
185
+ } catch (error) {
186
+ releaseLease();
187
+ throw error;
188
+ }
189
+ }
190
+ async close() {
191
+ const releaseLease = await this.#connectionMutex.lock();
192
+ try {
193
+ if (this.#closed) return;
194
+ this.#closed = true;
195
+ await this.directClient.end();
196
+ this.#connected = false;
197
+ } finally {
198
+ releaseLease();
199
+ }
200
+ }
201
+ async acquireClient() {
202
+ if (this.#connected) return this.directClient;
203
+ if (this.#connectPromise !== void 0) {
204
+ await this.#connectPromise;
205
+ return this.directClient;
206
+ }
207
+ this.#connectPromise = (async () => {
208
+ try {
209
+ await this.directClient.connect();
210
+ } catch (error) {
211
+ if (!isAlreadyConnectedError(error)) throw error;
212
+ } finally {
213
+ this.#connectPromise = void 0;
214
+ }
215
+ this.#connected = true;
216
+ })();
217
+ await this.#connectPromise;
218
+ return this.directClient;
219
+ }
220
+ async releaseClient(_client) {}
221
+ };
222
+ function createBoundDriverFromBinding(binding, cursorOpts) {
223
+ switch (binding.kind) {
224
+ case "url": return new PostgresPoolDriverImpl({
225
+ connect: { pool: new Pool({
226
+ connectionString: binding.url,
227
+ connectionTimeoutMillis: 2e4,
228
+ idleTimeoutMillis: 3e4
229
+ }) },
230
+ cursor: cursorOpts
231
+ });
232
+ case "pgPool": return new PostgresPoolDriverImpl({
233
+ connect: { pool: binding.pool },
234
+ cursor: cursorOpts
235
+ });
236
+ case "pgClient": return new PostgresDirectDriverImpl({
237
+ connect: { client: binding.client },
238
+ cursor: cursorOpts
239
+ });
240
+ }
241
+ }
242
+ function readCursor(cursor, size) {
243
+ return callbackToPromise((cb) => {
244
+ cursor.read(size, (err, rows) => cb(err, rows));
245
+ });
246
+ }
247
+ function closeCursor(cursor) {
248
+ return callbackToPromise((cb) => cursor.close(cb));
249
+ }
250
+ function rethrowNormalizedError(error) {
251
+ throw normalizePgError(error);
252
+ }
253
+
254
+ //#endregion
255
+ //#region src/exports/runtime.ts
256
+ const USE_BEFORE_CONNECT_MESSAGE = "Postgres driver not connected. Call connect(binding) before acquireConnection or execute.";
257
+ const ALREADY_CONNECTED_MESSAGE = "Postgres driver already connected. Call close() before reconnecting with a new binding.";
258
+ function driverError(code, message, details) {
259
+ const error = new Error(message);
260
+ Object.defineProperty(error, "name", {
261
+ value: "RuntimeError",
262
+ configurable: true
263
+ });
264
+ return Object.assign(error, {
265
+ code,
266
+ category: "RUNTIME",
267
+ severity: "error",
268
+ message,
269
+ details
270
+ });
271
+ }
272
+ function unboundExecute() {
273
+ return { [Symbol.asyncIterator]() {
274
+ return { async next() {
275
+ throw driverError("DRIVER.NOT_CONNECTED", USE_BEFORE_CONNECT_MESSAGE);
276
+ } };
277
+ } };
278
+ }
279
+ var PostgresUnboundDriverImpl = class {
280
+ familyId = "sql";
281
+ targetId = "postgres";
282
+ #delegate = null;
283
+ #closed = false;
284
+ #cursorOpts;
285
+ constructor(cursorOpts) {
286
+ this.#cursorOpts = cursorOpts;
287
+ }
288
+ get state() {
289
+ if (this.#delegate !== null) return "connected";
290
+ if (this.#closed) return "closed";
291
+ return "unbound";
292
+ }
293
+ #requireDelegate() {
294
+ const delegate = this.#delegate;
295
+ if (delegate === null) throw driverError("DRIVER.NOT_CONNECTED", USE_BEFORE_CONNECT_MESSAGE);
296
+ return delegate;
297
+ }
298
+ async connect(binding) {
299
+ if (this.#delegate !== null) throw driverError("DRIVER.ALREADY_CONNECTED", ALREADY_CONNECTED_MESSAGE, { bindingKind: binding.kind });
300
+ this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts);
301
+ this.#closed = false;
302
+ }
303
+ async acquireConnection() {
304
+ return this.#requireDelegate().acquireConnection();
305
+ }
306
+ async close() {
307
+ const delegate = this.#delegate;
308
+ if (delegate !== null) {
309
+ this.#delegate = null;
310
+ await delegate.close();
311
+ }
312
+ this.#closed = true;
313
+ }
314
+ execute(request) {
315
+ const delegate = this.#delegate;
316
+ if (delegate === null) return unboundExecute();
317
+ return delegate.execute(request);
318
+ }
319
+ async explain(request) {
320
+ const delegate = this.#requireDelegate();
321
+ const explain = delegate.explain;
322
+ if (explain === void 0) throw driverError("DRIVER.NOT_CONNECTED", USE_BEFORE_CONNECT_MESSAGE);
323
+ return explain.call(delegate, request);
324
+ }
325
+ async query(sql, params) {
326
+ return this.#requireDelegate().query(sql, params);
327
+ }
328
+ };
329
+ const postgresRuntimeDriverDescriptor = {
330
+ ...postgresDriverDescriptorMeta,
331
+ create(options) {
332
+ return new PostgresUnboundDriverImpl(options?.cursor);
333
+ }
334
+ };
335
+ var runtime_default = postgresRuntimeDriverDescriptor;
336
+
337
+ //#endregion
338
+ export { runtime_default as default };
339
+ //# sourceMappingURL=runtime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.mjs","names":["#queue","releaseLock: (() => void) | undefined","#connection","#onRelease","#closed","#connectionMutex","#connected","#connectPromise","error: unknown","#cursorOpts","#delegate","#closed","#requireDelegate","postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n>"],"sources":["../src/callback-to-promise.ts","../src/postgres-driver.ts","../src/exports/runtime.ts"],"sourcesContent":["/**\n * Wraps a Node-style callback function into a Promise.\n * Supports both (err) => void and (err, result) => void patterns.\n */\nexport function callbackToPromise(\n fn: (callback: (err: Error | null | undefined) => void) => void,\n): Promise<void>;\nexport function callbackToPromise<T>(\n fn: (callback: (err: Error | null | undefined, result: T) => void) => void,\n): Promise<T>;\nexport function callbackToPromise<T = void>(\n fn: (callback: (err: Error | null | undefined, result?: T) => void) => void,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n fn((err, result) => {\n if (err) {\n reject(err);\n return;\n }\n resolve(result as T);\n });\n });\n}\n","import type {\n SqlConnection,\n SqlDriver,\n SqlDriverState,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryable,\n SqlQueryResult,\n SqlTransaction,\n} from '@prisma-next/sql-relational-core/ast';\nimport type {\n Client,\n QueryResult as PgQueryResult,\n PoolClient,\n Pool as PoolType,\n QueryResultRow,\n} from 'pg';\nimport { Pool } from 'pg';\nimport Cursor from 'pg-cursor';\nimport { callbackToPromise } from './callback-to-promise';\nimport { isAlreadyConnectedError, isPostgresError, normalizePgError } from './normalize-error';\n\nexport type QueryResult<T extends QueryResultRow = QueryResultRow> = PgQueryResult<T>;\n\nexport type PostgresBinding =\n | { readonly kind: 'url'; readonly url: string }\n | { readonly kind: 'pgPool'; readonly pool: PoolType }\n | { readonly kind: 'pgClient'; readonly client: Client };\n\nexport interface PostgresCursorOptions {\n readonly batchSize?: number;\n readonly disabled?: boolean;\n}\n\ninterface PostgresDriverOptions {\n readonly connect: { client: Client } | { pool: PoolType };\n readonly cursor?: PostgresCursorOptions | undefined;\n}\n\nexport type PostgresDriverCreateOptions = Omit<PostgresDriverOptions, 'connect'>;\n\nconst DEFAULT_BATCH_SIZE = 100;\n\ntype ConnectionOptions =\n | { readonly cursorDisabled: true }\n | { readonly cursorBatchSize: number; readonly cursorDisabled: false };\n\nclass AsyncMutex {\n #queue = Promise.resolve();\n\n async lock(): Promise<() => void> {\n const previous = this.#queue;\n let releaseLock: (() => void) | undefined;\n this.#queue = new Promise<void>((resolve) => {\n releaseLock = resolve;\n });\n await previous;\n return () => {\n releaseLock?.();\n releaseLock = undefined;\n };\n }\n}\n\nabstract class PostgresQueryable<C extends PoolClient | Client = PoolClient | Client>\n implements SqlQueryable\n{\n abstract acquireClient(): Promise<C>;\n abstract releaseClient(client: C): Promise<void>;\n\n protected readonly options: ConnectionOptions;\n\n constructor(options: ConnectionOptions) {\n this.options = options;\n }\n\n async *execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const client = await this.acquireClient();\n try {\n if (!this.options.cursorDisabled) {\n try {\n for await (const row of this.executeWithCursor(\n client,\n request.sql,\n request.params,\n this.options.cursorBatchSize,\n )) {\n yield row as Row;\n }\n return;\n } catch (cursorError) {\n if (!(cursorError instanceof Error)) {\n throw cursorError;\n }\n // Check if this is a pg error - if so, normalize and throw\n // Otherwise, fall back to buffered mode for cursor-specific errors\n if (isPostgresError(cursorError)) {\n throw normalizePgError(cursorError);\n }\n // Not a pg error - cursor-specific error, fall back to buffered mode\n }\n }\n\n for await (const row of this.executeBuffered(client, request.sql, request.params)) {\n yield row as Row;\n }\n } catch (error) {\n throw normalizePgError(error);\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n // SQL is generated by Prisma Next planners (or validated raw paths), so\n // EXPLAIN prefixing preserves the same statement semantics.\n const text = `EXPLAIN (FORMAT JSON) ${request.sql}`;\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(text, request.params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return { rows: result.rows as ReadonlyArray<Record<string, unknown>> };\n } finally {\n await this.releaseClient(client);\n }\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const client = await this.acquireClient();\n try {\n const result = await client\n .query(sql, params as unknown[] | undefined)\n .catch(rethrowNormalizedError);\n return result as unknown as SqlQueryResult<Row>;\n } finally {\n await this.releaseClient(client);\n }\n }\n\n private async *executeWithCursor(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n cursorBatchSize: number,\n ): AsyncIterable<Record<string, unknown>> {\n const cursor = client.query(new Cursor(sql, params as unknown[] | undefined));\n\n try {\n while (true) {\n const rows = await readCursor(cursor, cursorBatchSize);\n if (rows.length === 0) {\n break;\n }\n\n for (const row of rows) {\n yield row;\n }\n }\n } finally {\n await closeCursor(cursor);\n }\n }\n\n private async *executeBuffered(\n client: PoolClient | Client,\n sql: string,\n params: readonly unknown[] | undefined,\n ): AsyncIterable<Record<string, unknown>> {\n const result = await client.query(sql, params as unknown[] | undefined);\n for (const row of result.rows as Record<string, unknown>[]) {\n yield row;\n }\n }\n}\n\nclass PostgresConnectionImpl extends PostgresQueryable implements SqlConnection {\n #connection: PoolClient | Client;\n #onRelease: (() => void) | undefined;\n\n constructor(connection: PoolClient | Client, options: ConnectionOptions, onRelease?: () => void) {\n super(options);\n this.#connection = connection;\n this.#onRelease = onRelease;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async beginTransaction(): Promise<SqlTransaction> {\n await this.#connection.query('BEGIN').catch(rethrowNormalizedError);\n return new PostgresTransactionImpl(this.#connection, this.options);\n }\n\n async release(): Promise<void> {\n if ('release' in this.#connection) {\n this.#connection.release();\n }\n this.#onRelease?.();\n this.#onRelease = undefined;\n }\n}\n\nclass PostgresTransactionImpl extends PostgresQueryable implements SqlTransaction {\n #connection: PoolClient | Client;\n\n constructor(connection: PoolClient | Client, options: ConnectionOptions) {\n super(options);\n this.#connection = connection;\n }\n\n override acquireClient(): Promise<PoolClient | Client> {\n return Promise.resolve(this.#connection);\n }\n\n override releaseClient(_client: PoolClient | Client): Promise<void> {\n return Promise.resolve();\n }\n\n async commit(): Promise<void> {\n await this.#connection.query('COMMIT').catch(rethrowNormalizedError);\n }\n\n async rollback(): Promise<void> {\n await this.#connection.query('ROLLBACK').catch(rethrowNormalizedError);\n }\n}\n\nclass PostgresPoolDriverImpl\n extends PostgresQueryable<PoolClient>\n implements SqlDriver<PostgresBinding>\n{\n private readonly pool: PoolType;\n #closed = false;\n\n constructor(options: PostgresDriverOptions & { connect: { pool: PoolType } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.pool = options.connect.pool;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with pool already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(client, this.options);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.pool.end();\n }\n\n async acquireClient(): Promise<PoolClient> {\n return this.pool.connect();\n }\n\n async releaseClient(client: PoolClient): Promise<void> {\n client.release();\n }\n}\n\nclass PostgresDirectDriverImpl\n extends PostgresQueryable<Client>\n implements SqlDriver<PostgresBinding>\n{\n private readonly directClient: Client;\n readonly #connectionMutex = new AsyncMutex();\n #closed = false;\n #connected = false;\n #connectPromise: Promise<void> | undefined;\n\n constructor(options: PostgresDriverOptions & { connect: { client: Client } }) {\n super({\n cursorBatchSize: options.cursor?.batchSize ?? DEFAULT_BATCH_SIZE,\n cursorDisabled: options.cursor?.disabled ?? false,\n });\n this.directClient = options.connect.client;\n }\n\n get state(): SqlDriverState {\n return this.#closed ? 'closed' : 'connected';\n }\n\n // Bound drivers are created via createBoundDriverFromBinding with client already\n // configured; connect is intentionally no-op.\n async connect(_binding: PostgresBinding): Promise<void> {}\n\n async acquireConnection(): Promise<SqlConnection> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n const client = await this.acquireClient();\n return new PostgresConnectionImpl(client, this.options, releaseLease);\n } catch (error) {\n releaseLease();\n throw error;\n }\n }\n\n async close(): Promise<void> {\n const releaseLease = await this.#connectionMutex.lock();\n try {\n if (this.#closed) {\n return;\n }\n this.#closed = true;\n await this.directClient.end();\n this.#connected = false;\n } finally {\n releaseLease();\n }\n }\n\n async acquireClient(): Promise<Client> {\n if (this.#connected) {\n return this.directClient;\n }\n if (this.#connectPromise !== undefined) {\n await this.#connectPromise;\n return this.directClient;\n }\n\n this.#connectPromise = (async () => {\n try {\n await this.directClient.connect();\n } catch (error: unknown) {\n if (!isAlreadyConnectedError(error)) {\n throw error;\n }\n } finally {\n this.#connectPromise = undefined;\n }\n this.#connected = true;\n })();\n await this.#connectPromise;\n\n return this.directClient;\n }\n\n async releaseClient(_client: Client): Promise<void> {}\n}\n\nexport function createBoundDriverFromBinding(\n binding: PostgresBinding,\n cursorOpts: PostgresDriverCreateOptions['cursor'],\n): SqlDriver<PostgresBinding> {\n switch (binding.kind) {\n case 'url': {\n const pool = new Pool({\n connectionString: binding.url,\n connectionTimeoutMillis: 20_000,\n idleTimeoutMillis: 30_000,\n });\n return new PostgresPoolDriverImpl({\n connect: { pool },\n cursor: cursorOpts,\n });\n }\n case 'pgPool':\n return new PostgresPoolDriverImpl({\n connect: { pool: binding.pool },\n cursor: cursorOpts,\n });\n case 'pgClient':\n return new PostgresDirectDriverImpl({\n connect: { client: binding.client },\n cursor: cursorOpts,\n });\n }\n}\n\nfunction readCursor<Row>(cursor: Cursor<Row>, size: number): Promise<Row[]> {\n return callbackToPromise<Row[]>((cb) => {\n cursor.read(size, (err, rows) => cb(err, rows));\n });\n}\n\nfunction closeCursor(cursor: Cursor<unknown>): Promise<void> {\n return callbackToPromise((cb) => cursor.close(cb));\n}\n\nfunction rethrowNormalizedError(error: unknown): never {\n throw normalizePgError(error);\n}\n","import type {\n RuntimeDriverDescriptor,\n RuntimeDriverInstance,\n} from '@prisma-next/core-execution-plane/types';\nimport type {\n SqlConnection,\n SqlDriver,\n SqlExecuteRequest,\n SqlExplainResult,\n SqlQueryResult,\n} from '@prisma-next/sql-relational-core/ast';\nimport { postgresDriverDescriptorMeta } from '../core/descriptor-meta';\nimport {\n createBoundDriverFromBinding,\n type PostgresBinding,\n type PostgresDriverCreateOptions,\n} from '../postgres-driver';\n\nexport type PostgresRuntimeDriver = RuntimeDriverInstance<'sql', 'postgres'> &\n SqlDriver<PostgresBinding>;\n\nconst USE_BEFORE_CONNECT_MESSAGE =\n 'Postgres driver not connected. Call connect(binding) before acquireConnection or execute.';\nconst ALREADY_CONNECTED_MESSAGE =\n 'Postgres driver already connected. Call close() before reconnecting with a new binding.';\n\ninterface DriverRuntimeError extends Error {\n readonly code: 'DRIVER.NOT_CONNECTED' | 'DRIVER.ALREADY_CONNECTED';\n readonly category: 'RUNTIME';\n readonly severity: 'error';\n readonly details?: Record<string, unknown>;\n}\n\nfunction driverError(\n code: DriverRuntimeError['code'],\n message: string,\n details?: Record<string, unknown>,\n): DriverRuntimeError {\n const error = new Error(message) as DriverRuntimeError;\n Object.defineProperty(error, 'name', {\n value: 'RuntimeError',\n configurable: true,\n });\n return Object.assign(error, {\n code,\n category: 'RUNTIME' as const,\n severity: 'error' as const,\n message,\n details,\n });\n}\n\nfunction unboundExecute<Row>(): AsyncIterable<Row> {\n return {\n [Symbol.asyncIterator]() {\n return {\n async next() {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n },\n };\n },\n };\n}\n\nclass PostgresUnboundDriverImpl implements PostgresRuntimeDriver {\n readonly familyId = 'sql' as const;\n readonly targetId = 'postgres' as const;\n\n #delegate: SqlDriver<PostgresBinding> | null = null;\n #closed = false;\n #cursorOpts: PostgresDriverCreateOptions['cursor'];\n\n constructor(cursorOpts?: PostgresDriverCreateOptions['cursor']) {\n this.#cursorOpts = cursorOpts;\n }\n\n get state(): 'unbound' | 'connected' | 'closed' {\n if (this.#delegate !== null) {\n return 'connected';\n }\n if (this.#closed) {\n return 'closed';\n }\n return 'unbound';\n }\n\n #requireDelegate(): SqlDriver<PostgresBinding> {\n const delegate = this.#delegate;\n if (delegate === null) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return delegate;\n }\n\n async connect(binding: PostgresBinding): Promise<void> {\n if (this.#delegate !== null) {\n throw driverError('DRIVER.ALREADY_CONNECTED', ALREADY_CONNECTED_MESSAGE, {\n bindingKind: binding.kind,\n });\n }\n this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts);\n this.#closed = false;\n }\n\n async acquireConnection(): Promise<SqlConnection> {\n const delegate = this.#requireDelegate();\n return delegate.acquireConnection();\n }\n\n async close(): Promise<void> {\n const delegate = this.#delegate;\n if (delegate !== null) {\n this.#delegate = null;\n await delegate.close();\n }\n this.#closed = true;\n }\n\n execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {\n const delegate = this.#delegate;\n if (delegate === null) {\n return unboundExecute<Row>();\n }\n return delegate.execute<Row>(request);\n }\n\n async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {\n const delegate = this.#requireDelegate();\n const explain = delegate.explain;\n if (explain === undefined) {\n throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);\n }\n return explain.call(delegate, request);\n }\n\n async query<Row = Record<string, unknown>>(\n sql: string,\n params?: readonly unknown[],\n ): Promise<SqlQueryResult<Row>> {\n const delegate = this.#requireDelegate();\n return delegate.query<Row>(sql, params);\n }\n}\n\nconst postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<\n 'sql',\n 'postgres',\n PostgresDriverCreateOptions,\n PostgresRuntimeDriver\n> = {\n ...postgresDriverDescriptorMeta,\n create(options?: PostgresDriverCreateOptions): PostgresRuntimeDriver {\n return new PostgresUnboundDriverImpl(options?.cursor);\n },\n};\n\nexport default postgresRuntimeDriverDescriptor;\nexport type {\n PostgresBinding,\n PostgresDriverCreateOptions,\n QueryResult,\n} from '../postgres-driver';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,53 +1,54 @@
1
1
  {
2
2
  "name": "@prisma-next/driver-postgres",
3
- "version": "0.3.0-dev.7",
3
+ "version": "0.3.0-dev.70",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
7
7
  "arktype": "^2.0.0",
8
8
  "pg": "8.16.3",
9
9
  "pg-cursor": "^2.10.5",
10
- "@prisma-next/core-control-plane": "0.3.0-dev.7",
11
- "@prisma-next/core-execution-plane": "0.3.0-dev.7",
12
- "@prisma-next/sql-contract": "0.3.0-dev.7",
13
- "@prisma-next/sql-errors": "0.3.0-dev.7",
14
- "@prisma-next/sql-operations": "0.3.0-dev.7",
15
- "@prisma-next/sql-relational-core": "0.3.0-dev.7",
16
- "@prisma-next/utils": "0.3.0-dev.7",
17
- "@prisma-next/contract": "0.3.0-dev.7"
10
+ "@prisma-next/contract": "0.3.0-dev.70",
11
+ "@prisma-next/core-control-plane": "0.3.0-dev.70",
12
+ "@prisma-next/core-execution-plane": "0.3.0-dev.70",
13
+ "@prisma-next/sql-contract": "0.3.0-dev.70",
14
+ "@prisma-next/sql-errors": "0.3.0-dev.70",
15
+ "@prisma-next/sql-relational-core": "0.3.0-dev.70",
16
+ "@prisma-next/sql-operations": "0.3.0-dev.70",
17
+ "@prisma-next/utils": "0.3.0-dev.70"
18
18
  },
19
19
  "devDependencies": {
20
- "@vitest/coverage-v8": "4.0.16",
21
- "pg-mem": "^3.0.5",
22
- "tsup": "8.5.1",
23
- "typescript": "5.9.3",
24
- "vitest": "4.0.16",
25
20
  "@types/pg": "8.16.0",
26
21
  "@types/pg-cursor": "^2.4.6",
27
- "@prisma-next/test-utils": "0.0.1"
22
+ "pg-mem": "^3.0.5",
23
+ "tsdown": "0.18.4",
24
+ "typescript": "5.9.3",
25
+ "vitest": "4.0.17",
26
+ "@prisma-next/test-utils": "0.0.1",
27
+ "@prisma-next/tsconfig": "0.0.0",
28
+ "@prisma-next/tsdown": "0.0.0"
28
29
  },
29
30
  "files": [
30
31
  "dist",
31
32
  "src"
32
33
  ],
33
34
  "exports": {
34
- "./control": {
35
- "types": "./dist/exports/control.d.ts",
36
- "import": "./dist/exports/control.js"
37
- },
38
- "./runtime": {
39
- "types": "./dist/exports/runtime.d.ts",
40
- "import": "./dist/exports/runtime.js"
41
- }
35
+ "./control": "./dist/control.mjs",
36
+ "./runtime": "./dist/runtime.mjs",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/prisma/prisma-next.git",
42
+ "directory": "packages/3-targets/7-drivers/postgres"
42
43
  },
43
44
  "scripts": {
44
- "build": "tsup --config tsup.config.ts && tsc --project tsconfig.build.json",
45
+ "build": "tsdown",
45
46
  "test": "vitest run",
46
47
  "test:coverage": "vitest run --coverage",
47
48
  "typecheck": "tsc --project tsconfig.json --noEmit",
48
- "lint": "biome check . --config-path ../../../../biome.json --error-on-warnings",
49
- "lint:fix": "biome check --write . --config-path ../../../biome.json",
50
- "lint:fix:unsafe": "biome check --write --unsafe . --config-path ../../../biome.json",
51
- "clean": "node ../../../../scripts/clean.mjs"
49
+ "lint": "biome check . --error-on-warnings",
50
+ "lint:fix": "biome check --write .",
51
+ "lint:fix:unsafe": "biome check --write --unsafe .",
52
+ "clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
52
53
  }
53
54
  }
@@ -4,6 +4,7 @@ import type {
4
4
  ControlDriverInstance,
5
5
  } from '@prisma-next/core-control-plane/types';
6
6
  import { SqlQueryError } from '@prisma-next/sql-errors';
7
+ import { ifDefined } from '@prisma-next/utils/defined';
7
8
  import { redactDatabaseUrl } from '@prisma-next/utils/redact-db-url';
8
9
  import { Client } from 'pg';
9
10
  import { postgresDriverDescriptorMeta } from '../core/descriptor-meta';
@@ -71,7 +72,7 @@ const postgresDriverDescriptor: ControlDriverDescriptor<'sql', 'postgres', Postg
71
72
  why: normalized.message,
72
73
  fix: 'Verify the database URL, ensure the database is reachable, and confirm credentials/permissions',
73
74
  meta: {
74
- ...(typeof code !== 'undefined' ? { code } : {}),
75
+ ...ifDefined('code', code),
75
76
  ...redacted,
76
77
  },
77
78
  });
@@ -2,37 +2,161 @@ import type {
2
2
  RuntimeDriverDescriptor,
3
3
  RuntimeDriverInstance,
4
4
  } from '@prisma-next/core-execution-plane/types';
5
- import type { SqlDriver } from '@prisma-next/sql-relational-core/ast';
5
+ import type {
6
+ SqlConnection,
7
+ SqlDriver,
8
+ SqlExecuteRequest,
9
+ SqlExplainResult,
10
+ SqlQueryResult,
11
+ } from '@prisma-next/sql-relational-core/ast';
6
12
  import { postgresDriverDescriptorMeta } from '../core/descriptor-meta';
7
- import type { PostgresDriverOptions } from '../postgres-driver';
8
- import { createPostgresDriverFromOptions } from '../postgres-driver';
9
-
10
- /**
11
- * Postgres runtime driver instance interface.
12
- * SqlDriver provides SQL-specific methods (execute, explain, close).
13
- * RuntimeDriverInstance provides target identification (familyId, targetId).
14
- * We use intersection type to combine both interfaces.
15
- */
16
- export type PostgresRuntimeDriver = RuntimeDriverInstance<'sql', 'postgres'> & SqlDriver;
17
-
18
- /**
19
- * Postgres driver descriptor for runtime plane.
20
- */
13
+ import {
14
+ createBoundDriverFromBinding,
15
+ type PostgresBinding,
16
+ type PostgresDriverCreateOptions,
17
+ } from '../postgres-driver';
18
+
19
+ export type PostgresRuntimeDriver = RuntimeDriverInstance<'sql', 'postgres'> &
20
+ SqlDriver<PostgresBinding>;
21
+
22
+ const USE_BEFORE_CONNECT_MESSAGE =
23
+ 'Postgres driver not connected. Call connect(binding) before acquireConnection or execute.';
24
+ const ALREADY_CONNECTED_MESSAGE =
25
+ 'Postgres driver already connected. Call close() before reconnecting with a new binding.';
26
+
27
+ interface DriverRuntimeError extends Error {
28
+ readonly code: 'DRIVER.NOT_CONNECTED' | 'DRIVER.ALREADY_CONNECTED';
29
+ readonly category: 'RUNTIME';
30
+ readonly severity: 'error';
31
+ readonly details?: Record<string, unknown>;
32
+ }
33
+
34
+ function driverError(
35
+ code: DriverRuntimeError['code'],
36
+ message: string,
37
+ details?: Record<string, unknown>,
38
+ ): DriverRuntimeError {
39
+ const error = new Error(message) as DriverRuntimeError;
40
+ Object.defineProperty(error, 'name', {
41
+ value: 'RuntimeError',
42
+ configurable: true,
43
+ });
44
+ return Object.assign(error, {
45
+ code,
46
+ category: 'RUNTIME' as const,
47
+ severity: 'error' as const,
48
+ message,
49
+ details,
50
+ });
51
+ }
52
+
53
+ function unboundExecute<Row>(): AsyncIterable<Row> {
54
+ return {
55
+ [Symbol.asyncIterator]() {
56
+ return {
57
+ async next() {
58
+ throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);
59
+ },
60
+ };
61
+ },
62
+ };
63
+ }
64
+
65
+ class PostgresUnboundDriverImpl implements PostgresRuntimeDriver {
66
+ readonly familyId = 'sql' as const;
67
+ readonly targetId = 'postgres' as const;
68
+
69
+ #delegate: SqlDriver<PostgresBinding> | null = null;
70
+ #closed = false;
71
+ #cursorOpts: PostgresDriverCreateOptions['cursor'];
72
+
73
+ constructor(cursorOpts?: PostgresDriverCreateOptions['cursor']) {
74
+ this.#cursorOpts = cursorOpts;
75
+ }
76
+
77
+ get state(): 'unbound' | 'connected' | 'closed' {
78
+ if (this.#delegate !== null) {
79
+ return 'connected';
80
+ }
81
+ if (this.#closed) {
82
+ return 'closed';
83
+ }
84
+ return 'unbound';
85
+ }
86
+
87
+ #requireDelegate(): SqlDriver<PostgresBinding> {
88
+ const delegate = this.#delegate;
89
+ if (delegate === null) {
90
+ throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);
91
+ }
92
+ return delegate;
93
+ }
94
+
95
+ async connect(binding: PostgresBinding): Promise<void> {
96
+ if (this.#delegate !== null) {
97
+ throw driverError('DRIVER.ALREADY_CONNECTED', ALREADY_CONNECTED_MESSAGE, {
98
+ bindingKind: binding.kind,
99
+ });
100
+ }
101
+ this.#delegate = createBoundDriverFromBinding(binding, this.#cursorOpts);
102
+ this.#closed = false;
103
+ }
104
+
105
+ async acquireConnection(): Promise<SqlConnection> {
106
+ const delegate = this.#requireDelegate();
107
+ return delegate.acquireConnection();
108
+ }
109
+
110
+ async close(): Promise<void> {
111
+ const delegate = this.#delegate;
112
+ if (delegate !== null) {
113
+ this.#delegate = null;
114
+ await delegate.close();
115
+ }
116
+ this.#closed = true;
117
+ }
118
+
119
+ execute<Row = Record<string, unknown>>(request: SqlExecuteRequest): AsyncIterable<Row> {
120
+ const delegate = this.#delegate;
121
+ if (delegate === null) {
122
+ return unboundExecute<Row>();
123
+ }
124
+ return delegate.execute<Row>(request);
125
+ }
126
+
127
+ async explain(request: SqlExecuteRequest): Promise<SqlExplainResult> {
128
+ const delegate = this.#requireDelegate();
129
+ const explain = delegate.explain;
130
+ if (explain === undefined) {
131
+ throw driverError('DRIVER.NOT_CONNECTED', USE_BEFORE_CONNECT_MESSAGE);
132
+ }
133
+ return explain.call(delegate, request);
134
+ }
135
+
136
+ async query<Row = Record<string, unknown>>(
137
+ sql: string,
138
+ params?: readonly unknown[],
139
+ ): Promise<SqlQueryResult<Row>> {
140
+ const delegate = this.#requireDelegate();
141
+ return delegate.query<Row>(sql, params);
142
+ }
143
+ }
144
+
21
145
  const postgresRuntimeDriverDescriptor: RuntimeDriverDescriptor<
22
146
  'sql',
23
147
  'postgres',
148
+ PostgresDriverCreateOptions,
24
149
  PostgresRuntimeDriver
25
150
  > = {
26
151
  ...postgresDriverDescriptorMeta,
27
- create(options: PostgresDriverOptions): PostgresRuntimeDriver {
28
- return createPostgresDriverFromOptions(options) as PostgresRuntimeDriver;
152
+ create(options?: PostgresDriverCreateOptions): PostgresRuntimeDriver {
153
+ return new PostgresUnboundDriverImpl(options?.cursor);
29
154
  },
30
155
  };
31
156
 
32
157
  export default postgresRuntimeDriverDescriptor;
33
158
  export type {
34
- CreatePostgresDriverOptions,
35
- PostgresDriverOptions,
159
+ PostgresBinding,
160
+ PostgresDriverCreateOptions,
36
161
  QueryResult,
37
162
  } from '../postgres-driver';
38
- export { createPostgresDriver, createPostgresDriverFromOptions } from '../postgres-driver';