@agentuity/drizzle 1.0.6 → 1.0.8

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.d.ts CHANGED
@@ -30,10 +30,11 @@
30
30
  *
31
31
  * @packageDocumentation
32
32
  */
33
- export { createPostgresDrizzle } from './postgres';
33
+ export { createPostgresDrizzle, drizzle } from './postgres';
34
34
  export type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
35
35
  export { postgres, PostgresClient, type CallablePostgresClient, type PostgresConfig, type ReconnectConfig, type ConnectionStats, type TLSConfig, type TransactionOptions, type ReserveOptions, } from '@agentuity/postgres';
36
36
  export { sql, eq, and, or, not, desc, asc, gt, gte, lt, lte, ne, isNull, isNotNull, inArray, notInArray, between, like, ilike, relations, } from 'drizzle-orm';
37
+ export { BunSQLDatabase, BunSQLPreparedQuery, BunSQLSession, BunSQLTransaction, } from 'drizzle-orm/bun-sql';
37
38
  export { pgTable, pgSchema, pgEnum, pgView, bigint, bigserial, boolean, vector, char, cidr, customType, date, doublePrecision, inet, integer, interval, json, jsonb, macaddr, macaddr8, numeric, real, serial, smallint, smallserial, text, time, timestamp, uuid, varchar, primaryKey, foreignKey, unique, uniqueIndex, index, check, } from 'drizzle-orm/pg-core';
38
39
  export { drizzleAdapter } from 'better-auth/adapters/drizzle';
39
40
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGnD,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EACN,QAAQ,EACR,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACN,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,IAAI,EACJ,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,OAAO,EACP,IAAI,EACJ,KAAK,EACL,SAAS,GACT,MAAM,aAAa,CAAC;AAGrB,OAAO,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,EAEN,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,OAAO,EAEP,UAAU,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,KAAK,EACL,KAAK,GACL,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5D,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,EACN,QAAQ,EACR,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACN,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,IAAI,EACJ,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,OAAO,EACP,IAAI,EACJ,KAAK,EACL,SAAS,GACT,MAAM,aAAa,CAAC;AAGrB,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,EAEN,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,OAAO,EAEP,UAAU,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,KAAK,EACL,KAAK,GACL,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
package/dist/index.js CHANGED
@@ -31,11 +31,13 @@
31
31
  * @packageDocumentation
32
32
  */
33
33
  // Main factory function
34
- export { createPostgresDrizzle } from './postgres';
34
+ export { createPostgresDrizzle, drizzle } from './postgres';
35
35
  // Re-export from @agentuity/postgres for convenience
36
36
  export { postgres, PostgresClient, } from '@agentuity/postgres';
37
37
  // Re-export common Drizzle utilities for convenience
38
38
  export { sql, eq, and, or, not, desc, asc, gt, gte, lt, lte, ne, isNull, isNotNull, inArray, notInArray, between, like, ilike, relations, } from 'drizzle-orm';
39
+ // Re-export Bun SQL driver types for compatibility
40
+ export { BunSQLDatabase, BunSQLPreparedQuery, BunSQLSession, BunSQLTransaction, } from 'drizzle-orm/bun-sql';
39
41
  // Re-export pg-core table and column definitions
40
42
  export { pgTable, pgSchema, pgEnum, pgView,
41
43
  // Column types
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,wBAAwB;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKnD,qDAAqD;AACrD,OAAO,EACN,QAAQ,EACR,cAAc,GAQd,MAAM,qBAAqB,CAAC;AAE7B,qDAAqD;AACrD,OAAO,EACN,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,IAAI,EACJ,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,OAAO,EACP,IAAI,EACJ,KAAK,EACL,SAAS,GACT,MAAM,aAAa,CAAC;AAErB,iDAAiD;AACjD,OAAO,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM;AACN,eAAe;AACf,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,OAAO;AACP,0BAA0B;AAC1B,UAAU,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,KAAK,EACL,KAAK,GACL,MAAM,qBAAqB,CAAC;AAE7B,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,wBAAwB;AACxB,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK5D,qDAAqD;AACrD,OAAO,EACN,QAAQ,EACR,cAAc,GAQd,MAAM,qBAAqB,CAAC;AAE7B,qDAAqD;AACrD,OAAO,EACN,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,IAAI,EACJ,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,GAAG,EACH,EAAE,EACF,MAAM,EACN,SAAS,EACT,OAAO,EACP,UAAU,EACV,OAAO,EACP,IAAI,EACJ,KAAK,EACL,SAAS,GACT,MAAM,aAAa,CAAC;AAErB,mDAAmD;AACnD,OAAO,EACN,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,iDAAiD;AACjD,OAAO,EACN,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM;AACN,eAAe;AACf,MAAM,EACN,SAAS,EACT,OAAO,EACP,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,UAAU,EACV,IAAI,EACJ,eAAe,EACf,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,IAAI,EACJ,OAAO;AACP,0BAA0B;AAC1B,UAAU,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,KAAK,EACL,KAAK,GACL,MAAM,qBAAqB,CAAC;AAE7B,qEAAqE;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC"}
@@ -1,4 +1,6 @@
1
- import { SQL as BunSQL } from 'bun';
1
+ import { SQL as BunSQL, type SQL as BunSQLClient, type SQLOptions } from 'bun';
2
+ import { type BunSQLDatabase } from 'drizzle-orm/bun-sql';
3
+ import type { DrizzleConfig } from 'drizzle-orm';
2
4
  import { type CallablePostgresClient, type PostgresConfig } from '@agentuity/postgres';
3
5
  import type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
4
6
  /**
@@ -20,6 +22,24 @@ export declare function resolvePostgresClientConfig<TSchema extends Record<strin
20
22
  * @internal Exported for testing — not part of the public package API.
21
23
  */
22
24
  export declare function createResilientSQLProxy(client: CallablePostgresClient): InstanceType<typeof BunSQL>;
25
+ type DrizzleConnectionConfig = string | ({
26
+ url?: string;
27
+ } & SQLOptions);
28
+ declare function _drizzle<TSchema extends Record<string, unknown> = Record<string, never>, TClient extends BunSQLClient = BunSQLClient>(...params: [TClient | string] | [TClient | string, DrizzleConfig<TSchema>] | [DrizzleConfig<TSchema> & ({
29
+ connection: DrizzleConnectionConfig;
30
+ } | {
31
+ client: TClient;
32
+ })]): BunSQLDatabase<TSchema> & {
33
+ $client: TClient;
34
+ };
35
+ declare namespace _drizzle {
36
+ var mock: <TSchema extends Record<string, unknown> = Record<string, never>>(config?: DrizzleConfig<TSchema>) => BunSQLDatabase<TSchema> & {
37
+ $client: "$client is not available on drizzle.mock()";
38
+ };
39
+ }
40
+ export declare const drizzle: typeof _drizzle & {
41
+ mock: typeof _drizzle.mock;
42
+ };
23
43
  /**
24
44
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
25
45
  *
@@ -65,4 +85,5 @@ export declare function createResilientSQLProxy(client: CallablePostgresClient):
65
85
  * ```
66
86
  */
67
87
  export declare function createPostgresDrizzle<TSchema extends Record<string, unknown> = Record<string, never>>(config?: PostgresDrizzleConfig<TSchema>): PostgresDrizzle<TSchema>;
88
+ export {};
68
89
  //# sourceMappingURL=postgres.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,MAAM,EAAE,KAAK,GAAG,IAAI,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAC/E,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACtF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,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;AA8FD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACtC,MAAM,EAAE,sBAAsB,GAC5B,YAAY,CAAC,OAAO,MAAM,CAAC,CA4D7B;AAED,KAAK,uBAAuB,GAAG,MAAM,GAAG,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAAC,CAAC;AAoHxE,iBAAS,QAAQ,CAChB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC/D,OAAO,SAAS,YAAY,GAAG,YAAY,EAE3C,GAAG,MAAM,EACN,CAAC,OAAO,GAAG,MAAM,CAAC,GAClB,CAAC,OAAO,GAAG,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,GAC1C,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC;IAAE,UAAU,EAAE,uBAAuB,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,GAC3F,cAAc,CAAC,OAAO,CAAC,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAwBhD;kBAhCQ,QAAQ;eAkCA,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,mCAC9C,aAAa,CAAC,OAAO,CAAC,KAC7B,cAAc,CAAC,OAAO,CAAC,GAAG;QAAE,OAAO,EAAE,4CAA4C,CAAA;KAAE;;AAStF,eAAO,MAAM,OAAO,EAAe,OAAO,QAAQ,GAAG;IACpD,IAAI,EAAE,OAAO,QAAQ,CAAC,IAAI,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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,4 +1,5 @@
1
- import { drizzle } from 'drizzle-orm/bun-sql';
1
+ import { drizzle as upstreamDrizzle } from 'drizzle-orm/bun-sql';
2
+ import { isConfig } from 'drizzle-orm/utils';
2
3
  import { postgres } from '@agentuity/postgres';
3
4
  /**
4
5
  * Resolves the PostgreSQL client configuration from Drizzle config options.
@@ -32,6 +33,85 @@ export function resolvePostgresClientConfig(config) {
32
33
  }
33
34
  return clientConfig;
34
35
  }
36
+ /**
37
+ * Strips leading whitespace and SQL comments (block and line) from a query string.
38
+ * Returns the remaining query text starting at the first non-comment token.
39
+ */
40
+ const LEADING_COMMENTS_RE = /^(?:\s+|\/\*[\s\S]*?\*\/|--[^\n]*\n)*/;
41
+ /**
42
+ * Determines whether a SQL query is a non-retryable INSERT statement.
43
+ *
44
+ * Handles two patterns:
45
+ * 1. Direct INSERT: `INSERT INTO ...` (with optional leading comments/whitespace)
46
+ * 2. CTE INSERT: `WITH cte AS (...) INSERT INTO ...` — scans past the WITH clause
47
+ * by tracking parenthesis depth to skip CTE subexpressions, then checks
48
+ * if the first top-level DML keyword is INSERT.
49
+ *
50
+ * @see https://github.com/agentuity/sdk/issues/911
51
+ */
52
+ function isNonRetryableInsert(query) {
53
+ // Strip leading whitespace and SQL comments
54
+ const stripped = query.replace(LEADING_COMMENTS_RE, '');
55
+ // Fast path: direct INSERT statement
56
+ if (/^INSERT\s/i.test(stripped)) {
57
+ return true;
58
+ }
59
+ // Check for WITH (CTE) prefix
60
+ if (!/^WITH\s/i.test(stripped)) {
61
+ return false;
62
+ }
63
+ // Scan past the CTE clause to find the first top-level DML keyword.
64
+ // We track parenthesis depth so we skip CTE subexpressions like
65
+ // "WITH cte AS (SELECT ... INSERT ...)" without false-matching the
66
+ // INSERT inside the parens.
67
+ let depth = 0;
68
+ let i = 4; // skip past "WITH"
69
+ const len = stripped.length;
70
+ while (i < len) {
71
+ const ch = stripped[i];
72
+ if (ch === '(') {
73
+ depth++;
74
+ i++;
75
+ continue;
76
+ }
77
+ if (ch === ')') {
78
+ depth--;
79
+ i++;
80
+ continue;
81
+ }
82
+ // Only inspect keywords at top level (depth === 0)
83
+ if (depth === 0) {
84
+ // Skip whitespace at top level
85
+ if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
86
+ i++;
87
+ continue;
88
+ }
89
+ // Skip commas between CTEs: WITH a AS (...), b AS (...)
90
+ if (ch === ',') {
91
+ i++;
92
+ continue;
93
+ }
94
+ // Check for DML keywords at this position.
95
+ // We look for INSERT, UPDATE, DELETE, or SELECT — the first one
96
+ // we find at top level determines whether this is retryable.
97
+ const rest = stripped.substring(i);
98
+ const dmlMatch = /^(INSERT|UPDATE|DELETE|SELECT)\s/i.exec(rest);
99
+ if (dmlMatch) {
100
+ return dmlMatch[1].toUpperCase() === 'INSERT';
101
+ }
102
+ // Skip over any other word (e.g., CTE names, AS keyword, RECURSIVE)
103
+ // by advancing past alphanumeric/underscore characters
104
+ if (/\w/.test(ch)) {
105
+ while (i < len && /\w/.test(stripped[i])) {
106
+ i++;
107
+ }
108
+ continue;
109
+ }
110
+ }
111
+ i++;
112
+ }
113
+ return false;
114
+ }
35
115
  /**
36
116
  * Creates a dynamic SQL proxy that always delegates to the PostgresClient's
37
117
  * current raw connection. This ensures that after automatic reconnection,
@@ -47,6 +127,9 @@ export function createResilientSQLProxy(client) {
47
127
  get(_target, prop, _receiver) {
48
128
  // Always resolve from the CURRENT raw connection (changes after reconnect)
49
129
  const raw = client.raw;
130
+ if (prop === 'close') {
131
+ return () => client.close();
132
+ }
50
133
  if (prop === 'unsafe') {
51
134
  // Wrap unsafe() with retry logic for resilient queries.
52
135
  // Returns a thenable that also supports .values() chaining,
@@ -54,6 +137,23 @@ export function createResilientSQLProxy(client) {
54
137
  // client.unsafe(query, params) → Promise<rows>
55
138
  // client.unsafe(query, params).values() → Promise<rows>
56
139
  return (query, params) => {
140
+ // INSERT statements (including CTE-based) are NOT retried to prevent
141
+ // duplicate rows. If an INSERT succeeds on the server but the connection
142
+ // drops before the response, retrying would re-execute it — creating a
143
+ // duplicate row when the primary key is server-generated.
144
+ // See: https://github.com/agentuity/sdk/issues/911
145
+ const isInsert = isNonRetryableInsert(query);
146
+ if (isInsert) {
147
+ const makeDirectExecutor = (useValues) => {
148
+ const currentRaw = client.raw;
149
+ const q = currentRaw.unsafe(query, params);
150
+ return useValues ? q.values() : q;
151
+ };
152
+ const result = makeDirectExecutor(false);
153
+ return Object.assign(result, {
154
+ values: () => makeDirectExecutor(true),
155
+ });
156
+ }
57
157
  const makeExecutor = (useValues) => client.executeWithRetry(async () => {
58
158
  // Re-resolve raw inside retry to get post-reconnect instance
59
159
  const currentRaw = client.raw;
@@ -76,6 +176,114 @@ export function createResilientSQLProxy(client) {
76
176
  },
77
177
  });
78
178
  }
179
+ function isCallablePostgresClient(value) {
180
+ return (typeof value === 'function' &&
181
+ value !== null &&
182
+ 'raw' in value &&
183
+ typeof value.executeWithRetry === 'function');
184
+ }
185
+ function createProxyClientFromSql(client) {
186
+ // Bun SQL instances are callable as tagged template literals.
187
+ // Create a function that forwards calls to the client.
188
+ const proxy = (async (strings, ...values) => {
189
+ // Forward tagged template to the Bun SQL client directly
190
+ return client(strings, ...values);
191
+ });
192
+ Object.defineProperties(proxy, {
193
+ raw: {
194
+ get: () => client,
195
+ enumerable: true,
196
+ },
197
+ });
198
+ proxy.executeWithRetry = async (operation) => operation();
199
+ proxy.close = async () => {
200
+ const close = client.close;
201
+ if (typeof close === 'function') {
202
+ await close.call(client);
203
+ }
204
+ };
205
+ return proxy;
206
+ }
207
+ function extractPostgresConfigFromSql(client) {
208
+ const options = client.options;
209
+ if (!options || typeof options !== 'object') {
210
+ return undefined;
211
+ }
212
+ const config = {};
213
+ const keys = [
214
+ 'url',
215
+ 'hostname',
216
+ 'port',
217
+ 'username',
218
+ 'password',
219
+ 'database',
220
+ 'tls',
221
+ 'max',
222
+ 'idleTimeout',
223
+ 'connectionTimeout',
224
+ ];
225
+ for (const key of keys) {
226
+ if (key in options) {
227
+ config[key] = options[key];
228
+ }
229
+ }
230
+ return Object.keys(config).length > 0 ? config : undefined;
231
+ }
232
+ function resolvePostgresClient(client) {
233
+ if (isCallablePostgresClient(client)) {
234
+ return client;
235
+ }
236
+ const config = extractPostgresConfigFromSql(client);
237
+ if (config) {
238
+ return postgres(config);
239
+ }
240
+ return createProxyClientFromSql(client);
241
+ }
242
+ function resolvePostgresClientFromConnection(connection) {
243
+ if (!connection) {
244
+ return postgres();
245
+ }
246
+ if (typeof connection === 'string') {
247
+ return postgres(connection);
248
+ }
249
+ if (typeof connection === 'object' && connection.url !== undefined) {
250
+ const { url, ...config } = connection;
251
+ return postgres({ url, ...config });
252
+ }
253
+ return postgres(connection);
254
+ }
255
+ function createDrizzleDatabase(client, config) {
256
+ const resilientSQL = createResilientSQLProxy(client);
257
+ return upstreamDrizzle({
258
+ client: resilientSQL,
259
+ ...(config ?? {}),
260
+ });
261
+ }
262
+ function _drizzle(...params) {
263
+ if (typeof params[0] === 'string') {
264
+ const client = resolvePostgresClientFromConnection(params[0]);
265
+ return createDrizzleDatabase(client, params[1]);
266
+ }
267
+ if (isConfig(params[0])) {
268
+ const config = params[0];
269
+ const { connection, client, ...drizzleConfig } = config;
270
+ if (client) {
271
+ const resolvedClient = resolvePostgresClient(client);
272
+ return createDrizzleDatabase(resolvedClient, drizzleConfig);
273
+ }
274
+ const resolvedClient = resolvePostgresClientFromConnection(connection);
275
+ return createDrizzleDatabase(resolvedClient, drizzleConfig);
276
+ }
277
+ const client = resolvePostgresClient(params[0]);
278
+ return createDrizzleDatabase(client, params[1]);
279
+ }
280
+ _drizzle.mock = (config) => {
281
+ const db = upstreamDrizzle.mock(config);
282
+ db.$client =
283
+ '$client is not available on drizzle.mock()';
284
+ return db;
285
+ };
286
+ export const drizzle = _drizzle;
79
287
  /**
80
288
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
81
289
  *
@@ -138,7 +346,7 @@ export function createPostgresDrizzle(config) {
138
346
  const resilientSQL = createResilientSQLProxy(client);
139
347
  // Create Drizzle instance using the resilient proxy instead of a static
140
348
  // reference to client.raw, which would become stale after reconnection.
141
- const db = drizzle({
349
+ const db = upstreamDrizzle({
142
350
  client: resilientSQL,
143
351
  schema: config?.schema,
144
352
  logger: config?.logger,
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentuity/drizzle",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Agentuity employees and contributors",
6
6
  "type": "module",
@@ -41,12 +41,12 @@
41
41
  "prepublishOnly": "bun run clean && bun run build"
42
42
  },
43
43
  "dependencies": {
44
- "@agentuity/postgres": "1.0.6",
44
+ "@agentuity/postgres": "1.0.8",
45
45
  "drizzle-orm": "^0.45.0",
46
46
  "better-auth": "^1.4.9"
47
47
  },
48
48
  "devDependencies": {
49
- "@agentuity/test-utils": "1.0.6",
49
+ "@agentuity/test-utils": "1.0.8",
50
50
  "@types/bun": "latest",
51
51
  "bun-types": "latest",
52
52
  "typescript": "^5.9.0"
package/src/index.ts CHANGED
@@ -32,7 +32,7 @@
32
32
  */
33
33
 
34
34
  // Main factory function
35
- export { createPostgresDrizzle } from './postgres';
35
+ export { createPostgresDrizzle, drizzle } from './postgres';
36
36
 
37
37
  // Types
38
38
  export type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
@@ -74,6 +74,14 @@ export {
74
74
  relations,
75
75
  } from 'drizzle-orm';
76
76
 
77
+ // Re-export Bun SQL driver types for compatibility
78
+ export {
79
+ BunSQLDatabase,
80
+ BunSQLPreparedQuery,
81
+ BunSQLSession,
82
+ BunSQLTransaction,
83
+ } from 'drizzle-orm/bun-sql';
84
+
77
85
  // Re-export pg-core table and column definitions
78
86
  export {
79
87
  pgTable,
package/src/postgres.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { SQL as BunSQL } from 'bun';
2
- import { drizzle } from 'drizzle-orm/bun-sql';
1
+ import { SQL as BunSQL, type SQL as BunSQLClient, type SQLOptions } from 'bun';
2
+ import { drizzle as upstreamDrizzle, type BunSQLDatabase } from 'drizzle-orm/bun-sql';
3
+ import type { DrizzleConfig } from 'drizzle-orm';
4
+ import { isConfig } from 'drizzle-orm/utils';
3
5
  import { postgres, type CallablePostgresClient, type PostgresConfig } from '@agentuity/postgres';
4
6
  import type { PostgresDrizzleConfig, PostgresDrizzle } from './types';
5
7
 
@@ -40,6 +42,98 @@ export function resolvePostgresClientConfig<
40
42
  return clientConfig;
41
43
  }
42
44
 
45
+ /**
46
+ * Strips leading whitespace and SQL comments (block and line) from a query string.
47
+ * Returns the remaining query text starting at the first non-comment token.
48
+ */
49
+ const LEADING_COMMENTS_RE = /^(?:\s+|\/\*[\s\S]*?\*\/|--[^\n]*\n)*/;
50
+
51
+ /**
52
+ * Determines whether a SQL query is a non-retryable INSERT statement.
53
+ *
54
+ * Handles two patterns:
55
+ * 1. Direct INSERT: `INSERT INTO ...` (with optional leading comments/whitespace)
56
+ * 2. CTE INSERT: `WITH cte AS (...) INSERT INTO ...` — scans past the WITH clause
57
+ * by tracking parenthesis depth to skip CTE subexpressions, then checks
58
+ * if the first top-level DML keyword is INSERT.
59
+ *
60
+ * @see https://github.com/agentuity/sdk/issues/911
61
+ */
62
+ function isNonRetryableInsert(query: string): boolean {
63
+ // Strip leading whitespace and SQL comments
64
+ const stripped = query.replace(LEADING_COMMENTS_RE, '');
65
+
66
+ // Fast path: direct INSERT statement
67
+ if (/^INSERT\s/i.test(stripped)) {
68
+ return true;
69
+ }
70
+
71
+ // Check for WITH (CTE) prefix
72
+ if (!/^WITH\s/i.test(stripped)) {
73
+ return false;
74
+ }
75
+
76
+ // Scan past the CTE clause to find the first top-level DML keyword.
77
+ // We track parenthesis depth so we skip CTE subexpressions like
78
+ // "WITH cte AS (SELECT ... INSERT ...)" without false-matching the
79
+ // INSERT inside the parens.
80
+ let depth = 0;
81
+ let i = 4; // skip past "WITH"
82
+ const len = stripped.length;
83
+
84
+ while (i < len) {
85
+ const ch = stripped[i]!;
86
+
87
+ if (ch === '(') {
88
+ depth++;
89
+ i++;
90
+ continue;
91
+ }
92
+ if (ch === ')') {
93
+ depth--;
94
+ i++;
95
+ continue;
96
+ }
97
+
98
+ // Only inspect keywords at top level (depth === 0)
99
+ if (depth === 0) {
100
+ // Skip whitespace at top level
101
+ if (ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r') {
102
+ i++;
103
+ continue;
104
+ }
105
+
106
+ // Skip commas between CTEs: WITH a AS (...), b AS (...)
107
+ if (ch === ',') {
108
+ i++;
109
+ continue;
110
+ }
111
+
112
+ // Check for DML keywords at this position.
113
+ // We look for INSERT, UPDATE, DELETE, or SELECT — the first one
114
+ // we find at top level determines whether this is retryable.
115
+ const rest = stripped.substring(i);
116
+ const dmlMatch = /^(INSERT|UPDATE|DELETE|SELECT)\s/i.exec(rest);
117
+ if (dmlMatch) {
118
+ return dmlMatch[1]!.toUpperCase() === 'INSERT';
119
+ }
120
+
121
+ // Skip over any other word (e.g., CTE names, AS keyword, RECURSIVE)
122
+ // by advancing past alphanumeric/underscore characters
123
+ if (/\w/.test(ch)) {
124
+ while (i < len && /\w/.test(stripped[i]!)) {
125
+ i++;
126
+ }
127
+ continue;
128
+ }
129
+ }
130
+
131
+ i++;
132
+ }
133
+
134
+ return false;
135
+ }
136
+
43
137
  /**
44
138
  * Creates a dynamic SQL proxy that always delegates to the PostgresClient's
45
139
  * current raw connection. This ensures that after automatic reconnection,
@@ -58,6 +152,10 @@ export function createResilientSQLProxy(
58
152
  // Always resolve from the CURRENT raw connection (changes after reconnect)
59
153
  const raw = client.raw;
60
154
 
155
+ if (prop === 'close') {
156
+ return () => client.close();
157
+ }
158
+
61
159
  if (prop === 'unsafe') {
62
160
  // Wrap unsafe() with retry logic for resilient queries.
63
161
  // Returns a thenable that also supports .values() chaining,
@@ -65,6 +163,25 @@ export function createResilientSQLProxy(
65
163
  // client.unsafe(query, params) → Promise<rows>
66
164
  // client.unsafe(query, params).values() → Promise<rows>
67
165
  return (query: string, params?: unknown[]) => {
166
+ // INSERT statements (including CTE-based) are NOT retried to prevent
167
+ // duplicate rows. If an INSERT succeeds on the server but the connection
168
+ // drops before the response, retrying would re-execute it — creating a
169
+ // duplicate row when the primary key is server-generated.
170
+ // See: https://github.com/agentuity/sdk/issues/911
171
+ const isInsert = isNonRetryableInsert(query);
172
+
173
+ if (isInsert) {
174
+ const makeDirectExecutor = (useValues: boolean) => {
175
+ const currentRaw = client.raw;
176
+ const q = currentRaw.unsafe(query, params);
177
+ return useValues ? q.values() : q;
178
+ };
179
+ const result = makeDirectExecutor(false);
180
+ return Object.assign(result, {
181
+ values: () => makeDirectExecutor(true),
182
+ });
183
+ }
184
+
68
185
  const makeExecutor = (useValues: boolean) =>
69
186
  client.executeWithRetry(async () => {
70
187
  // Re-resolve raw inside retry to get post-reconnect instance
@@ -91,6 +208,171 @@ export function createResilientSQLProxy(
91
208
  });
92
209
  }
93
210
 
211
+ type DrizzleConnectionConfig = string | ({ url?: string } & SQLOptions);
212
+
213
+ function isCallablePostgresClient(value: unknown): value is CallablePostgresClient {
214
+ return (
215
+ typeof value === 'function' &&
216
+ value !== null &&
217
+ 'raw' in (value as CallablePostgresClient) &&
218
+ typeof (value as CallablePostgresClient).executeWithRetry === 'function'
219
+ );
220
+ }
221
+
222
+ function createProxyClientFromSql(client: BunSQLClient): CallablePostgresClient {
223
+ // Bun SQL instances are callable as tagged template literals.
224
+ // Create a function that forwards calls to the client.
225
+ const proxy = (async (strings: TemplateStringsArray, ...values: unknown[]) => {
226
+ // Forward tagged template to the Bun SQL client directly
227
+ return (client as unknown as CallablePostgresClient)(strings, ...values);
228
+ }) as unknown as CallablePostgresClient;
229
+
230
+ Object.defineProperties(proxy, {
231
+ raw: {
232
+ get: () => client as InstanceType<typeof BunSQL>,
233
+ enumerable: true,
234
+ },
235
+ });
236
+
237
+ proxy.executeWithRetry = async <T>(operation: () => T | Promise<T>) => operation();
238
+ proxy.close = async () => {
239
+ const close = (client as { close?: () => Promise<void> | void }).close;
240
+ if (typeof close === 'function') {
241
+ await close.call(client);
242
+ }
243
+ };
244
+
245
+ return proxy;
246
+ }
247
+
248
+ function extractPostgresConfigFromSql(client: BunSQLClient): PostgresConfig | undefined {
249
+ const options = (client as { options?: Record<string, unknown> }).options;
250
+ if (!options || typeof options !== 'object') {
251
+ return undefined;
252
+ }
253
+
254
+ const config: PostgresConfig = {};
255
+ const keys = [
256
+ 'url',
257
+ 'hostname',
258
+ 'port',
259
+ 'username',
260
+ 'password',
261
+ 'database',
262
+ 'tls',
263
+ 'max',
264
+ 'idleTimeout',
265
+ 'connectionTimeout',
266
+ ] as const;
267
+
268
+ for (const key of keys) {
269
+ if (key in options) {
270
+ (config as Record<string, unknown>)[key] = options[key];
271
+ }
272
+ }
273
+
274
+ return Object.keys(config).length > 0 ? config : undefined;
275
+ }
276
+
277
+ function resolvePostgresClient<TClient extends BunSQLClient>(
278
+ client: TClient
279
+ ): CallablePostgresClient {
280
+ if (isCallablePostgresClient(client)) {
281
+ return client;
282
+ }
283
+
284
+ const config = extractPostgresConfigFromSql(client);
285
+ if (config) {
286
+ return postgres(config);
287
+ }
288
+
289
+ return createProxyClientFromSql(client);
290
+ }
291
+
292
+ function resolvePostgresClientFromConnection(
293
+ connection?: DrizzleConnectionConfig
294
+ ): CallablePostgresClient {
295
+ if (!connection) {
296
+ return postgres();
297
+ }
298
+
299
+ if (typeof connection === 'string') {
300
+ return postgres(connection);
301
+ }
302
+
303
+ if (typeof connection === 'object' && connection.url !== undefined) {
304
+ const { url, ...config } = connection;
305
+ return postgres({ url, ...(config as PostgresConfig) });
306
+ }
307
+
308
+ return postgres(connection as PostgresConfig);
309
+ }
310
+
311
+ function createDrizzleDatabase<
312
+ TSchema extends Record<string, unknown> = Record<string, never>,
313
+ TClient extends BunSQLClient = BunSQLClient,
314
+ >(
315
+ client: CallablePostgresClient,
316
+ config?: DrizzleConfig<TSchema>
317
+ ): BunSQLDatabase<TSchema> & {
318
+ $client: TClient;
319
+ } {
320
+ const resilientSQL = createResilientSQLProxy(client);
321
+ return upstreamDrizzle({
322
+ client: resilientSQL,
323
+ ...(config ?? {}),
324
+ }) as BunSQLDatabase<TSchema> & { $client: TClient };
325
+ }
326
+
327
+ function _drizzle<
328
+ TSchema extends Record<string, unknown> = Record<string, never>,
329
+ TClient extends BunSQLClient = BunSQLClient,
330
+ >(
331
+ ...params:
332
+ | [TClient | string]
333
+ | [TClient | string, DrizzleConfig<TSchema>]
334
+ | [DrizzleConfig<TSchema> & ({ connection: DrizzleConnectionConfig } | { client: TClient })]
335
+ ): BunSQLDatabase<TSchema> & { $client: TClient } {
336
+ if (typeof params[0] === 'string') {
337
+ const client = resolvePostgresClientFromConnection(params[0]);
338
+ return createDrizzleDatabase(client, params[1]);
339
+ }
340
+
341
+ if (isConfig(params[0])) {
342
+ const config = params[0] as DrizzleConfig<TSchema> & {
343
+ connection?: DrizzleConnectionConfig;
344
+ client?: TClient;
345
+ };
346
+ const { connection, client, ...drizzleConfig } = config;
347
+
348
+ if (client) {
349
+ const resolvedClient = resolvePostgresClient(client);
350
+ return createDrizzleDatabase(resolvedClient, drizzleConfig);
351
+ }
352
+
353
+ const resolvedClient = resolvePostgresClientFromConnection(connection);
354
+ return createDrizzleDatabase(resolvedClient, drizzleConfig);
355
+ }
356
+
357
+ const client = resolvePostgresClient(params[0] as TClient);
358
+ return createDrizzleDatabase(client, params[1]);
359
+ }
360
+
361
+ _drizzle.mock = <TSchema extends Record<string, unknown> = Record<string, never>>(
362
+ config?: DrizzleConfig<TSchema>
363
+ ): BunSQLDatabase<TSchema> & { $client: '$client is not available on drizzle.mock()' } => {
364
+ const db = upstreamDrizzle.mock(config);
365
+ (db as unknown as Record<string, unknown>).$client =
366
+ '$client is not available on drizzle.mock()';
367
+ return db as BunSQLDatabase<TSchema> & {
368
+ $client: '$client is not available on drizzle.mock()';
369
+ };
370
+ };
371
+
372
+ export const drizzle = _drizzle as typeof _drizzle & {
373
+ mock: typeof _drizzle.mock;
374
+ };
375
+
94
376
  /**
95
377
  * Creates a Drizzle ORM instance with a resilient PostgreSQL connection.
96
378
  *
@@ -159,7 +441,7 @@ export function createPostgresDrizzle<
159
441
 
160
442
  // Create Drizzle instance using the resilient proxy instead of a static
161
443
  // reference to client.raw, which would become stale after reconnection.
162
- const db = drizzle({
444
+ const db = upstreamDrizzle({
163
445
  client: resilientSQL,
164
446
  schema: config?.schema,
165
447
  logger: config?.logger,