@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.
- package/dist/postgres.d.ts +21 -0
- package/dist/postgres.d.ts.map +1 -1
- package/dist/postgres.js +85 -24
- package/dist/postgres.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/postgres.ts +100 -27
- package/src/types.ts +7 -0
package/dist/postgres.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/postgres.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"
|
|
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
|
-
//
|
|
49
|
-
|
|
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
|
|
79
|
-
//
|
|
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:
|
|
142
|
+
client: resilientSQL,
|
|
82
143
|
schema: config?.schema,
|
|
83
144
|
logger: config?.logger,
|
|
84
145
|
});
|
package/dist/postgres.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"
|
|
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`.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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
|
+
"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.
|
|
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.
|
|
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
|
-
//
|
|
53
|
-
|
|
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
|
|
88
|
-
//
|
|
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:
|
|
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`.
|