@pineliner/odb-client 1.1.3 → 1.1.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,5 +1,5 @@
1
1
  import type { ODBLiteConfig, ODBLiteConnection, QueryResult, Transaction, TransactionCallback, TransactionMode, SQLFragment } from '../types.ts';
2
- import { HTTPClient } from './http-client.ts';
2
+ import { HTTPClient, type QueryRequestOptions } from './http-client.ts';
3
3
  interface BatchResult<T = any> {
4
4
  results: QueryResult<T>[];
5
5
  executionTime: number;
@@ -10,11 +10,11 @@ interface SQLFunction {
10
10
  <T = any>(input: any): SQLFragment;
11
11
  raw(text: string): string;
12
12
  identifier(name: string): string;
13
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
13
+ query<T = any>(sql: string, params?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>>;
14
14
  execute<T = any>(sql: string | {
15
15
  sql: string;
16
16
  args?: any[];
17
- }, args?: any[]): Promise<QueryResult<T>>;
17
+ }, args?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>>;
18
18
  batch<T = any>(statements: Array<{
19
19
  sql: string;
20
20
  params?: any[];
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,11 @@
1
1
  import type { ODBLiteConfig, QueryResult } from '../types.ts';
2
+ /** Per-request options for a single query. */
3
+ export interface QueryRequestOptions {
4
+ /** Transaction/session token tying this statement to a server-pinned connection. */
5
+ txId?: string;
6
+ /** Override the client's retry count (transaction statements pass 1 = no retry). */
7
+ maxRetries?: number;
8
+ }
2
9
  /**
3
10
  * HTTP client for communicating with ODBLite service
4
11
  */
@@ -8,7 +15,7 @@ export declare class HTTPClient {
8
15
  /**
9
16
  * Execute a query against the ODBLite service
10
17
  */
11
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
18
+ query<T = any>(sql: string, params?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>>;
12
19
  /**
13
20
  * Execute multiple statements on the same connection (for transactions)
14
21
  * All statements are sent in a single HTTP request and executed atomically on the server
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/core/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9E;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,aAAa;IAajC;;OAEG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAqD9E;;;OAGG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC;QAChF,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IA4CF;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAqB9B;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC;IAqBrC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;YACW,WAAW;IAgDzB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,UAAU;IAOtD;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC;IAIpC;;;OAGG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACpD,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IAqCF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,oBAAoB,EAAE,MAAM,EAAE,CAAC;KAChC,CAAC;CAiCH"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../../src/core/http-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAA;AAG9E,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,aAAa;IAajC;;OAEG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAwD1G;;;OAGG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC;QAChF,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IA4CF;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAqB9B;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC;IAqBrC;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;YACW,WAAW;IAoDzB;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,UAAU;IAOtD;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC;IAIpC;;;OAGG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACpD,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IAqCF;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,OAAO,CAAC;QACpB,oBAAoB,EAAE,MAAM,EAAE,CAAC;KAChC,CAAC;CAiCH"}
@@ -1 +1 @@
1
- {"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EAEd,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAS;IAClC,OAAO,CAAC,MAAM,CAAe;IACtB,aAAa,EAAE,aAAa,CAAA;gBAEvB,MAAM,EAAE,aAAa;IAQ3B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
1
+ {"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EAEd,MAAM,UAAU,CAAA;AA6FjB;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAS;IAClC,OAAO,CAAC,MAAM,CAAe;IACtB,aAAa,EAAE,aAAa,CAAA;gBAEvB,MAAM,EAAE,aAAa;IAQ3B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sql-parser.d.ts","sourceRoot":"","sources":["../../src/database/sql-parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CA0B7F;AAoFD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/D"}
1
+ {"version":3,"file":"sql-parser.d.ts","sourceRoot":"","sources":["../../src/database/sql-parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,gBAAgB,CA0B7F;AAwGD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/D"}
@@ -58,6 +58,7 @@ export interface Connection {
58
58
  prepare(sql: string): PreparedStatement;
59
59
  transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
60
60
  begin<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
61
+ savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
61
62
  close(): Promise<void>;
62
63
  createORM?(): any;
63
64
  databaseHash?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -412,6 +412,12 @@ var __webpack_modules__ = {
412
412
  function createORM(db) {
413
413
  return new ORM(db);
414
414
  }
415
+ },
416
+ async_hooks (module) {
417
+ module.exports = require("async_hooks");
418
+ },
419
+ "node:async_hooks" (module) {
420
+ module.exports = require("node:async_hooks");
415
421
  }
416
422
  };
417
423
  var __webpack_module_cache__ = {};
@@ -540,12 +546,15 @@ var __webpack_exports__ = {};
540
546
  };
541
547
  if ('string' == typeof this.config.baseUrl) this.config.baseUrl = this.config.baseUrl.replace(/\/$/, '');
542
548
  }
543
- async query(sql, params = []) {
549
+ async query(sql, params = [], opts) {
544
550
  if (!this.config.databaseId) throw new ConnectionError('No database ID configured. Use setDatabase() first.');
545
551
  const url = `${this.config.baseUrl}/query/${this.config.databaseId}`;
546
552
  const body = {
547
553
  sql,
548
- params
554
+ params,
555
+ ...opts?.txId ? {
556
+ txId: opts.txId
557
+ } : {}
549
558
  };
550
559
  try {
551
560
  const response = await this.makeRequest(url, {
@@ -555,7 +564,7 @@ var __webpack_exports__ = {};
555
564
  Authorization: `Bearer ${this.config.apiKey}`
556
565
  },
557
566
  body: JSON.stringify(body)
558
- });
567
+ }, opts?.maxRetries);
559
568
  const data = await response.json();
560
569
  if (!data.success) throw new types_QueryError(data.error || 'Query failed', sql, params);
561
570
  let rows = [];
@@ -634,9 +643,10 @@ var __webpack_exports__ = {};
634
643
  setDatabase(databaseId) {
635
644
  this.config.databaseId = databaseId;
636
645
  }
637
- async makeRequest(url, options) {
646
+ async makeRequest(url, options, maxRetries) {
638
647
  let lastError;
639
- for(let attempt = 1; attempt <= (this.config.retries || 3); attempt++)try {
648
+ const retries = maxRetries ?? this.config.retries ?? 3;
649
+ for(let attempt = 1; attempt <= retries; attempt++)try {
640
650
  const controller = new AbortController();
641
651
  const timeoutId = setTimeout(()=>controller.abort(), this.config.timeout);
642
652
  const response = await fetch(url, {
@@ -660,12 +670,12 @@ var __webpack_exports__ = {};
660
670
  } catch (error) {
661
671
  lastError = error instanceof Error ? error : new Error('Unknown error');
662
672
  if (error instanceof ConnectionError && error.message.includes('HTTP 4')) throw error;
663
- if (attempt < (this.config.retries || 3)) {
673
+ if (attempt < retries) {
664
674
  const delay = Math.min(1000 * 2 ** (attempt - 1), 10000);
665
675
  await new Promise((resolve)=>setTimeout(resolve, delay));
666
676
  }
667
677
  }
668
- throw new ConnectionError(`Failed after ${this.config.retries} attempts: ${lastError?.message}`, lastError);
678
+ throw new ConnectionError(`Failed after ${retries} attempts: ${lastError?.message}`, lastError);
669
679
  }
670
680
  configure(updates) {
671
681
  return new HTTPClient({
@@ -1138,10 +1148,10 @@ var __webpack_exports__ = {};
1138
1148
  };
1139
1149
  sqlFunction.raw = (text)=>sql_parser_SQLParser.raw(text);
1140
1150
  sqlFunction.identifier = (name)=>sql_parser_SQLParser.identifier(name);
1141
- sqlFunction.query = async (sql, params = [])=>await this.httpClient.query(sql, params);
1142
- sqlFunction.execute = async (sql, args)=>{
1143
- if ('string' == typeof sql) return await this.httpClient.query(sql, args || []);
1144
- return await this.httpClient.query(sql.sql, sql.args || []);
1151
+ sqlFunction.query = async (sql, params = [], opts)=>await this.httpClient.query(sql, params, opts);
1152
+ sqlFunction.execute = async (sql, args, opts)=>{
1153
+ if ('string' == typeof sql) return await this.httpClient.query(sql, args || [], opts);
1154
+ return await this.httpClient.query(sql.sql, sql.args || [], opts);
1145
1155
  };
1146
1156
  sqlFunction.batch = async (statements)=>await this.httpClient.batch(statements);
1147
1157
  sqlFunction.begin = async (modeOrCallback, callback)=>{
@@ -1697,6 +1707,19 @@ var __webpack_exports__ = {};
1697
1707
  async begin(fn) {
1698
1708
  return this.transaction(fn);
1699
1709
  }
1710
+ async savepoint(fn) {
1711
+ if (!this.inTransaction) return this.transaction(fn);
1712
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1713
+ try {
1714
+ await this.execute(`SAVEPOINT ${name}`);
1715
+ const result = await fn(this);
1716
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1717
+ return result;
1718
+ } catch (error) {
1719
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1720
+ throw error;
1721
+ }
1722
+ }
1700
1723
  async close() {
1701
1724
  this.db.close();
1702
1725
  }
@@ -1853,12 +1876,44 @@ var __webpack_exports__ = {};
1853
1876
  async begin(fn) {
1854
1877
  return this.transaction(fn);
1855
1878
  }
1879
+ async savepoint(fn) {
1880
+ if (!this.txClient) return this.transaction(fn);
1881
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1882
+ try {
1883
+ await this.execute(`SAVEPOINT ${name}`);
1884
+ const result = await fn(this);
1885
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1886
+ return result;
1887
+ } catch (error) {
1888
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1889
+ throw error;
1890
+ }
1891
+ }
1856
1892
  async close() {}
1857
1893
  createORM() {
1858
1894
  const { createORM } = __webpack_require__("./src/orm/index.ts");
1859
1895
  return createORM(this);
1860
1896
  }
1861
1897
  }
1898
+ let AsyncLocalStorageCtor = null;
1899
+ try {
1900
+ AsyncLocalStorageCtor = __webpack_require__("node:async_hooks").AsyncLocalStorage;
1901
+ } catch {
1902
+ try {
1903
+ AsyncLocalStorageCtor = __webpack_require__("async_hooks").AsyncLocalStorage;
1904
+ } catch {
1905
+ AsyncLocalStorageCtor = null;
1906
+ }
1907
+ }
1908
+ let txCounter = 0;
1909
+ function newTxId() {
1910
+ try {
1911
+ const c = globalThis.crypto;
1912
+ if (c?.randomUUID) return `tx_${c.randomUUID()}`;
1913
+ } catch {}
1914
+ txCounter = (txCounter + 1) % Number.MAX_SAFE_INTEGER;
1915
+ return `tx_${Date.now().toString(36)}_${txCounter.toString(36)}`;
1916
+ }
1862
1917
  function odblite_parseJsonColumns(rows, jsonColumns) {
1863
1918
  if (!jsonColumns || 0 === jsonColumns.length) return rows;
1864
1919
  return rows.map((row)=>{
@@ -1933,7 +1988,8 @@ var __webpack_exports__ = {};
1933
1988
  class ODBLiteConnection {
1934
1989
  client;
1935
1990
  serviceClient;
1936
- inTransaction = false;
1991
+ txStorage = AsyncLocalStorageCtor ? new AsyncLocalStorageCtor() : null;
1992
+ fallbackTxId;
1937
1993
  databaseName;
1938
1994
  databaseHash;
1939
1995
  sql;
@@ -1968,11 +2024,22 @@ var __webpack_exports__ = {};
1968
2024
  async query(sql, params = [], options) {
1969
2025
  return this.execute(sql, params, options);
1970
2026
  }
2027
+ activeTxId() {
2028
+ return this.txStorage ? this.txStorage.getStore()?.txId : this.fallbackTxId;
2029
+ }
2030
+ requestOpts() {
2031
+ const txId = this.activeTxId();
2032
+ return txId ? {
2033
+ txId,
2034
+ maxRetries: 1
2035
+ } : void 0;
2036
+ }
1971
2037
  async execute(sql, params = [], options) {
1972
2038
  try {
1973
2039
  let rows;
1974
2040
  let rowsAffected;
1975
2041
  let lastInsertRowid;
2042
+ const reqOpts = this.requestOpts();
1976
2043
  if ('object' == typeof sql) {
1977
2044
  if (process.env.DEBUG_SQL) {
1978
2045
  console.log('[ODBLite] Executing SQL:', sql.sql);
@@ -1980,7 +2047,7 @@ var __webpack_exports__ = {};
1980
2047
  }
1981
2048
  let args = sql.args || [];
1982
2049
  if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1983
- const result = await this.client.sql.execute(sql.sql, args);
2050
+ const result = await this.client.sql.execute(sql.sql, args, reqOpts);
1984
2051
  rows = result.rows;
1985
2052
  rowsAffected = result.rowsAffected || 0;
1986
2053
  lastInsertRowid = result.lastInsertRowid;
@@ -1991,7 +2058,7 @@ var __webpack_exports__ = {};
1991
2058
  }
1992
2059
  let args = params;
1993
2060
  if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1994
- const result = await this.client.sql.execute(sql, args);
2061
+ const result = await this.client.sql.execute(sql, args, reqOpts);
1995
2062
  rows = result.rows;
1996
2063
  rowsAffected = result.rowsAffected || 0;
1997
2064
  lastInsertRowid = result.lastInsertRowid;
@@ -2020,36 +2087,45 @@ var __webpack_exports__ = {};
2020
2087
  };
2021
2088
  }
2022
2089
  async transaction(fn) {
2023
- if (this.inTransaction) {
2024
- const savepointName = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2025
- try {
2026
- await this.execute(`SAVEPOINT ${savepointName}`);
2027
- const result = await fn(this);
2028
- await this.execute(`RELEASE SAVEPOINT ${savepointName}`);
2029
- return result;
2030
- } catch (error) {
2031
- await this.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`).catch(()=>{});
2032
- throw error;
2033
- }
2034
- }
2035
- this.inTransaction = true;
2036
- try {
2037
- await this.execute('BEGIN');
2090
+ if (this.activeTxId()) return fn(this);
2091
+ const txId = newTxId();
2092
+ const run = async ()=>{
2093
+ const prevFallback = this.fallbackTxId;
2094
+ if (!this.txStorage) this.fallbackTxId = txId;
2038
2095
  try {
2039
- const result = await fn(this);
2040
- await this.execute('COMMIT');
2041
- return result;
2042
- } catch (error) {
2043
- await this.execute('ROLLBACK').catch(()=>{});
2044
- throw error;
2096
+ await this.execute('BEGIN');
2097
+ try {
2098
+ const result = await fn(this);
2099
+ await this.execute('COMMIT');
2100
+ return result;
2101
+ } catch (error) {
2102
+ await this.execute('ROLLBACK').catch(()=>{});
2103
+ throw error;
2104
+ }
2105
+ } finally{
2106
+ if (!this.txStorage) this.fallbackTxId = prevFallback;
2045
2107
  }
2046
- } finally{
2047
- this.inTransaction = false;
2048
- }
2108
+ };
2109
+ return this.txStorage ? this.txStorage.run({
2110
+ txId
2111
+ }, run) : run();
2049
2112
  }
2050
2113
  async begin(fn) {
2051
2114
  return this.transaction(fn);
2052
2115
  }
2116
+ async savepoint(fn) {
2117
+ if (!this.activeTxId()) return this.transaction(fn);
2118
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2119
+ try {
2120
+ await this.execute(`SAVEPOINT ${name}`);
2121
+ const result = await fn(this);
2122
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
2123
+ return result;
2124
+ } catch (error) {
2125
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
2126
+ throw error;
2127
+ }
2128
+ }
2053
2129
  async close() {}
2054
2130
  createORM() {
2055
2131
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -2087,6 +2163,8 @@ var __webpack_exports__ = {};
2087
2163
  let inQuote = false;
2088
2164
  let quoteChar = '';
2089
2165
  let beginEndDepth = 0;
2166
+ let inCte = false;
2167
+ let cteParenDepth = 0;
2090
2168
  const lines = sqlContent.split('\n');
2091
2169
  for (const line of lines){
2092
2170
  let processedLine = '';
@@ -2100,6 +2178,15 @@ var __webpack_exports__ = {};
2100
2178
  if (beginEndDepth < 0) beginEndDepth = 0;
2101
2179
  }
2102
2180
  }
2181
+ if (!inQuote && lineWithoutComments.trim()) {
2182
+ const withMatch = lineWithoutComments.match(/\bWITH\b.*\bAS\b/i);
2183
+ if (withMatch) inCte = true;
2184
+ if (inCte) for (const char of lineWithoutComments){
2185
+ if ('(' === char) cteParenDepth++;
2186
+ if (')' === char) cteParenDepth--;
2187
+ if (0 === cteParenDepth && ')' === char) inCte = false;
2188
+ }
2189
+ }
2103
2190
  for(let i = 0; i < line.length; i++){
2104
2191
  const char = line[i];
2105
2192
  const prevChar = i > 0 ? line[i - 1] : '';
@@ -2115,7 +2202,7 @@ var __webpack_exports__ = {};
2115
2202
  quoteChar = char;
2116
2203
  }
2117
2204
  processedLine += char;
2118
- if (';' === char && !inQuote && 0 === beginEndDepth) {
2205
+ if (';' === char && !inQuote && 0 === beginEndDepth && !inCte) {
2119
2206
  currentStatement += processedLine;
2120
2207
  const stmt = currentStatement.trim();
2121
2208
  if (stmt && !stmt.startsWith('--')) statements.push(stmt);
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ import * as __rspack_external_async_hooks from "async_hooks";
2
+ import * as __rspack_external_node_async_hooks_e65a2d6c from "node:async_hooks";
1
3
  import { __webpack_require__ } from "./rslib-runtime.js";
2
4
  import { Database } from "bun:sqlite";
3
5
  import { createClient } from "@libsql/client";
@@ -415,6 +417,12 @@ __webpack_require__.add({
415
417
  function createORM(db) {
416
418
  return new ORM(db);
417
419
  }
420
+ },
421
+ async_hooks (module) {
422
+ module.exports = __rspack_external_async_hooks;
423
+ },
424
+ "node:async_hooks" (module) {
425
+ module.exports = __rspack_external_node_async_hooks_e65a2d6c;
418
426
  }
419
427
  });
420
428
  class ODBLiteError extends Error {
@@ -450,12 +458,15 @@ class HTTPClient {
450
458
  };
451
459
  if ('string' == typeof this.config.baseUrl) this.config.baseUrl = this.config.baseUrl.replace(/\/$/, '');
452
460
  }
453
- async query(sql, params = []) {
461
+ async query(sql, params = [], opts) {
454
462
  if (!this.config.databaseId) throw new ConnectionError('No database ID configured. Use setDatabase() first.');
455
463
  const url = `${this.config.baseUrl}/query/${this.config.databaseId}`;
456
464
  const body = {
457
465
  sql,
458
- params
466
+ params,
467
+ ...opts?.txId ? {
468
+ txId: opts.txId
469
+ } : {}
459
470
  };
460
471
  try {
461
472
  const response = await this.makeRequest(url, {
@@ -465,7 +476,7 @@ class HTTPClient {
465
476
  Authorization: `Bearer ${this.config.apiKey}`
466
477
  },
467
478
  body: JSON.stringify(body)
468
- });
479
+ }, opts?.maxRetries);
469
480
  const data = await response.json();
470
481
  if (!data.success) throw new types_QueryError(data.error || 'Query failed', sql, params);
471
482
  let rows = [];
@@ -544,9 +555,10 @@ class HTTPClient {
544
555
  setDatabase(databaseId) {
545
556
  this.config.databaseId = databaseId;
546
557
  }
547
- async makeRequest(url, options) {
558
+ async makeRequest(url, options, maxRetries) {
548
559
  let lastError;
549
- for(let attempt = 1; attempt <= (this.config.retries || 3); attempt++)try {
560
+ const retries = maxRetries ?? this.config.retries ?? 3;
561
+ for(let attempt = 1; attempt <= retries; attempt++)try {
550
562
  const controller = new AbortController();
551
563
  const timeoutId = setTimeout(()=>controller.abort(), this.config.timeout);
552
564
  const response = await fetch(url, {
@@ -570,12 +582,12 @@ class HTTPClient {
570
582
  } catch (error) {
571
583
  lastError = error instanceof Error ? error : new Error('Unknown error');
572
584
  if (error instanceof ConnectionError && error.message.includes('HTTP 4')) throw error;
573
- if (attempt < (this.config.retries || 3)) {
585
+ if (attempt < retries) {
574
586
  const delay = Math.min(1000 * 2 ** (attempt - 1), 10000);
575
587
  await new Promise((resolve)=>setTimeout(resolve, delay));
576
588
  }
577
589
  }
578
- throw new ConnectionError(`Failed after ${this.config.retries} attempts: ${lastError?.message}`, lastError);
590
+ throw new ConnectionError(`Failed after ${retries} attempts: ${lastError?.message}`, lastError);
579
591
  }
580
592
  configure(updates) {
581
593
  return new HTTPClient({
@@ -1048,10 +1060,10 @@ class ODBLiteClient {
1048
1060
  };
1049
1061
  sqlFunction.raw = (text)=>sql_parser_SQLParser.raw(text);
1050
1062
  sqlFunction.identifier = (name)=>sql_parser_SQLParser.identifier(name);
1051
- sqlFunction.query = async (sql, params = [])=>await this.httpClient.query(sql, params);
1052
- sqlFunction.execute = async (sql, args)=>{
1053
- if ('string' == typeof sql) return await this.httpClient.query(sql, args || []);
1054
- return await this.httpClient.query(sql.sql, sql.args || []);
1063
+ sqlFunction.query = async (sql, params = [], opts)=>await this.httpClient.query(sql, params, opts);
1064
+ sqlFunction.execute = async (sql, args, opts)=>{
1065
+ if ('string' == typeof sql) return await this.httpClient.query(sql, args || [], opts);
1066
+ return await this.httpClient.query(sql.sql, sql.args || [], opts);
1055
1067
  };
1056
1068
  sqlFunction.batch = async (statements)=>await this.httpClient.batch(statements);
1057
1069
  sqlFunction.begin = async (modeOrCallback, callback)=>{
@@ -1606,6 +1618,19 @@ class BunSQLiteConnection {
1606
1618
  async begin(fn) {
1607
1619
  return this.transaction(fn);
1608
1620
  }
1621
+ async savepoint(fn) {
1622
+ if (!this.inTransaction) return this.transaction(fn);
1623
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1624
+ try {
1625
+ await this.execute(`SAVEPOINT ${name}`);
1626
+ const result = await fn(this);
1627
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1628
+ return result;
1629
+ } catch (error) {
1630
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1631
+ throw error;
1632
+ }
1633
+ }
1609
1634
  async close() {
1610
1635
  this.db.close();
1611
1636
  }
@@ -1761,12 +1786,44 @@ class LibSQLConnection {
1761
1786
  async begin(fn) {
1762
1787
  return this.transaction(fn);
1763
1788
  }
1789
+ async savepoint(fn) {
1790
+ if (!this.txClient) return this.transaction(fn);
1791
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1792
+ try {
1793
+ await this.execute(`SAVEPOINT ${name}`);
1794
+ const result = await fn(this);
1795
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1796
+ return result;
1797
+ } catch (error) {
1798
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1799
+ throw error;
1800
+ }
1801
+ }
1764
1802
  async close() {}
1765
1803
  createORM() {
1766
1804
  const { createORM } = __webpack_require__("./src/orm/index.ts");
1767
1805
  return createORM(this);
1768
1806
  }
1769
1807
  }
1808
+ let AsyncLocalStorageCtor = null;
1809
+ try {
1810
+ AsyncLocalStorageCtor = __webpack_require__("node:async_hooks").AsyncLocalStorage;
1811
+ } catch {
1812
+ try {
1813
+ AsyncLocalStorageCtor = __webpack_require__("async_hooks").AsyncLocalStorage;
1814
+ } catch {
1815
+ AsyncLocalStorageCtor = null;
1816
+ }
1817
+ }
1818
+ let txCounter = 0;
1819
+ function newTxId() {
1820
+ try {
1821
+ const c = globalThis.crypto;
1822
+ if (c?.randomUUID) return `tx_${c.randomUUID()}`;
1823
+ } catch {}
1824
+ txCounter = (txCounter + 1) % Number.MAX_SAFE_INTEGER;
1825
+ return `tx_${Date.now().toString(36)}_${txCounter.toString(36)}`;
1826
+ }
1770
1827
  function odblite_parseJsonColumns(rows, jsonColumns) {
1771
1828
  if (!jsonColumns || 0 === jsonColumns.length) return rows;
1772
1829
  return rows.map((row)=>{
@@ -1841,7 +1898,8 @@ class ODBLiteAdapter {
1841
1898
  class ODBLiteConnection {
1842
1899
  client;
1843
1900
  serviceClient;
1844
- inTransaction = false;
1901
+ txStorage = AsyncLocalStorageCtor ? new AsyncLocalStorageCtor() : null;
1902
+ fallbackTxId;
1845
1903
  databaseName;
1846
1904
  databaseHash;
1847
1905
  sql;
@@ -1876,11 +1934,22 @@ class ODBLiteConnection {
1876
1934
  async query(sql, params = [], options) {
1877
1935
  return this.execute(sql, params, options);
1878
1936
  }
1937
+ activeTxId() {
1938
+ return this.txStorage ? this.txStorage.getStore()?.txId : this.fallbackTxId;
1939
+ }
1940
+ requestOpts() {
1941
+ const txId = this.activeTxId();
1942
+ return txId ? {
1943
+ txId,
1944
+ maxRetries: 1
1945
+ } : void 0;
1946
+ }
1879
1947
  async execute(sql, params = [], options) {
1880
1948
  try {
1881
1949
  let rows;
1882
1950
  let rowsAffected;
1883
1951
  let lastInsertRowid;
1952
+ const reqOpts = this.requestOpts();
1884
1953
  if ('object' == typeof sql) {
1885
1954
  if (process.env.DEBUG_SQL) {
1886
1955
  console.log('[ODBLite] Executing SQL:', sql.sql);
@@ -1888,7 +1957,7 @@ class ODBLiteConnection {
1888
1957
  }
1889
1958
  let args = sql.args || [];
1890
1959
  if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1891
- const result = await this.client.sql.execute(sql.sql, args);
1960
+ const result = await this.client.sql.execute(sql.sql, args, reqOpts);
1892
1961
  rows = result.rows;
1893
1962
  rowsAffected = result.rowsAffected || 0;
1894
1963
  lastInsertRowid = result.lastInsertRowid;
@@ -1899,7 +1968,7 @@ class ODBLiteConnection {
1899
1968
  }
1900
1969
  let args = params;
1901
1970
  if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1902
- const result = await this.client.sql.execute(sql, args);
1971
+ const result = await this.client.sql.execute(sql, args, reqOpts);
1903
1972
  rows = result.rows;
1904
1973
  rowsAffected = result.rowsAffected || 0;
1905
1974
  lastInsertRowid = result.lastInsertRowid;
@@ -1928,36 +1997,45 @@ class ODBLiteConnection {
1928
1997
  };
1929
1998
  }
1930
1999
  async transaction(fn) {
1931
- if (this.inTransaction) {
1932
- const savepointName = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2000
+ if (this.activeTxId()) return fn(this);
2001
+ const txId = newTxId();
2002
+ const run = async ()=>{
2003
+ const prevFallback = this.fallbackTxId;
2004
+ if (!this.txStorage) this.fallbackTxId = txId;
1933
2005
  try {
1934
- await this.execute(`SAVEPOINT ${savepointName}`);
1935
- const result = await fn(this);
1936
- await this.execute(`RELEASE SAVEPOINT ${savepointName}`);
1937
- return result;
1938
- } catch (error) {
1939
- await this.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`).catch(()=>{});
1940
- throw error;
1941
- }
1942
- }
1943
- this.inTransaction = true;
1944
- try {
1945
- await this.execute('BEGIN');
1946
- try {
1947
- const result = await fn(this);
1948
- await this.execute('COMMIT');
1949
- return result;
1950
- } catch (error) {
1951
- await this.execute('ROLLBACK').catch(()=>{});
1952
- throw error;
2006
+ await this.execute('BEGIN');
2007
+ try {
2008
+ const result = await fn(this);
2009
+ await this.execute('COMMIT');
2010
+ return result;
2011
+ } catch (error) {
2012
+ await this.execute('ROLLBACK').catch(()=>{});
2013
+ throw error;
2014
+ }
2015
+ } finally{
2016
+ if (!this.txStorage) this.fallbackTxId = prevFallback;
1953
2017
  }
1954
- } finally{
1955
- this.inTransaction = false;
1956
- }
2018
+ };
2019
+ return this.txStorage ? this.txStorage.run({
2020
+ txId
2021
+ }, run) : run();
1957
2022
  }
1958
2023
  async begin(fn) {
1959
2024
  return this.transaction(fn);
1960
2025
  }
2026
+ async savepoint(fn) {
2027
+ if (!this.activeTxId()) return this.transaction(fn);
2028
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2029
+ try {
2030
+ await this.execute(`SAVEPOINT ${name}`);
2031
+ const result = await fn(this);
2032
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
2033
+ return result;
2034
+ } catch (error) {
2035
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
2036
+ throw error;
2037
+ }
2038
+ }
1961
2039
  async close() {}
1962
2040
  createORM() {
1963
2041
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -1995,6 +2073,8 @@ function splitStatements(sqlContent) {
1995
2073
  let inQuote = false;
1996
2074
  let quoteChar = '';
1997
2075
  let beginEndDepth = 0;
2076
+ let inCte = false;
2077
+ let cteParenDepth = 0;
1998
2078
  const lines = sqlContent.split('\n');
1999
2079
  for (const line of lines){
2000
2080
  let processedLine = '';
@@ -2008,6 +2088,15 @@ function splitStatements(sqlContent) {
2008
2088
  if (beginEndDepth < 0) beginEndDepth = 0;
2009
2089
  }
2010
2090
  }
2091
+ if (!inQuote && lineWithoutComments.trim()) {
2092
+ const withMatch = lineWithoutComments.match(/\bWITH\b.*\bAS\b/i);
2093
+ if (withMatch) inCte = true;
2094
+ if (inCte) for (const char of lineWithoutComments){
2095
+ if ('(' === char) cteParenDepth++;
2096
+ if (')' === char) cteParenDepth--;
2097
+ if (0 === cteParenDepth && ')' === char) inCte = false;
2098
+ }
2099
+ }
2011
2100
  for(let i = 0; i < line.length; i++){
2012
2101
  const char = line[i];
2013
2102
  const prevChar = i > 0 ? line[i - 1] : '';
@@ -2023,7 +2112,7 @@ function splitStatements(sqlContent) {
2023
2112
  quoteChar = char;
2024
2113
  }
2025
2114
  processedLine += char;
2026
- if (';' === char && !inQuote && 0 === beginEndDepth) {
2115
+ if (';' === char && !inQuote && 0 === beginEndDepth && !inCte) {
2027
2116
  currentStatement += processedLine;
2028
2117
  const stmt = currentStatement.trim();
2029
2118
  if (stmt && !stmt.startsWith('--')) statements.push(stmt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pineliner/odb-client",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Isomorphic client for ODB-Lite with postgres.js-like template string SQL support",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -14,7 +14,7 @@
14
14
  "deploy": "bun run build && npm publish --access public"
15
15
  },
16
16
  "dependencies": {
17
- "@libsql/client": "^0.17.0",
17
+ "@libsql/client": "^0.17.3",
18
18
  "node-sql-parser": "^5.3.12"
19
19
  },
20
20
  "devDependencies": {
@@ -1,5 +1,5 @@
1
1
  import type { ODBLiteConfig, ODBLiteConnection, QueryResult, Transaction, TransactionCallback, TransactionMode, SQLFragment } from '../types.ts';
2
- import { HTTPClient } from './http-client.ts';
2
+ import { HTTPClient, type QueryRequestOptions } from './http-client.ts';
3
3
  import { SQLParser } from './sql-parser.ts';
4
4
  import { createBatchTransaction, createSimpleTransaction, SimpleTransaction } from './transaction.ts';
5
5
  import { ConnectionError } from '../types.ts';
@@ -22,10 +22,10 @@ interface SQLFunction {
22
22
  identifier(name: string): string;
23
23
 
24
24
  // Additional postgres.js-like methods
25
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
25
+ query<T = any>(sql: string, params?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>>;
26
26
 
27
27
  // libsql-compatible execute method (for backward compatibility)
28
- execute<T = any>(sql: string | { sql: string; args?: any[] }, args?: any[]): Promise<QueryResult<T>>;
28
+ execute<T = any>(sql: string | { sql: string; args?: any[] }, args?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>>;
29
29
 
30
30
  // Batch execution - runs multiple statements on same connection (for transactions)
31
31
  batch<T = any>(statements: Array<{ sql: string; params?: any[] }>): Promise<BatchResult<T>>;
@@ -59,6 +59,12 @@ export class ODBLiteClient implements ODBLiteConnection {
59
59
  // Handle template string queries (returns Promise)
60
60
  if (Array.isArray(sql) && (('raw' in sql) || (typeof sql[0] === 'string' && values.length >= 0))) {
61
61
  const parsed = SQLParser.parse(sql, values);
62
+ // NOTE: this raw template path sends NO transaction token. The router pins
63
+ // a transaction's statements by txId, so do NOT use `client.sql`...`` (or
64
+ // ODBLiteClient.query) for statements inside a router-pinned transaction —
65
+ // they'd be sent tokenless and treated as foreign work (deadlock-until-
66
+ // watchdog). The DB adapter (ODBLiteConnection) goes through sql.execute()
67
+ // with opts, which carries the token; use the connection, not the raw client.
62
68
  return this.httpClient.query<T>(parsed.sql, parsed.params);
63
69
  }
64
70
 
@@ -75,16 +81,16 @@ export class ODBLiteClient implements ODBLiteConnection {
75
81
  sqlFunction.identifier = (name: string): string => SQLParser.identifier(name);
76
82
 
77
83
  // Attach client methods to the function
78
- sqlFunction.query = async <T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> => {
79
- return await this.httpClient.query<T>(sql, params);
84
+ sqlFunction.query = async <T = any>(sql: string, params: any[] = [], opts?: QueryRequestOptions): Promise<QueryResult<T>> => {
85
+ return await this.httpClient.query<T>(sql, params, opts);
80
86
  };
81
87
 
82
88
  // libsql-compatible execute method (for backward compatibility)
83
- sqlFunction.execute = async <T = any>(sql: string | { sql: string; args?: any[] }, args?: any[]): Promise<QueryResult<T>> => {
89
+ sqlFunction.execute = async <T = any>(sql: string | { sql: string; args?: any[] }, args?: any[], opts?: QueryRequestOptions): Promise<QueryResult<T>> => {
84
90
  if (typeof sql === 'string') {
85
- return await this.httpClient.query<T>(sql, args || []);
91
+ return await this.httpClient.query<T>(sql, args || [], opts);
86
92
  } else {
87
- return await this.httpClient.query<T>(sql.sql, sql.args || []);
93
+ return await this.httpClient.query<T>(sql.sql, sql.args || [], opts);
88
94
  }
89
95
  };
90
96
 
@@ -1,6 +1,14 @@
1
1
  import type { ODBLiteConfig, ODBLiteResponse, QueryResult } from '../types.ts'
2
2
  import { ConnectionError, QueryError } from '../types.ts'
3
3
 
4
+ /** Per-request options for a single query. */
5
+ export interface QueryRequestOptions {
6
+ /** Transaction/session token tying this statement to a server-pinned connection. */
7
+ txId?: string
8
+ /** Override the client's retry count (transaction statements pass 1 = no retry). */
9
+ maxRetries?: number
10
+ }
11
+
4
12
  /**
5
13
  * HTTP client for communicating with ODBLite service
6
14
  */
@@ -23,7 +31,7 @@ export class HTTPClient {
23
31
  /**
24
32
  * Execute a query against the ODBLite service
25
33
  */
26
- async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
34
+ async query<T = any>(sql: string, params: any[] = [], opts?: QueryRequestOptions): Promise<QueryResult<T>> {
27
35
  if (!this.config.databaseId) {
28
36
  throw new ConnectionError('No database ID configured. Use setDatabase() first.')
29
37
  }
@@ -31,7 +39,10 @@ export class HTTPClient {
31
39
  const url = `${this.config.baseUrl}/query/${this.config.databaseId}`
32
40
  const body = {
33
41
  sql,
34
- params
42
+ params,
43
+ // Transaction/session token — present for every statement inside a
44
+ // connection.transaction() so the router pins them to one connection.
45
+ ...(opts?.txId ? { txId: opts.txId } : {})
35
46
  }
36
47
 
37
48
  try {
@@ -42,7 +53,7 @@ export class HTTPClient {
42
53
  Authorization: `Bearer ${this.config.apiKey}`
43
54
  },
44
55
  body: JSON.stringify(body)
45
- })
56
+ }, opts?.maxRetries)
46
57
 
47
58
  const data: ODBLiteResponse<T> = await response.json()
48
59
 
@@ -186,10 +197,14 @@ export class HTTPClient {
186
197
  /**
187
198
  * Make HTTP request with retry logic
188
199
  */
189
- private async makeRequest(url: string, options: RequestInit): Promise<Response> {
200
+ private async makeRequest(url: string, options: RequestInit, maxRetries?: number): Promise<Response> {
190
201
  let lastError: Error | undefined
202
+ // Transaction-control and in-transaction statements pass maxRetries=1: they
203
+ // are NOT idempotent (a retried COMMIT/INSERT could double-apply or hit a
204
+ // closed transaction), so they must never be auto-retried.
205
+ const retries = maxRetries ?? this.config.retries ?? 3
191
206
 
192
- for (let attempt = 1; attempt <= (this.config.retries || 3); attempt++) {
207
+ for (let attempt = 1; attempt <= retries; attempt++) {
193
208
  try {
194
209
  const controller = new AbortController()
195
210
  const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)
@@ -224,14 +239,14 @@ export class HTTPClient {
224
239
  }
225
240
 
226
241
  // Wait before retry (exponential backoff)
227
- if (attempt < (this.config.retries || 3)) {
242
+ if (attempt < retries) {
228
243
  const delay = Math.min(1000 * 2 ** (attempt - 1), 10000)
229
244
  await new Promise((resolve) => setTimeout(resolve, delay))
230
245
  }
231
246
  }
232
247
  }
233
248
 
234
- throw new ConnectionError(`Failed after ${this.config.retries} attempts: ${lastError?.message}`, lastError)
249
+ throw new ConnectionError(`Failed after ${retries} attempts: ${lastError?.message}`, lastError)
235
250
  }
236
251
 
237
252
  /**
@@ -292,6 +292,27 @@ class BunSQLiteConnection implements Connection {
292
292
  return this.transaction(fn)
293
293
  }
294
294
 
295
+ /**
296
+ * Execute function within an explicit savepoint for partial rollback isolation.
297
+ * If called outside a transaction, behaves like transaction().
298
+ */
299
+ async savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
300
+ if (!this.inTransaction) {
301
+ return this.transaction(fn)
302
+ }
303
+
304
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
305
+ try {
306
+ await this.execute(`SAVEPOINT ${name}`)
307
+ const result = await fn(this)
308
+ await this.execute(`RELEASE SAVEPOINT ${name}`)
309
+ return result
310
+ } catch (error) {
311
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(() => {})
312
+ throw error
313
+ }
314
+ }
315
+
295
316
  /**
296
317
  * Close connection
297
318
  */
@@ -270,6 +270,27 @@ class LibSQLConnection implements Connection {
270
270
  return this.transaction(fn)
271
271
  }
272
272
 
273
+ /**
274
+ * Execute function within an explicit savepoint for partial rollback isolation.
275
+ * If called outside a transaction, behaves like transaction().
276
+ */
277
+ async savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
278
+ if (!this.txClient) {
279
+ return this.transaction(fn)
280
+ }
281
+
282
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
283
+ try {
284
+ await this.execute(`SAVEPOINT ${name}`)
285
+ const result = await fn(this)
286
+ await this.execute(`RELEASE SAVEPOINT ${name}`)
287
+ return result
288
+ } catch (error) {
289
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(() => {})
290
+ throw error
291
+ }
292
+ }
293
+
273
294
  /**
274
295
  * Close connection
275
296
  */
@@ -19,6 +19,45 @@ import {
19
19
  where
20
20
  } from '../sql-template'
21
21
 
22
+ /**
23
+ * AsyncLocalStorage, loaded lazily so the client stays isomorphic. On the server
24
+ * (Node/Bun) it carries the active transaction token across awaits so every
25
+ * statement inside a transaction is tagged with — and only with — its own token,
26
+ * even when other transactions run concurrently on the same shared connection.
27
+ * In browsers/edge (no async_hooks) it's null and we fall back to a per-connection
28
+ * flag, which is sufficient there because that concurrency pattern doesn't occur.
29
+ */
30
+ type TxStore = { txId: string }
31
+ let AsyncLocalStorageCtor: (new () => AsyncLocalStorageLike<TxStore>) | null = null
32
+ interface AsyncLocalStorageLike<T> {
33
+ getStore(): T | undefined
34
+ run<R>(store: T, fn: () => R): R
35
+ }
36
+ try {
37
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
38
+ AsyncLocalStorageCtor = require('node:async_hooks').AsyncLocalStorage
39
+ } catch {
40
+ try {
41
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
42
+ AsyncLocalStorageCtor = require('async_hooks').AsyncLocalStorage
43
+ } catch {
44
+ AsyncLocalStorageCtor = null
45
+ }
46
+ }
47
+
48
+ let txCounter = 0
49
+ /** Generate a process-unique transaction token. */
50
+ function newTxId(): string {
51
+ try {
52
+ const c = (globalThis as any).crypto
53
+ if (c?.randomUUID) return `tx_${c.randomUUID()}`
54
+ } catch {
55
+ // fall through
56
+ }
57
+ txCounter = (txCounter + 1) % Number.MAX_SAFE_INTEGER
58
+ return `tx_${Date.now().toString(36)}_${txCounter.toString(36)}`
59
+ }
60
+
22
61
  /**
23
62
  * Parse JSON columns in query results
24
63
  * Only parses if the value is a string (to avoid double-parsing)
@@ -139,7 +178,14 @@ export class ODBLiteAdapter implements DatabaseAdapter {
139
178
  class ODBLiteConnection implements Connection {
140
179
  private client: ODBLiteClient
141
180
  private serviceClient: ServiceClient
142
- private inTransaction = false
181
+ // Carries the active transaction token across awaits (server only). Distinguishes
182
+ // a genuinely-nested transaction() call (same async context) from a concurrent
183
+ // top-level one on this shared connection — replacing the old shared
184
+ // `inTransaction` boolean, which leaked between concurrent requests.
185
+ private txStorage: AsyncLocalStorageLike<TxStore> | null =
186
+ AsyncLocalStorageCtor ? new AsyncLocalStorageCtor() : null
187
+ // Browser/edge fallback when AsyncLocalStorage is unavailable.
188
+ private fallbackTxId?: string
143
189
 
144
190
  // Public metadata fields
145
191
  public databaseName: string
@@ -209,6 +255,23 @@ class ODBLiteConnection implements Connection {
209
255
  return this.execute(sql, params, options) as Promise<QueryResult<T>>
210
256
  }
211
257
 
258
+ /** The token of the transaction this async context belongs to, if any. */
259
+ private activeTxId(): string | undefined {
260
+ return this.txStorage ? this.txStorage.getStore()?.txId : this.fallbackTxId
261
+ }
262
+
263
+ /**
264
+ * Per-request options for the underlying client. Inside a transaction every
265
+ * statement carries the transaction token (so the router pins it to the
266
+ * transaction's connection) and disables retries (transaction statements are
267
+ * not idempotent — a retried COMMIT/INSERT could double-apply or hit a closed
268
+ * transaction).
269
+ */
270
+ private requestOpts(): { txId?: string; maxRetries?: number } | undefined {
271
+ const txId = this.activeTxId()
272
+ return txId ? { txId, maxRetries: 1 } : undefined
273
+ }
274
+
212
275
  /**
213
276
  * Execute SQL with parameters
214
277
  */
@@ -217,6 +280,7 @@ class ODBLiteConnection implements Connection {
217
280
  let rows: any[]
218
281
  let rowsAffected: number
219
282
  let lastInsertRowid: any
283
+ const reqOpts = this.requestOpts()
220
284
 
221
285
  // Handle object format { sql, args }
222
286
  if (typeof sql === 'object') {
@@ -230,7 +294,7 @@ class ODBLiteConnection implements Connection {
230
294
  if (options?.stringifyParams) {
231
295
  args = stringifyJsonParams(args, options.stringifyParams)
232
296
  }
233
- const result = await this.client.sql.execute(sql.sql, args)
297
+ const result = await this.client.sql.execute(sql.sql, args, reqOpts)
234
298
  rows = result.rows
235
299
  rowsAffected = result.rowsAffected || 0
236
300
  lastInsertRowid = (result as any).lastInsertRowid
@@ -245,7 +309,7 @@ class ODBLiteConnection implements Connection {
245
309
  if (options?.stringifyParams) {
246
310
  args = stringifyJsonParams(args, options.stringifyParams)
247
311
  }
248
- const result = await this.client.sql.execute(sql, args)
312
+ const result = await this.client.sql.execute(sql, args, reqOpts)
249
313
  rows = result.rows
250
314
  rowsAffected = result.rowsAffected || 0
251
315
  lastInsertRowid = (result as any).lastInsertRowid
@@ -300,42 +364,42 @@ class ODBLiteConnection implements Connection {
300
364
  * same connection without interleaving from other requests.
301
365
  */
302
366
  async transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
303
- if (this.inTransaction) {
304
- // Nested transaction - use savepoint
305
- const savepointName = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
306
- try {
307
- await this.execute(`SAVEPOINT ${savepointName}`)
308
- const result = await fn(this)
309
- await this.execute(`RELEASE SAVEPOINT ${savepointName}`)
310
- return result
311
- } catch (error) {
312
- await this.execute(`ROLLBACK TO SAVEPOINT ${savepointName}`).catch(() => {})
313
- throw error
314
- }
367
+ if (this.activeTxId()) {
368
+ // Genuinely nested call: this async context already owns a transaction on
369
+ // this connection — join it (no nested BEGIN), exactly like the previous
370
+ // reentrancy behaviour. Convenience functions that each wrap in
371
+ // db.transaction() compose freely inside an outer transaction.
372
+ return fn(this)
315
373
  }
316
374
 
317
- this.inTransaction = true
318
-
319
- try {
320
- // Start transaction
321
- await this.execute('BEGIN')
375
+ // New top-level transaction: mint a token so the router pins every one of
376
+ // this transaction's statements to a single connection and never lets a
377
+ // concurrent transaction's (or autocommit) statements interleave onto it.
378
+ const txId = newTxId()
322
379
 
380
+ const run = async (): Promise<T> => {
381
+ const prevFallback = this.fallbackTxId
382
+ if (!this.txStorage) this.fallbackTxId = txId
323
383
  try {
324
- // Execute user function - each statement executes immediately
325
- const result = await fn(this)
326
-
327
- // Commit transaction
328
- await this.execute('COMMIT')
329
-
330
- return result
331
- } catch (error) {
332
- // Rollback on error
333
- await this.execute('ROLLBACK').catch(() => {})
334
- throw error
384
+ // Start transaction (tagged with txId + no-retry via requestOpts())
385
+ await this.execute('BEGIN')
386
+ try {
387
+ // Execute user function - each statement executes immediately
388
+ const result = await fn(this)
389
+ // Commit transaction
390
+ await this.execute('COMMIT')
391
+ return result
392
+ } catch (error) {
393
+ // Rollback on error
394
+ await this.execute('ROLLBACK').catch(() => {})
395
+ throw error
396
+ }
397
+ } finally {
398
+ if (!this.txStorage) this.fallbackTxId = prevFallback
335
399
  }
336
- } finally {
337
- this.inTransaction = false
338
400
  }
401
+
402
+ return this.txStorage ? this.txStorage.run({ txId }, run) : run()
339
403
  }
340
404
 
341
405
  /**
@@ -345,6 +409,27 @@ class ODBLiteConnection implements Connection {
345
409
  return this.transaction(fn)
346
410
  }
347
411
 
412
+ /**
413
+ * Execute function within an explicit savepoint for partial rollback isolation.
414
+ * If called outside a transaction, behaves like transaction().
415
+ */
416
+ async savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
417
+ if (!this.activeTxId()) {
418
+ return this.transaction(fn)
419
+ }
420
+
421
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
422
+ try {
423
+ await this.execute(`SAVEPOINT ${name}`)
424
+ const result = await fn(this)
425
+ await this.execute(`RELEASE SAVEPOINT ${name}`)
426
+ return result
427
+ } catch (error) {
428
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(() => {})
429
+ throw error
430
+ }
431
+ }
432
+
348
433
  /**
349
434
  * Close connection
350
435
  */
@@ -20,7 +20,7 @@ export function parseSQL(sqlContent: string, options: SQLParserOptions = {}): Pa
20
20
  if (!separatePragma) {
21
21
  return {
22
22
  pragmaStatements: [],
23
- regularStatements: statements,
23
+ regularStatements: statements
24
24
  }
25
25
  }
26
26
 
@@ -49,6 +49,8 @@ function splitStatements(sqlContent: string): string[] {
49
49
  let inQuote = false
50
50
  let quoteChar = ''
51
51
  let beginEndDepth = 0
52
+ let inCte = false
53
+ let cteParenDepth = 0
52
54
 
53
55
  const lines = sqlContent.split('\n')
54
56
 
@@ -72,6 +74,24 @@ function splitStatements(sqlContent: string): string[] {
72
74
  }
73
75
  }
74
76
 
77
+ // Track CTE (WITH ... AS) parenthesis depth
78
+ if (!inQuote && lineWithoutComments.trim()) {
79
+ const withMatch = lineWithoutComments.match(/\bWITH\b.*\bAS\b/i)
80
+ if (withMatch) {
81
+ inCte = true
82
+ }
83
+
84
+ if (inCte) {
85
+ for (const char of lineWithoutComments) {
86
+ if (char === '(') cteParenDepth++
87
+ if (char === ')') cteParenDepth--
88
+ if (cteParenDepth === 0 && char === ')') {
89
+ inCte = false
90
+ }
91
+ }
92
+ }
93
+ }
94
+
75
95
  for (let i = 0; i < line.length; i++) {
76
96
  const char = line[i]
77
97
  const prevChar = i > 0 ? line[i - 1] : ''
@@ -95,8 +115,8 @@ function splitStatements(sqlContent: string): string[] {
95
115
 
96
116
  processedLine += char
97
117
 
98
- // Split on semicolon when not in quotes AND not inside BEGIN...END
99
- if (char === ';' && !inQuote && beginEndDepth === 0) {
118
+ // Split on semicolon when not in quotes AND not inside BEGIN...END AND not inside CTE
119
+ if (char === ';' && !inQuote && beginEndDepth === 0 && !inCte) {
100
120
  currentStatement += processedLine
101
121
  const stmt = currentStatement.trim()
102
122
  if (stmt && !stmt.startsWith('--')) {
@@ -77,6 +77,11 @@ export interface Connection {
77
77
  transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T>
78
78
  begin<T>(fn: (tx: Connection) => Promise<T>): Promise<T>
79
79
 
80
+ // Savepoint support — explicit partial rollback isolation within a transaction.
81
+ // Use when you need an inner operation to fail without rolling back the outer transaction.
82
+ // If called outside a transaction, behaves like transaction().
83
+ savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T>
84
+
80
85
  // Connection management
81
86
  close(): Promise<void>
82
87