@agentuity/drizzle 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,25 @@
1
+ import { SQL as BunSQL } from 'bun';
2
+ import { type CallablePostgresClient, type PostgresConfig } from '@agentuity/postgres';
1
3
  import type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
4
+ /**
5
+ * Resolves the PostgreSQL client configuration from Drizzle config options.
6
+ *
7
+ * URL priority chain: `connection.url` > `url` > `connectionString` > `process.env.DATABASE_URL`
8
+ *
9
+ * @internal Exported for testing — not part of the public package API.
10
+ */
11
+ export declare function resolvePostgresClientConfig<TSchema extends Record<string, unknown> = Record<string, never>>(config?: PostgresDrizzleConfig<TSchema>): PostgresConfig;
12
+ /**
13
+ * Creates a dynamic SQL proxy that always delegates to the PostgresClient's
14
+ * current raw connection. This ensures that after automatic reconnection,
15
+ * Drizzle ORM uses the fresh connection instead of a stale reference.
16
+ *
17
+ * The proxy also wraps `unsafe()` calls with the client's retry logic,
18
+ * providing automatic retry on transient connection errors.
19
+ *
20
+ * @internal Exported for testing — not part of the public package API.
21
+ */
22
+ export declare function createResilientSQLProxy(client: CallablePostgresClient): InstanceType<typeof BunSQL>;
2
23
  /**
3
24
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
4
25
  *
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC9D,MAAM,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,CAoDnE"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,MAAM,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAY,KAAK,sBAAsB,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,KAAK,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEtE;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAC1C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC9D,MAAM,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,cAAc,CA0BzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,sBAAsB,GAC5B,YAAY,CAAC,OAAO,MAAM,CAAC,CAqC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC9D,MAAM,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,CAoCnE"}
package/dist/postgres.js CHANGED
@@ -1,5 +1,81 @@
1
1
  import { drizzle } from 'drizzle-orm/bun-sql';
2
2
  import { postgres } from '@agentuity/postgres';
3
+ /**
4
+ * Resolves the PostgreSQL client configuration from Drizzle config options.
5
+ *
6
+ * URL priority chain: `connection.url` > `url` > `connectionString` > `process.env.DATABASE_URL`
7
+ *
8
+ * @internal Exported for testing — not part of the public package API.
9
+ */
10
+ export function resolvePostgresClientConfig(config) {
11
+ // Clone the connection config to avoid mutating the caller's object
12
+ const clientConfig = config?.connection ? { ...config.connection } : {};
13
+ // Resolve URL using priority chain
14
+ if (!clientConfig.url) {
15
+ if (config?.url) {
16
+ clientConfig.url = config.url;
17
+ }
18
+ else if (config?.connectionString) {
19
+ clientConfig.url = config.connectionString;
20
+ }
21
+ else if (process.env.DATABASE_URL) {
22
+ clientConfig.url = process.env.DATABASE_URL;
23
+ }
24
+ }
25
+ // Add reconnection configuration
26
+ if (config?.reconnect) {
27
+ clientConfig.reconnect = config.reconnect;
28
+ }
29
+ // Add callbacks
30
+ if (config?.onReconnected) {
31
+ clientConfig.onreconnected = config.onReconnected;
32
+ }
33
+ return clientConfig;
34
+ }
35
+ /**
36
+ * Creates a dynamic SQL proxy that always delegates to the PostgresClient's
37
+ * current raw connection. This ensures that after automatic reconnection,
38
+ * Drizzle ORM uses the fresh connection instead of a stale reference.
39
+ *
40
+ * The proxy also wraps `unsafe()` calls with the client's retry logic,
41
+ * providing automatic retry on transient connection errors.
42
+ *
43
+ * @internal Exported for testing — not part of the public package API.
44
+ */
45
+ export function createResilientSQLProxy(client) {
46
+ return new Proxy({}, {
47
+ get(_target, prop, _receiver) {
48
+ // Always resolve from the CURRENT raw connection (changes after reconnect)
49
+ const raw = client.raw;
50
+ if (prop === 'unsafe') {
51
+ // Wrap unsafe() with retry logic for resilient queries.
52
+ // Returns a thenable that also supports .values() chaining,
53
+ // matching the SQLQuery interface that Drizzle expects:
54
+ // client.unsafe(query, params) → Promise<rows>
55
+ // client.unsafe(query, params).values() → Promise<rows>
56
+ return (query, params) => {
57
+ const makeExecutor = (useValues) => client.executeWithRetry(async () => {
58
+ // Re-resolve raw inside retry to get post-reconnect instance
59
+ const currentRaw = client.raw;
60
+ const q = currentRaw.unsafe(query, params);
61
+ return useValues ? q.values() : q;
62
+ });
63
+ // Return a thenable with .values() to match Bun's SQLQuery interface
64
+ const result = makeExecutor(false);
65
+ return Object.assign(result, {
66
+ values: () => makeExecutor(true),
67
+ });
68
+ };
69
+ }
70
+ const value = raw[prop];
71
+ if (typeof value === 'function') {
72
+ // Bind to raw so `this` is correct inside begin(), savepoint(), etc.
73
+ return value.bind(raw);
74
+ }
75
+ return value;
76
+ },
77
+ });
78
+ }
3
79
  /**
4
80
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
5
81
  *
@@ -45,27 +121,8 @@ import { postgres } from '@agentuity/postgres';
45
121
  * ```
46
122
  */
47
123
  export function createPostgresDrizzle(config) {
48
- // Build postgres client configuration by cloning the connection config
49
- // to avoid mutating the caller's object
50
- const clientConfig = config?.connection ? { ...config.connection } : {};
51
- // Use connectionString only if no url is already present on the cloned config
52
- // This ensures connection (when provided) keeps precedence over connectionString
53
- if (!clientConfig.url) {
54
- if (config?.connectionString) {
55
- clientConfig.url = config.connectionString;
56
- }
57
- else if (process.env.DATABASE_URL) {
58
- clientConfig.url = process.env.DATABASE_URL;
59
- }
60
- }
61
- // Add reconnection configuration
62
- if (config?.reconnect) {
63
- clientConfig.reconnect = config.reconnect;
64
- }
65
- // Add callbacks
66
- if (config?.onReconnected) {
67
- clientConfig.onreconnected = config.onReconnected;
68
- }
124
+ // Resolve the postgres client configuration
125
+ const clientConfig = resolvePostgresClientConfig(config);
69
126
  // Create the postgres client
70
127
  const client = postgres(clientConfig);
71
128
  // Wait for connection before calling onConnect callback
@@ -75,10 +132,14 @@ export function createPostgresDrizzle(config) {
75
132
  config.onConnect();
76
133
  });
77
134
  }
78
- // Create Drizzle instance using the client's raw SQL connection
79
- // The bun-sql driver accepts a client that implements the Bun.SQL interface
135
+ // Create a resilient proxy that always delegates to the current raw SQL
136
+ // connection. This ensures that after reconnection, Drizzle automatically
137
+ // uses the new connection instead of the stale one.
138
+ const resilientSQL = createResilientSQLProxy(client);
139
+ // Create Drizzle instance using the resilient proxy instead of a static
140
+ // reference to client.raw, which would become stale after reconnection.
80
141
  const db = drizzle({
81
- client: client.raw,
142
+ client: resilientSQL,
82
143
  schema: config?.schema,
83
144
  logger: config?.logger,
84
145
  });
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAA+B,MAAM,qBAAqB,CAAC;AAG5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,UAAU,qBAAqB,CAEnC,MAAuC;IACxC,uEAAuE;IACvE,wCAAwC;IACxC,MAAM,YAAY,GAAG,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,8EAA8E;IAC9E,iFAAiF;IACjF,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,EAAE,gBAAgB,EAAE,CAAC;YAC9B,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,gBAAgB,CAAC;QAC5C,CAAC;aAAM,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YACrC,YAAY,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAC7C,CAAC;IACF,CAAC;IAED,iCAAiC;IACjC,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;QACvB,YAAY,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAC3C,CAAC;IAED,gBAAgB;IAChB,IAAI,MAAM,EAAE,aAAa,EAAE,CAAC;QAC3B,YAAY,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IACnD,CAAC;IAED,6BAA6B;IAC7B,MAAM,MAAM,GAA2B,QAAQ,CAAC,YAAY,CAAC,CAAC;IAE9D,wDAAwD;IACxD,8EAA8E;IAC9E,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;QACvB,MAAM,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YACpC,MAAM,CAAC,SAAU,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,gEAAgE;IAChE,4EAA4E;IAC5E,MAAM,EAAE,GAAG,OAAO,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,GAAG;QAClB,MAAM,EAAE,MAAM,EAAE,MAAM;QACtB,MAAM,EAAE,MAAM,EAAE,MAAM;KACtB,CAAC,CAAC;IAEH,gCAAgC;IAChC,OAAO;QACN,EAAE;QACF,MAAM;QACN,KAAK,EAAE,KAAK,IAAI,EAAE;YACjB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -7,6 +7,12 @@ import type { PostgresConfig, ReconnectConfig, CallablePostgresClient } from '@a
7
7
  * @template TSchema - The Drizzle schema type
8
8
  */
9
9
  export interface PostgresDrizzleConfig<TSchema extends Record<string, unknown> = Record<string, never>> {
10
+ /**
11
+ * PostgreSQL connection URL.
12
+ * Shorthand for `connection.url`.
13
+ * If not provided, falls back to `connectionString`, then `process.env.DATABASE_URL`.
14
+ */
15
+ url?: string;
10
16
  /**
11
17
  * PostgreSQL connection string.
12
18
  * If not provided, uses `process.env.DATABASE_URL`.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEnG;;;;GAIG;AACH,MAAM,WAAW,qBAAqB,CACrC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAE/D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAC/F;;OAEG;IACH,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAE5B;;;OAGG;IACH,MAAM,EAAE,sBAAsB,CAAC;IAE/B;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEnG;;;;GAIG;AACH,MAAM,WAAW,qBAAqB,CACrC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAE/D;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IAEvB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAC/F;;OAEG;IACH,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAE5B;;;OAGG;IACH,MAAM,EAAE,sBAAsB,CAAC;IAE/B;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/drizzle",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -31,12 +31,12 @@
31
31
  "prepublishOnly": "bun run clean && bun run build"
32
32
  },
33
33
  "dependencies": {
34
- "@agentuity/postgres": "1.0.3",
34
+ "@agentuity/postgres": "1.0.5",
35
35
  "drizzle-orm": "^0.45.0",
36
36
  "better-auth": "^1.4.9"
37
37
  },
38
38
  "devDependencies": {
39
- "@agentuity/test-utils": "1.0.3",
39
+ "@agentuity/test-utils": "1.0.5",
40
40
  "@types/bun": "latest",
41
41
  "bun-types": "latest",
42
42
  "typescript": "^5.9.0"
package/src/postgres.ts CHANGED
@@ -1,7 +1,96 @@
1
+ import { SQL as BunSQL } from 'bun';
1
2
  import { drizzle } from 'drizzle-orm/bun-sql';
2
- import { postgres, type CallablePostgresClient } from '@agentuity/postgres';
3
+ import { postgres, type CallablePostgresClient, type PostgresConfig } from '@agentuity/postgres';
3
4
  import type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
4
5
 
6
+ /**
7
+ * Resolves the PostgreSQL client configuration from Drizzle config options.
8
+ *
9
+ * URL priority chain: `connection.url` > `url` > `connectionString` > `process.env.DATABASE_URL`
10
+ *
11
+ * @internal Exported for testing — not part of the public package API.
12
+ */
13
+ export function resolvePostgresClientConfig<
14
+ TSchema extends Record<string, unknown> = Record<string, never>,
15
+ >(config?: PostgresDrizzleConfig<TSchema>): PostgresConfig {
16
+ // Clone the connection config to avoid mutating the caller's object
17
+ const clientConfig: PostgresConfig = config?.connection ? { ...config.connection } : {};
18
+
19
+ // Resolve URL using priority chain
20
+ if (!clientConfig.url) {
21
+ if (config?.url) {
22
+ clientConfig.url = config.url;
23
+ } else if (config?.connectionString) {
24
+ clientConfig.url = config.connectionString;
25
+ } else if (process.env.DATABASE_URL) {
26
+ clientConfig.url = process.env.DATABASE_URL;
27
+ }
28
+ }
29
+
30
+ // Add reconnection configuration
31
+ if (config?.reconnect) {
32
+ clientConfig.reconnect = config.reconnect;
33
+ }
34
+
35
+ // Add callbacks
36
+ if (config?.onReconnected) {
37
+ clientConfig.onreconnected = config.onReconnected;
38
+ }
39
+
40
+ return clientConfig;
41
+ }
42
+
43
+ /**
44
+ * Creates a dynamic SQL proxy that always delegates to the PostgresClient's
45
+ * current raw connection. This ensures that after automatic reconnection,
46
+ * Drizzle ORM uses the fresh connection instead of a stale reference.
47
+ *
48
+ * The proxy also wraps `unsafe()` calls with the client's retry logic,
49
+ * providing automatic retry on transient connection errors.
50
+ *
51
+ * @internal Exported for testing — not part of the public package API.
52
+ */
53
+ export function createResilientSQLProxy(
54
+ client: CallablePostgresClient
55
+ ): InstanceType<typeof BunSQL> {
56
+ return new Proxy({} as InstanceType<typeof BunSQL>, {
57
+ get(_target, prop, _receiver) {
58
+ // Always resolve from the CURRENT raw connection (changes after reconnect)
59
+ const raw = client.raw;
60
+
61
+ if (prop === 'unsafe') {
62
+ // Wrap unsafe() with retry logic for resilient queries.
63
+ // Returns a thenable that also supports .values() chaining,
64
+ // matching the SQLQuery interface that Drizzle expects:
65
+ // client.unsafe(query, params) → Promise<rows>
66
+ // client.unsafe(query, params).values() → Promise<rows>
67
+ return (query: string, params?: unknown[]) => {
68
+ const makeExecutor = (useValues: boolean) =>
69
+ client.executeWithRetry(async () => {
70
+ // Re-resolve raw inside retry to get post-reconnect instance
71
+ const currentRaw = client.raw;
72
+ const q = currentRaw.unsafe(query, params);
73
+ return useValues ? q.values() : q;
74
+ });
75
+
76
+ // Return a thenable with .values() to match Bun's SQLQuery interface
77
+ const result = makeExecutor(false);
78
+ return Object.assign(result, {
79
+ values: () => makeExecutor(true),
80
+ });
81
+ };
82
+ }
83
+
84
+ const value = (raw as unknown as Record<string | symbol, unknown>)[prop];
85
+ if (typeof value === 'function') {
86
+ // Bind to raw so `this` is correct inside begin(), savepoint(), etc.
87
+ return (value as (...args: unknown[]) => unknown).bind(raw);
88
+ }
89
+ return value;
90
+ },
91
+ });
92
+ }
93
+
5
94
  /**
6
95
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
7
96
  *
@@ -49,29 +138,8 @@ import type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
49
138
  export function createPostgresDrizzle<
50
139
  TSchema extends Record<string, unknown> = Record<string, never>,
51
140
  >(config?: PostgresDrizzleConfig<TSchema>): PostgresDrizzle<TSchema> {
52
- // Build postgres client configuration by cloning the connection config
53
- // to avoid mutating the caller's object
54
- const clientConfig = config?.connection ? { ...config.connection } : {};
55
-
56
- // Use connectionString only if no url is already present on the cloned config
57
- // This ensures connection (when provided) keeps precedence over connectionString
58
- if (!clientConfig.url) {
59
- if (config?.connectionString) {
60
- clientConfig.url = config.connectionString;
61
- } else if (process.env.DATABASE_URL) {
62
- clientConfig.url = process.env.DATABASE_URL;
63
- }
64
- }
65
-
66
- // Add reconnection configuration
67
- if (config?.reconnect) {
68
- clientConfig.reconnect = config.reconnect;
69
- }
70
-
71
- // Add callbacks
72
- if (config?.onReconnected) {
73
- clientConfig.onreconnected = config.onReconnected;
74
- }
141
+ // Resolve the postgres client configuration
142
+ const clientConfig = resolvePostgresClientConfig(config);
75
143
 
76
144
  // Create the postgres client
77
145
  const client: CallablePostgresClient = postgres(clientConfig);
@@ -84,10 +152,15 @@ export function createPostgresDrizzle<
84
152
  });
85
153
  }
86
154
 
87
- // Create Drizzle instance using the client's raw SQL connection
88
- // The bun-sql driver accepts a client that implements the Bun.SQL interface
155
+ // Create a resilient proxy that always delegates to the current raw SQL
156
+ // connection. This ensures that after reconnection, Drizzle automatically
157
+ // uses the new connection instead of the stale one.
158
+ const resilientSQL = createResilientSQLProxy(client);
159
+
160
+ // Create Drizzle instance using the resilient proxy instead of a static
161
+ // reference to client.raw, which would become stale after reconnection.
89
162
  const db = drizzle({
90
- client: client.raw,
163
+ client: resilientSQL,
91
164
  schema: config?.schema,
92
165
  logger: config?.logger,
93
166
  });
package/src/types.ts CHANGED
@@ -10,6 +10,13 @@ import type { PostgresConfig, ReconnectConfig, CallablePostgresClient } from '@a
10
10
  export interface PostgresDrizzleConfig<
11
11
  TSchema extends Record<string, unknown> = Record<string, never>,
12
12
  > {
13
+ /**
14
+ * PostgreSQL connection URL.
15
+ * Shorthand for `connection.url`.
16
+ * If not provided, falls back to `connectionString`, then `process.env.DATABASE_URL`.
17
+ */
18
+ url?: string;
19
+
13
20
  /**
14
21
  * PostgreSQL connection string.
15
22
  * If not provided, uses `process.env.DATABASE_URL`.