@nextlyhq/adapter-postgres 0.0.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/dist/index.cjs ADDED
@@ -0,0 +1,679 @@
1
+ 'use strict';
2
+
3
+ var adapterDrizzle = require('@nextlyhq/adapter-drizzle');
4
+ var types = require('@nextlyhq/adapter-drizzle/types');
5
+ var versionCheck = require('@nextlyhq/adapter-drizzle/version-check');
6
+ var nodePostgres = require('drizzle-orm/node-postgres');
7
+ var pg = require('pg');
8
+
9
+ // src/index.ts
10
+
11
+ // src/provider.ts
12
+ function detectPostgresProvider(url, explicitProvider) {
13
+ if (explicitProvider) {
14
+ const normalized = explicitProvider.toLowerCase();
15
+ if (normalized === "neon" || normalized === "supabase" || normalized === "standard") {
16
+ return normalized;
17
+ }
18
+ }
19
+ if (url.includes(".neon.tech") || url.includes("neon.")) {
20
+ return "neon";
21
+ }
22
+ if (url.includes(".supabase.") || url.includes("supabase.")) {
23
+ return "supabase";
24
+ }
25
+ return "standard";
26
+ }
27
+ function getProviderDefaults(provider) {
28
+ switch (provider) {
29
+ case "neon":
30
+ return {
31
+ ssl: true,
32
+ poolMax: 5,
33
+ poolMin: 0,
34
+ idleTimeoutMs: 1e4,
35
+ connectionTimeoutMs: 2e4,
36
+ statementTimeoutMs: 3e4,
37
+ retryAttempts: 5
38
+ };
39
+ case "supabase":
40
+ return {
41
+ ssl: true,
42
+ poolMax: 5,
43
+ poolMin: 0,
44
+ idleTimeoutMs: 3e4,
45
+ connectionTimeoutMs: 15e3,
46
+ statementTimeoutMs: 15e3,
47
+ retryAttempts: 3
48
+ };
49
+ case "standard":
50
+ default:
51
+ return {
52
+ ssl: false,
53
+ poolMax: 10,
54
+ poolMin: 0,
55
+ idleTimeoutMs: 3e4,
56
+ connectionTimeoutMs: 15e3,
57
+ statementTimeoutMs: 15e3,
58
+ retryAttempts: 3
59
+ };
60
+ }
61
+ }
62
+
63
+ // src/index.ts
64
+ var VERSION = "0.1.0";
65
+ var DEFAULT_POOL_CONFIG = {
66
+ min: 0,
67
+ max: 5,
68
+ idleTimeoutMs: 3e4,
69
+ connectionTimeoutMs: 15e3
70
+ };
71
+ var PG_ERROR_CODES = {
72
+ // Class 08 - Connection Exception
73
+ "08000": "connection",
74
+ "08003": "connection",
75
+ "08006": "connection",
76
+ "08001": "connection",
77
+ "08004": "connection",
78
+ "08007": "connection",
79
+ "08P01": "connection",
80
+ // Class 23 - Integrity Constraint Violation
81
+ "23000": "constraint",
82
+ "23001": "constraint",
83
+ "23502": "not_null_violation",
84
+ "23503": "foreign_key_violation",
85
+ "23505": "unique_violation",
86
+ "23514": "check_violation",
87
+ "23P01": "constraint",
88
+ // Class 40 - Transaction Rollback
89
+ "40000": "query",
90
+ "40001": "serialization_failure",
91
+ "40002": "constraint",
92
+ "40003": "query",
93
+ "40P01": "deadlock",
94
+ // Class 57 - Operator Intervention
95
+ "57014": "timeout",
96
+ "57P01": "connection",
97
+ "57P02": "connection",
98
+ "57P03": "connection"
99
+ };
100
+ function delay(ms) {
101
+ return new Promise((resolve) => setTimeout(resolve, ms));
102
+ }
103
+ var PostgresAdapter = class extends adapterDrizzle.DrizzleAdapter {
104
+ /**
105
+ * The database dialect - always 'postgresql' for this adapter.
106
+ */
107
+ dialect = "postgresql";
108
+ /**
109
+ * Adapter configuration.
110
+ */
111
+ config;
112
+ /**
113
+ * Connection pool instance.
114
+ */
115
+ pool = null;
116
+ /**
117
+ * Connection state flag.
118
+ */
119
+ connected = false;
120
+ /**
121
+ * Auto-detected provider (Neon, Supabase, or standard).
122
+ * Set during connect() from DATABASE_URL pattern or DB_PROVIDER env var.
123
+ */
124
+ detectedProvider = "standard";
125
+ /**
126
+ * Provider-specific connection defaults. Applied as fallbacks when
127
+ * user config doesn't specify a value.
128
+ */
129
+ providerDefaults = getProviderDefaults("standard");
130
+ /**
131
+ * Creates a new PostgreSQL adapter instance.
132
+ *
133
+ * @param config - Adapter configuration
134
+ */
135
+ constructor(config) {
136
+ super();
137
+ this.config = config;
138
+ }
139
+ /**
140
+ * Establishes a connection to the PostgreSQL database.
141
+ *
142
+ * @remarks
143
+ * This method initializes the connection pool and verifies connectivity
144
+ * by executing a simple query. It is idempotent - calling it multiple
145
+ * times will not create multiple pools.
146
+ *
147
+ * @throws {DatabaseError} If connection fails
148
+ */
149
+ async connect() {
150
+ if (this.connected && this.pool) {
151
+ return;
152
+ }
153
+ const url = this.config.url || "";
154
+ this.detectedProvider = detectPostgresProvider(
155
+ url,
156
+ process.env.DB_PROVIDER
157
+ );
158
+ this.providerDefaults = getProviderDefaults(this.detectedProvider);
159
+ if (this.config.logger?.info) {
160
+ const source = process.env.DB_PROVIDER ? "(explicit)" : "(auto-detected)";
161
+ this.config.logger.info(
162
+ `PostgreSQL provider: ${this.detectedProvider} ${source}`,
163
+ {}
164
+ );
165
+ }
166
+ const retryableNodeCodes = /* @__PURE__ */ new Set([
167
+ "ETIMEDOUT",
168
+ "ECONNREFUSED",
169
+ "ECONNRESET",
170
+ "ENOTFOUND",
171
+ "EAI_AGAIN"
172
+ ]);
173
+ const maxAttempts = this.providerDefaults.retryAttempts;
174
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
175
+ try {
176
+ const poolConfig = this.buildPoolConfig();
177
+ this.pool = new pg.Pool(poolConfig);
178
+ this.pool.on("error", (err) => {
179
+ if (this.config.logger?.error) {
180
+ this.config.logger.error(err, {
181
+ context: "pool_error",
182
+ message: "Unexpected error on idle client"
183
+ });
184
+ }
185
+ });
186
+ const client = await this.pool.connect();
187
+ try {
188
+ await client.query("SELECT 1");
189
+ await versionCheck.checkDialectVersion(client, "postgresql", {
190
+ // Why: route any future variant warnings through the adapter's
191
+ // logger. PG has no recognized variants today, but this keeps
192
+ // the integration symmetric with MySQL.
193
+ onWarning: (msg) => this.config.logger?.warn?.(msg)
194
+ });
195
+ this.connected = true;
196
+ if (this.config.logger?.info) {
197
+ this.config.logger.info("PostgreSQL connection established", {
198
+ host: this.config.host ?? "from URL",
199
+ database: this.config.database ?? "from URL"
200
+ });
201
+ }
202
+ return;
203
+ } finally {
204
+ client.release();
205
+ }
206
+ } catch (error) {
207
+ if (this.pool) {
208
+ await this.pool.end().catch(() => {
209
+ });
210
+ this.pool = null;
211
+ }
212
+ const nodeError = error;
213
+ const isRetryable = nodeError.code != null && retryableNodeCodes.has(nodeError.code);
214
+ if (isRetryable && attempt < maxAttempts) {
215
+ const waitMs = 1e3 * attempt;
216
+ const msg = `PostgreSQL connection attempt ${attempt}/${maxAttempts} failed with ${nodeError.code}, retrying in ${waitMs}ms...`;
217
+ if (this.config.logger?.warn) {
218
+ this.config.logger.warn(msg);
219
+ } else {
220
+ console.warn(`[PostgresAdapter] ${msg}`);
221
+ }
222
+ await delay(waitMs);
223
+ continue;
224
+ }
225
+ throw this.classifyError(error);
226
+ }
227
+ }
228
+ }
229
+ /**
230
+ * Closes the database connection and releases all pool resources.
231
+ *
232
+ * @remarks
233
+ * This method is idempotent - calling it multiple times is safe.
234
+ * It waits for all checked-out clients to be returned before shutting down.
235
+ */
236
+ async disconnect() {
237
+ if (!this.pool) {
238
+ return;
239
+ }
240
+ try {
241
+ await this.pool.end();
242
+ if (this.config.logger?.info) {
243
+ this.config.logger.info("PostgreSQL connection closed");
244
+ }
245
+ } finally {
246
+ this.pool = null;
247
+ this.connected = false;
248
+ }
249
+ }
250
+ /**
251
+ * Checks if the adapter is currently connected.
252
+ *
253
+ * @returns True if connected and pool is available
254
+ */
255
+ isConnected() {
256
+ return this.connected && this.pool !== null;
257
+ }
258
+ /**
259
+ * Executes a raw SQL query.
260
+ *
261
+ * @param sql - SQL statement with $1, $2, ... placeholders
262
+ * @param params - Query parameters
263
+ * @returns Array of result rows
264
+ *
265
+ * @throws {DatabaseError} If query execution fails
266
+ */
267
+ async executeQuery(sql, params = []) {
268
+ const pool = this.ensurePool();
269
+ const startTime = Date.now();
270
+ const retryableNodeCodes = /* @__PURE__ */ new Set([
271
+ "ETIMEDOUT",
272
+ "ECONNRESET",
273
+ "ECONNREFUSED"
274
+ ]);
275
+ const maxQueryAttempts = 3;
276
+ for (let attempt = 1; attempt <= maxQueryAttempts; attempt++) {
277
+ try {
278
+ const result = await pool.query(sql, params);
279
+ if (this.config.logger?.query) {
280
+ const durationMs = Date.now() - startTime;
281
+ this.config.logger.query(sql, params, durationMs);
282
+ }
283
+ return result.rows;
284
+ } catch (error) {
285
+ const nodeError = error;
286
+ const isRetryable = nodeError.code != null && retryableNodeCodes.has(nodeError.code);
287
+ if (isRetryable && attempt < maxQueryAttempts) {
288
+ const waitMs = 500 * attempt;
289
+ console.warn(
290
+ `[PostgresAdapter] Query attempt ${attempt}/${maxQueryAttempts} failed with ${nodeError.code}, retrying in ${waitMs}ms...`
291
+ );
292
+ await delay(waitMs);
293
+ continue;
294
+ }
295
+ throw this.classifyError(error, sql);
296
+ }
297
+ }
298
+ throw this.classifyError(new Error("executeQuery: exhausted retries"));
299
+ }
300
+ /**
301
+ * Executes a callback within a database transaction.
302
+ *
303
+ * @remarks
304
+ * PostgreSQL supports full ACID transactions with savepoints.
305
+ * If the callback throws, the transaction is rolled back.
306
+ *
307
+ * Supports automatic retry for serialization failures (40001) and
308
+ * deadlocks (40P01) when `retryCount` is specified in options.
309
+ *
310
+ * @param callback - Function to execute within the transaction
311
+ * @param options - Transaction options (isolation level, timeout, retry)
312
+ * @returns The result of the callback
313
+ *
314
+ * @throws {DatabaseError} If transaction fails after all retries
315
+ */
316
+ async transaction(callback, options) {
317
+ const pool = this.ensurePool();
318
+ const maxAttempts = (options?.retryCount ?? 0) + 1;
319
+ const retryDelayMs = options?.retryDelayMs ?? 100;
320
+ let lastError;
321
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
322
+ const client = await pool.connect();
323
+ const startTime = Date.now();
324
+ try {
325
+ await this.beginTransaction(client, options);
326
+ const ctx = this.createTransactionContext(client);
327
+ const result = await callback(ctx);
328
+ await client.query("COMMIT");
329
+ if (this.config.logger?.debug) {
330
+ const durationMs = Date.now() - startTime;
331
+ this.config.logger.debug("Transaction committed", {
332
+ attempt,
333
+ durationMs
334
+ });
335
+ }
336
+ return result;
337
+ } catch (error) {
338
+ await client.query("ROLLBACK").catch(() => {
339
+ });
340
+ lastError = error;
341
+ const pgError = error;
342
+ const isRetryable = pgError.code === "40001" || // serialization_failure
343
+ pgError.code === "40P01";
344
+ if (isRetryable && attempt < maxAttempts) {
345
+ if (this.config.logger?.warn) {
346
+ this.config.logger.warn(
347
+ `Transaction failed with ${pgError.code}, retrying (${attempt}/${maxAttempts})`,
348
+ { code: pgError.code, attempt }
349
+ );
350
+ }
351
+ await delay(retryDelayMs * attempt);
352
+ continue;
353
+ }
354
+ throw this.classifyError(error);
355
+ } finally {
356
+ client.release();
357
+ }
358
+ }
359
+ throw this.classifyError(lastError);
360
+ }
361
+ /**
362
+ * Returns the database capabilities for PostgreSQL.
363
+ *
364
+ * @remarks
365
+ * PostgreSQL has the most comprehensive feature set of all supported
366
+ * databases, including JSONB, arrays, full-text search, and more.
367
+ */
368
+ getCapabilities() {
369
+ return {
370
+ dialect: "postgresql",
371
+ supportsJsonb: true,
372
+ supportsJson: true,
373
+ supportsArrays: true,
374
+ supportsGeneratedColumns: true,
375
+ supportsFts: true,
376
+ supportsIlike: true,
377
+ supportsReturning: true,
378
+ supportsSavepoints: true,
379
+ supportsOnConflict: true,
380
+ maxParamsPerQuery: 65535,
381
+ // PostgreSQL limit
382
+ maxIdentifierLength: 63
383
+ // PostgreSQL default
384
+ };
385
+ }
386
+ /**
387
+ * Returns connection pool statistics.
388
+ *
389
+ * @returns Pool stats or null if not connected
390
+ */
391
+ getPoolStats() {
392
+ if (!this.pool) {
393
+ return null;
394
+ }
395
+ return {
396
+ total: this.pool.totalCount,
397
+ idle: this.pool.idleCount,
398
+ waiting: this.pool.waitingCount,
399
+ active: this.pool.totalCount - this.pool.idleCount
400
+ };
401
+ }
402
+ /**
403
+ * Override insertMany for bulk insert optimization.
404
+ *
405
+ * @remarks
406
+ * Uses a single multi-row INSERT statement for better performance
407
+ * when inserting multiple records.
408
+ */
409
+ async insertMany(table, data, options) {
410
+ if (data.length === 0) {
411
+ return [];
412
+ }
413
+ if (data.length === 1) {
414
+ const result = await this.insert(table, data[0], options);
415
+ return [result];
416
+ }
417
+ const columns = Object.keys(data[0]);
418
+ const params = [];
419
+ const valuesClauses = [];
420
+ for (let i = 0; i < data.length; i++) {
421
+ const record = data[i];
422
+ const placeholders = [];
423
+ for (const col of columns) {
424
+ params.push(record[col]);
425
+ placeholders.push(`$${params.length}`);
426
+ }
427
+ valuesClauses.push(`(${placeholders.join(", ")})`);
428
+ }
429
+ const columnList = columns.map((col) => this.escapeIdentifier(col)).join(", ");
430
+ let sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columnList}) VALUES ${valuesClauses.join(", ")}`;
431
+ if (options?.returning) {
432
+ const returning = options.returning === "*" ? "*" : options.returning.map((col) => this.escapeIdentifier(col)).join(", ");
433
+ sql += ` RETURNING ${returning}`;
434
+ }
435
+ try {
436
+ return await this.executeQuery(sql, params);
437
+ } catch (error) {
438
+ throw this.handleQueryError(error, "insertMany", table);
439
+ }
440
+ }
441
+ // ============================================================
442
+ // Protected Helper Methods
443
+ // ============================================================
444
+ /**
445
+ * Ensures pool is connected and returns it.
446
+ *
447
+ * @throws {DatabaseError} If not connected
448
+ */
449
+ ensurePool() {
450
+ if (!this.pool) {
451
+ throw types.createDatabaseError({
452
+ kind: "connection",
453
+ message: "PostgresAdapter is not connected. Call connect() first."
454
+ });
455
+ }
456
+ return this.pool;
457
+ }
458
+ /**
459
+ * Return the typed Drizzle instance for PostgreSQL.
460
+ * Guarded for server-only usage and requires an active connection.
461
+ *
462
+ * @param schema - Optional schema for relational queries (db.query.*)
463
+ * @returns Drizzle ORM instance wrapping the pg pool connection
464
+ * @throws {Error} If called in browser or not connected
465
+ */
466
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
467
+ getDrizzle(schema) {
468
+ if (typeof window !== "undefined") {
469
+ throw new Error("getDrizzle() is server-only");
470
+ }
471
+ const pool = this.ensurePool();
472
+ return schema ? nodePostgres.drizzle(pool, { schema }) : nodePostgres.drizzle(pool);
473
+ }
474
+ /**
475
+ * Builds pg Pool configuration from adapter config.
476
+ */
477
+ buildPoolConfig() {
478
+ const config = {};
479
+ if (this.config.url) {
480
+ config.connectionString = this.config.url;
481
+ } else {
482
+ if (this.config.host) config.host = this.config.host;
483
+ if (this.config.port) config.port = this.config.port;
484
+ if (this.config.database) config.database = this.config.database;
485
+ if (this.config.user) config.user = this.config.user;
486
+ if (this.config.password) config.password = this.config.password;
487
+ }
488
+ config.min = this.config.pool?.min ?? this.providerDefaults.poolMin ?? DEFAULT_POOL_CONFIG.min;
489
+ config.max = this.config.pool?.max ?? this.providerDefaults.poolMax ?? DEFAULT_POOL_CONFIG.max;
490
+ config.idleTimeoutMillis = this.config.pool?.idleTimeoutMs ?? this.providerDefaults.idleTimeoutMs ?? DEFAULT_POOL_CONFIG.idleTimeoutMs;
491
+ config.connectionTimeoutMillis = this.config.pool?.connectionTimeoutMs ?? this.providerDefaults.connectionTimeoutMs ?? DEFAULT_POOL_CONFIG.connectionTimeoutMs;
492
+ config.keepAlive = true;
493
+ config.keepAliveInitialDelayMillis = 1e4;
494
+ if (this.config.ssl) {
495
+ if (typeof this.config.ssl === "boolean") {
496
+ config.ssl = this.config.ssl;
497
+ } else {
498
+ if (this.config.ssl.rejectUnauthorized === false) {
499
+ console.warn(
500
+ "[nextly/adapter-postgres] ssl.rejectUnauthorized is set to false \u2014 TLS certificates will not be validated. This is unsafe on untrusted networks. Provide a trusted `ca` cert instead, or remove the rejectUnauthorized override."
501
+ );
502
+ }
503
+ config.ssl = {
504
+ rejectUnauthorized: this.config.ssl.rejectUnauthorized,
505
+ ca: this.config.ssl.ca,
506
+ cert: this.config.ssl.cert,
507
+ key: this.config.ssl.key
508
+ };
509
+ }
510
+ } else if (this.providerDefaults.ssl) {
511
+ config.ssl = { rejectUnauthorized: true };
512
+ }
513
+ if (this.config.applicationName) {
514
+ config.application_name = this.config.applicationName;
515
+ }
516
+ if (this.config.statementTimeout) {
517
+ config.statement_timeout = this.config.statementTimeout;
518
+ }
519
+ if (this.config.queryTimeout) {
520
+ config.query_timeout = this.config.queryTimeout;
521
+ }
522
+ return config;
523
+ }
524
+ /**
525
+ * Begins a transaction with the specified options.
526
+ */
527
+ async beginTransaction(client, options) {
528
+ let beginSql = "BEGIN";
529
+ if (options?.isolationLevel) {
530
+ const isolationMap = {
531
+ "read uncommitted": "READ UNCOMMITTED",
532
+ "read committed": "READ COMMITTED",
533
+ "repeatable read": "REPEATABLE READ",
534
+ serializable: "SERIALIZABLE"
535
+ };
536
+ const level = isolationMap[options.isolationLevel];
537
+ if (level) {
538
+ beginSql += ` ISOLATION LEVEL ${level}`;
539
+ }
540
+ }
541
+ if (options?.readOnly) {
542
+ beginSql += " READ ONLY";
543
+ }
544
+ await client.query(beginSql);
545
+ if (options?.timeoutMs) {
546
+ await client.query(`SET LOCAL statement_timeout = ${options.timeoutMs}`);
547
+ }
548
+ }
549
+ /**
550
+ * Creates a TransactionContext for the given client.
551
+ */
552
+ createTransactionContext(client) {
553
+ return {
554
+ execute: async (sql, params = []) => {
555
+ const result = await client.query(sql, params);
556
+ return result.rows;
557
+ },
558
+ insert: async (table, data, options) => {
559
+ const columns = Object.keys(data);
560
+ const values = Object.values(data);
561
+ const placeholders = values.map((_, i) => `$${i + 1}`).join(", ");
562
+ let sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map((c) => this.escapeIdentifier(c)).join(", ")}) VALUES (${placeholders})`;
563
+ if (options?.returning) {
564
+ const returning = options.returning === "*" ? "*" : options.returning.map((col) => this.escapeIdentifier(col)).join(", ");
565
+ sql += ` RETURNING ${returning}`;
566
+ } else {
567
+ sql += " RETURNING *";
568
+ }
569
+ const result = await client.query(sql, values);
570
+ return result.rows[0];
571
+ },
572
+ insertMany: async (table, data, options) => {
573
+ if (data.length === 0) return [];
574
+ const columns = Object.keys(data[0]);
575
+ const params = [];
576
+ const valuesClauses = [];
577
+ for (const record of data) {
578
+ const placeholders = [];
579
+ for (const col of columns) {
580
+ params.push(record[col]);
581
+ placeholders.push(`$${params.length}`);
582
+ }
583
+ valuesClauses.push(`(${placeholders.join(", ")})`);
584
+ }
585
+ let sql = `INSERT INTO ${this.escapeIdentifier(table)} (${columns.map((c) => this.escapeIdentifier(c)).join(", ")}) VALUES ${valuesClauses.join(", ")}`;
586
+ if (options?.returning) {
587
+ const returning = options.returning === "*" ? "*" : options.returning.map((col) => this.escapeIdentifier(col)).join(", ");
588
+ sql += ` RETURNING ${returning}`;
589
+ } else {
590
+ sql += " RETURNING *";
591
+ }
592
+ const result = await client.query(sql, params);
593
+ return result.rows;
594
+ },
595
+ // TransactionContext CRUD methods delegate to the adapter's CRUD
596
+ // which uses Drizzle query API via the TableResolver.
597
+ // The Drizzle transaction is handled at a higher level.
598
+ select: async (table, options) => {
599
+ return this.select(table, options);
600
+ },
601
+ selectOne: async (table, options) => {
602
+ return this.selectOne(table, options);
603
+ },
604
+ update: async (table, data, where, options) => {
605
+ return this.update(table, data, where, options);
606
+ },
607
+ delete: async (table, where, _options) => {
608
+ return this.delete(table, where);
609
+ },
610
+ upsert: async (table, data, options) => {
611
+ return this.upsert(table, data, options);
612
+ },
613
+ savepoint: async (name) => {
614
+ await client.query(`SAVEPOINT ${this.escapeIdentifier(name)}`);
615
+ },
616
+ rollbackToSavepoint: async (name) => {
617
+ await client.query(
618
+ `ROLLBACK TO SAVEPOINT ${this.escapeIdentifier(name)}`
619
+ );
620
+ },
621
+ releaseSavepoint: async (name) => {
622
+ await client.query(`RELEASE SAVEPOINT ${this.escapeIdentifier(name)}`);
623
+ }
624
+ };
625
+ }
626
+ /**
627
+ * Classifies a PostgreSQL error into a DatabaseError.
628
+ *
629
+ * @param error - Original error from pg
630
+ * @param sql - SQL statement that caused the error (optional)
631
+ * @returns DatabaseError with proper classification
632
+ */
633
+ classifyError(error, sql) {
634
+ if (types.isDatabaseError(error)) return error;
635
+ const pgError = error;
636
+ const kind = pgError.code && PG_ERROR_CODES[pgError.code] || "unknown";
637
+ let message = pgError.message ?? String(error);
638
+ if (sql && kind === "query") {
639
+ message = `Query failed: ${message}`;
640
+ }
641
+ return types.createDatabaseError({
642
+ kind,
643
+ message,
644
+ code: pgError.code,
645
+ constraint: pgError.constraint,
646
+ table: pgError.table,
647
+ column: pgError.column,
648
+ detail: pgError.detail,
649
+ hint: pgError.hint,
650
+ cause: error instanceof Error ? error : void 0
651
+ });
652
+ }
653
+ /**
654
+ * Override handleQueryError to use PostgreSQL-specific classification.
655
+ */
656
+ handleQueryError(error, operation, table) {
657
+ const dbError = this.classifyError(error);
658
+ if (!dbError.message.includes(operation)) {
659
+ dbError.message = `${operation} operation failed on table '${table}': ${dbError.message}`;
660
+ }
661
+ if (!dbError.table) {
662
+ dbError.table = table;
663
+ }
664
+ return dbError;
665
+ }
666
+ };
667
+ function createPostgresAdapter(config) {
668
+ return new PostgresAdapter(config);
669
+ }
670
+ function isPostgresAdapter(value) {
671
+ return value instanceof PostgresAdapter;
672
+ }
673
+
674
+ exports.PostgresAdapter = PostgresAdapter;
675
+ exports.VERSION = VERSION;
676
+ exports.createPostgresAdapter = createPostgresAdapter;
677
+ exports.isPostgresAdapter = isPostgresAdapter;
678
+ //# sourceMappingURL=index.cjs.map
679
+ //# sourceMappingURL=index.cjs.map