@pineliner/odb-client 1.1.2 → 1.1.4

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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  var __webpack_modules__ = {
3
- "./src/orm/index.ts": function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
3
+ "./src/orm/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
4
4
  __webpack_require__.r(__webpack_exports__);
5
5
  __webpack_require__.d(__webpack_exports__, {
6
6
  ORM: ()=>ORM,
@@ -446,7 +446,7 @@ function __webpack_require__(moduleId) {
446
446
  })();
447
447
  (()=>{
448
448
  __webpack_require__.r = (exports1)=>{
449
- if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
449
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
450
450
  value: 'Module'
451
451
  });
452
452
  Object.defineProperty(exports1, '__esModule', {
@@ -458,53 +458,53 @@ var __webpack_exports__ = {};
458
458
  (()=>{
459
459
  __webpack_require__.r(__webpack_exports__);
460
460
  __webpack_require__.d(__webpack_exports__, {
461
- insertValues: ()=>src_insertValues,
462
- updateSet: ()=>src_updateSet,
463
461
  gte: ()=>orm.gte,
464
- ODBLiteClient: ()=>ODBLiteClient,
465
- QueryError: ()=>types_QueryError,
462
+ ODBLiteTransaction: ()=>ODBLiteTransaction,
463
+ updateSet: ()=>src_updateSet,
466
464
  sql: ()=>sql_parser_sql,
467
- sqlFragment: ()=>sql_template_fragment,
468
- ODBLiteError: ()=>ODBLiteError,
465
+ isNotNull: ()=>orm.isNotNull,
466
+ LibSQLAdapter: ()=>LibSQLAdapter,
469
467
  ORM: ()=>orm.ORM,
470
- ConnectionError: ()=>ConnectionError,
471
468
  join: ()=>src_join,
472
- convertTemplateToQuery: ()=>convertTemplateToQuery,
473
- HTTPClient: ()=>HTTPClient,
474
- BunSQLiteAdapter: ()=>BunSQLiteAdapter,
475
- SQLParser: ()=>sql_parser_SQLParser,
469
+ lte: ()=>orm.lte,
476
470
  default: ()=>odblite,
471
+ splitSQLStatements: ()=>splitSQLStatements,
472
+ ne: ()=>orm.ne,
477
473
  gt: ()=>orm.gt,
478
474
  eq: ()=>orm.eq,
479
- identifier: ()=>src_identifier,
480
- lte: ()=>orm.lte,
481
- ne: ()=>orm.ne,
482
- parseSQL: ()=>parseSQL,
483
- splitSQLStatements: ()=>splitSQLStatements,
484
- where: ()=>src_where,
485
- LibSQLAdapter: ()=>LibSQLAdapter,
486
- ORMTransaction: ()=>orm.Transaction,
487
- lt: ()=>orm.lt,
488
475
  fragment: ()=>fragment,
476
+ where: ()=>src_where,
477
+ identifier: ()=>src_identifier,
478
+ insertValues: ()=>src_insertValues,
479
+ ConnectionError: ()=>ConnectionError,
480
+ ODBLiteClient: ()=>ODBLiteClient,
489
481
  isNull: ()=>orm.isNull,
490
- DatabaseManager: ()=>DatabaseManager,
482
+ HTTPClient: ()=>HTTPClient,
483
+ SQLParser: ()=>sql_parser_SQLParser,
484
+ BunSQLiteAdapter: ()=>BunSQLiteAdapter,
485
+ QueryError: ()=>types_QueryError,
486
+ createORM: ()=>orm.createORM,
487
+ lt: ()=>orm.lt,
491
488
  or: ()=>orm.or,
492
- ODBLiteTransaction: ()=>ODBLiteTransaction,
493
- ServiceClient: ()=>ServiceClient,
494
489
  and: ()=>orm.and,
495
490
  odblite: ()=>odblite,
491
+ parseSQL: ()=>parseSQL,
496
492
  rawSQL: ()=>sql_template_raw,
497
- set: ()=>set,
498
- sqlJoin: ()=>sql_template_join,
499
- createORM: ()=>orm.createORM,
493
+ ServiceClient: ()=>ServiceClient,
494
+ ORMTransaction: ()=>orm.Transaction,
495
+ ODBLiteAdapter: ()=>ODBLiteAdapter,
496
+ convertTemplateToQuery: ()=>convertTemplateToQuery,
500
497
  empty: ()=>empty,
501
- sqlTemplate: ()=>sql_template_sql,
498
+ set: ()=>set,
499
+ ODBLiteError: ()=>ODBLiteError,
502
500
  SimpleTransaction: ()=>SimpleTransaction,
503
501
  inArray: ()=>orm.inArray,
502
+ sqlFragment: ()=>sql_template_fragment,
503
+ sqlJoin: ()=>sql_template_join,
504
504
  sqlWhere: ()=>sql_template_where,
505
- ODBLiteAdapter: ()=>ODBLiteAdapter,
506
- isNotNull: ()=>orm.isNotNull,
505
+ DatabaseManager: ()=>DatabaseManager,
507
506
  like: ()=>orm.like,
507
+ sqlTemplate: ()=>sql_template_sql,
508
508
  raw: ()=>src_raw
509
509
  });
510
510
  class ODBLiteError extends Error {
@@ -1697,6 +1697,19 @@ var __webpack_exports__ = {};
1697
1697
  async begin(fn) {
1698
1698
  return this.transaction(fn);
1699
1699
  }
1700
+ async savepoint(fn) {
1701
+ if (!this.inTransaction) return this.transaction(fn);
1702
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1703
+ try {
1704
+ await this.execute(`SAVEPOINT ${name}`);
1705
+ const result = await fn(this);
1706
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1707
+ return result;
1708
+ } catch (error) {
1709
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1710
+ throw error;
1711
+ }
1712
+ }
1700
1713
  async close() {
1701
1714
  this.db.close();
1702
1715
  }
@@ -1853,6 +1866,19 @@ var __webpack_exports__ = {};
1853
1866
  async begin(fn) {
1854
1867
  return this.transaction(fn);
1855
1868
  }
1869
+ async savepoint(fn) {
1870
+ if (!this.txClient) return this.transaction(fn);
1871
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1872
+ try {
1873
+ await this.execute(`SAVEPOINT ${name}`);
1874
+ const result = await fn(this);
1875
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1876
+ return result;
1877
+ } catch (error) {
1878
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1879
+ throw error;
1880
+ }
1881
+ }
1856
1882
  async close() {}
1857
1883
  createORM() {
1858
1884
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -2022,53 +2048,16 @@ var __webpack_exports__ = {};
2022
2048
  async transaction(fn) {
2023
2049
  if (this.inTransaction) return fn(this);
2024
2050
  this.inTransaction = true;
2025
- const queuedStatements = [];
2026
- const txConnection = {
2027
- ...this,
2028
- execute: async (sql, params = [], options)=>{
2029
- const sqlStr = 'string' == typeof sql ? sql : sql.sql;
2030
- const sqlParams = 'string' == typeof sql ? params : sql.args || [];
2031
- queuedStatements.push({
2032
- sql: sqlStr,
2033
- params: sqlParams
2034
- });
2035
- return {
2036
- rows: [],
2037
- rowsAffected: 0
2038
- };
2039
- },
2040
- query: async (sql, params = [], options)=>{
2041
- queuedStatements.push({
2042
- sql,
2043
- params
2044
- });
2045
- return {
2046
- rows: [],
2047
- rowsAffected: 0
2048
- };
2049
- },
2050
- transaction: async (innerFn)=>innerFn(txConnection),
2051
- begin: async (innerFn)=>innerFn(txConnection)
2052
- };
2053
2051
  try {
2054
- const result = await fn(txConnection);
2055
- if (queuedStatements.length > 0) {
2056
- const batchStatements = [
2057
- {
2058
- sql: 'BEGIN',
2059
- params: []
2060
- },
2061
- ...queuedStatements,
2062
- {
2063
- sql: 'COMMIT',
2064
- params: []
2065
- }
2066
- ];
2067
- await this.client.sql.batch(batchStatements);
2052
+ await this.execute('BEGIN');
2053
+ try {
2054
+ const result = await fn(this);
2055
+ await this.execute('COMMIT');
2056
+ return result;
2057
+ } catch (error) {
2058
+ await this.execute('ROLLBACK').catch(()=>{});
2059
+ throw error;
2068
2060
  }
2069
- return result;
2070
- } catch (error) {
2071
- throw error;
2072
2061
  } finally{
2073
2062
  this.inTransaction = false;
2074
2063
  }
@@ -2076,6 +2065,19 @@ var __webpack_exports__ = {};
2076
2065
  async begin(fn) {
2077
2066
  return this.transaction(fn);
2078
2067
  }
2068
+ async savepoint(fn) {
2069
+ if (!this.inTransaction) return this.transaction(fn);
2070
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
2071
+ try {
2072
+ await this.execute(`SAVEPOINT ${name}`);
2073
+ const result = await fn(this);
2074
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
2075
+ return result;
2076
+ } catch (error) {
2077
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
2078
+ throw error;
2079
+ }
2080
+ }
2079
2081
  async close() {}
2080
2082
  createORM() {
2081
2083
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -2264,11 +2266,11 @@ var __webpack_exports__ = {};
2264
2266
  return Array.from(this.connections.keys());
2265
2267
  }
2266
2268
  async close() {
2267
- console.log(`🔌 Closing all database connections...`);
2269
+ console.log("\uD83D\uDD0C Closing all database connections...");
2268
2270
  for (const [name, conn] of this.connections)await conn.close();
2269
2271
  this.connections.clear();
2270
2272
  this.connectionTimestamps.clear();
2271
- console.log(`✅ All connections closed`);
2273
+ console.log("✅ All connections closed");
2272
2274
  }
2273
2275
  async runMigrations(conn, config) {
2274
2276
  const { pragmaStatements, regularStatements } = parseSQL(config.schemaContent);
@@ -2377,7 +2379,7 @@ exports.sqlTemplate = __webpack_exports__.sqlTemplate;
2377
2379
  exports.sqlWhere = __webpack_exports__.sqlWhere;
2378
2380
  exports.updateSet = __webpack_exports__.updateSet;
2379
2381
  exports.where = __webpack_exports__.where;
2380
- for(var __webpack_i__ in __webpack_exports__)if (-1 === [
2382
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
2381
2383
  "BunSQLiteAdapter",
2382
2384
  "ConnectionError",
2383
2385
  "DatabaseManager",
@@ -2426,7 +2428,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
2426
2428
  "sqlWhere",
2427
2429
  "updateSet",
2428
2430
  "where"
2429
- ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
2431
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
2430
2432
  Object.defineProperty(exports, '__esModule', {
2431
2433
  value: true
2432
2434
  });
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { __webpack_require__ } from "./rslib-runtime.js";
1
2
  import { Database } from "bun:sqlite";
2
3
  import { createClient } from "@libsql/client";
3
4
  import node_fs from "node:fs";
4
- var __webpack_modules__ = {
5
- "./src/orm/index.ts": function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
5
+ __webpack_require__.add({
6
+ "./src/orm/index.ts" (__unused_rspack_module, __webpack_exports__, __webpack_require__) {
6
7
  __webpack_require__.r(__webpack_exports__);
7
8
  __webpack_require__.d(__webpack_exports__, {
8
9
  ORM: ()=>ORM,
@@ -415,38 +416,7 @@ var __webpack_modules__ = {
415
416
  return new ORM(db);
416
417
  }
417
418
  }
418
- };
419
- var __webpack_module_cache__ = {};
420
- function __webpack_require__(moduleId) {
421
- var cachedModule = __webpack_module_cache__[moduleId];
422
- if (void 0 !== cachedModule) return cachedModule.exports;
423
- var module = __webpack_module_cache__[moduleId] = {
424
- exports: {}
425
- };
426
- __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
427
- return module.exports;
428
- }
429
- (()=>{
430
- __webpack_require__.d = (exports, definition)=>{
431
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
432
- enumerable: true,
433
- get: definition[key]
434
- });
435
- };
436
- })();
437
- (()=>{
438
- __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
439
- })();
440
- (()=>{
441
- __webpack_require__.r = (exports)=>{
442
- if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, {
443
- value: 'Module'
444
- });
445
- Object.defineProperty(exports, '__esModule', {
446
- value: true
447
- });
448
- };
449
- })();
419
+ });
450
420
  class ODBLiteError extends Error {
451
421
  code;
452
422
  query;
@@ -1636,6 +1606,19 @@ class BunSQLiteConnection {
1636
1606
  async begin(fn) {
1637
1607
  return this.transaction(fn);
1638
1608
  }
1609
+ async savepoint(fn) {
1610
+ if (!this.inTransaction) return this.transaction(fn);
1611
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1612
+ try {
1613
+ await this.execute(`SAVEPOINT ${name}`);
1614
+ const result = await fn(this);
1615
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1616
+ return result;
1617
+ } catch (error) {
1618
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1619
+ throw error;
1620
+ }
1621
+ }
1639
1622
  async close() {
1640
1623
  this.db.close();
1641
1624
  }
@@ -1791,6 +1774,19 @@ class LibSQLConnection {
1791
1774
  async begin(fn) {
1792
1775
  return this.transaction(fn);
1793
1776
  }
1777
+ async savepoint(fn) {
1778
+ if (!this.txClient) return this.transaction(fn);
1779
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1780
+ try {
1781
+ await this.execute(`SAVEPOINT ${name}`);
1782
+ const result = await fn(this);
1783
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1784
+ return result;
1785
+ } catch (error) {
1786
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1787
+ throw error;
1788
+ }
1789
+ }
1794
1790
  async close() {}
1795
1791
  createORM() {
1796
1792
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -1960,53 +1956,16 @@ class ODBLiteConnection {
1960
1956
  async transaction(fn) {
1961
1957
  if (this.inTransaction) return fn(this);
1962
1958
  this.inTransaction = true;
1963
- const queuedStatements = [];
1964
- const txConnection = {
1965
- ...this,
1966
- execute: async (sql, params = [], options)=>{
1967
- const sqlStr = 'string' == typeof sql ? sql : sql.sql;
1968
- const sqlParams = 'string' == typeof sql ? params : sql.args || [];
1969
- queuedStatements.push({
1970
- sql: sqlStr,
1971
- params: sqlParams
1972
- });
1973
- return {
1974
- rows: [],
1975
- rowsAffected: 0
1976
- };
1977
- },
1978
- query: async (sql, params = [], options)=>{
1979
- queuedStatements.push({
1980
- sql,
1981
- params
1982
- });
1983
- return {
1984
- rows: [],
1985
- rowsAffected: 0
1986
- };
1987
- },
1988
- transaction: async (innerFn)=>innerFn(txConnection),
1989
- begin: async (innerFn)=>innerFn(txConnection)
1990
- };
1991
1959
  try {
1992
- const result = await fn(txConnection);
1993
- if (queuedStatements.length > 0) {
1994
- const batchStatements = [
1995
- {
1996
- sql: 'BEGIN',
1997
- params: []
1998
- },
1999
- ...queuedStatements,
2000
- {
2001
- sql: 'COMMIT',
2002
- params: []
2003
- }
2004
- ];
2005
- await this.client.sql.batch(batchStatements);
1960
+ await this.execute('BEGIN');
1961
+ try {
1962
+ const result = await fn(this);
1963
+ await this.execute('COMMIT');
1964
+ return result;
1965
+ } catch (error) {
1966
+ await this.execute('ROLLBACK').catch(()=>{});
1967
+ throw error;
2006
1968
  }
2007
- return result;
2008
- } catch (error) {
2009
- throw error;
2010
1969
  } finally{
2011
1970
  this.inTransaction = false;
2012
1971
  }
@@ -2014,6 +1973,19 @@ class ODBLiteConnection {
2014
1973
  async begin(fn) {
2015
1974
  return this.transaction(fn);
2016
1975
  }
1976
+ async savepoint(fn) {
1977
+ if (!this.inTransaction) return this.transaction(fn);
1978
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1979
+ try {
1980
+ await this.execute(`SAVEPOINT ${name}`);
1981
+ const result = await fn(this);
1982
+ await this.execute(`RELEASE SAVEPOINT ${name}`);
1983
+ return result;
1984
+ } catch (error) {
1985
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(()=>{});
1986
+ throw error;
1987
+ }
1988
+ }
2017
1989
  async close() {}
2018
1990
  createORM() {
2019
1991
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -2200,11 +2172,11 @@ class DatabaseManager {
2200
2172
  return Array.from(this.connections.keys());
2201
2173
  }
2202
2174
  async close() {
2203
- console.log(`🔌 Closing all database connections...`);
2175
+ console.log("\uD83D\uDD0C Closing all database connections...");
2204
2176
  for (const [name, conn] of this.connections)await conn.close();
2205
2177
  this.connections.clear();
2206
2178
  this.connectionTimestamps.clear();
2207
- console.log(`✅ All connections closed`);
2179
+ console.log("✅ All connections closed");
2208
2180
  }
2209
2181
  async runMigrations(conn, config) {
2210
2182
  const { pragmaStatements, regularStatements } = parseSQL(config.schemaContent);
@@ -2262,21 +2234,22 @@ class DatabaseManager {
2262
2234
  }
2263
2235
  }
2264
2236
  }
2265
- var orm = __webpack_require__("./src/orm/index.ts");
2266
2237
  const { raw: src_raw, identifier: src_identifier, where: src_where, insertValues: src_insertValues, updateSet: src_updateSet, join: src_join } = ODBLiteClient;
2267
- var __webpack_exports__ORM = orm.ORM;
2268
- var __webpack_exports__ORMTransaction = orm.Transaction;
2269
- var __webpack_exports__and = orm.and;
2270
- var __webpack_exports__createORM = orm.createORM;
2271
- var __webpack_exports__eq = orm.eq;
2272
- var __webpack_exports__gt = orm.gt;
2273
- var __webpack_exports__gte = orm.gte;
2274
- var __webpack_exports__inArray = orm.inArray;
2275
- var __webpack_exports__isNotNull = orm.isNotNull;
2276
- var __webpack_exports__isNull = orm.isNull;
2277
- var __webpack_exports__like = orm.like;
2278
- var __webpack_exports__lt = orm.lt;
2279
- var __webpack_exports__lte = orm.lte;
2280
- var __webpack_exports__ne = orm.ne;
2281
- var __webpack_exports__or = orm.or;
2282
- export { BunSQLiteAdapter, ConnectionError, DatabaseManager, HTTPClient, LibSQLAdapter, ODBLiteAdapter, ODBLiteClient, ODBLiteError, ODBLiteTransaction, types_QueryError as QueryError, sql_parser_SQLParser as SQLParser, ServiceClient, SimpleTransaction, convertTemplateToQuery, odblite as default, empty, fragment, src_identifier as identifier, src_insertValues as insertValues, src_join as join, odblite, parseSQL, src_raw as raw, sql_template_raw as rawSQL, set, splitSQLStatements, sql_parser_sql as sql, sql_template_fragment as sqlFragment, sql_template_join as sqlJoin, sql_template_sql as sqlTemplate, sql_template_where as sqlWhere, src_updateSet as updateSet, src_where as where, __webpack_exports__ORM as ORM, __webpack_exports__ORMTransaction as ORMTransaction, __webpack_exports__and as and, __webpack_exports__createORM as createORM, __webpack_exports__eq as eq, __webpack_exports__gt as gt, __webpack_exports__gte as gte, __webpack_exports__inArray as inArray, __webpack_exports__isNotNull as isNotNull, __webpack_exports__isNull as isNull, __webpack_exports__like as like, __webpack_exports__lt as lt, __webpack_exports__lte as lte, __webpack_exports__ne as ne, __webpack_exports__or as or };
2238
+ const orm = __webpack_require__("./src/orm/index.ts");
2239
+ var ORM = orm.ORM;
2240
+ var Transaction = orm.Transaction;
2241
+ var and = orm.and;
2242
+ var createORM_0 = orm.createORM;
2243
+ var eq = orm.eq;
2244
+ var gt = orm.gt;
2245
+ var gte = orm.gte;
2246
+ var inArray = orm.inArray;
2247
+ var isNotNull = orm.isNotNull;
2248
+ var isNull = orm.isNull;
2249
+ var like = orm.like;
2250
+ var lt = orm.lt;
2251
+ var lte = orm.lte;
2252
+ var ne = orm.ne;
2253
+ var or = orm.or;
2254
+ export default odblite;
2255
+ export { BunSQLiteAdapter, ConnectionError, DatabaseManager, HTTPClient, LibSQLAdapter, ODBLiteAdapter, ODBLiteClient, ODBLiteError, ODBLiteTransaction, ORM, ServiceClient, SimpleTransaction, Transaction as ORMTransaction, and, convertTemplateToQuery, createORM_0 as createORM, empty, eq, fragment, gt, gte, inArray, isNotNull, isNull, like, lt, lte, ne, odblite, or, parseSQL, set, splitSQLStatements, sql_parser_SQLParser as SQLParser, sql_parser_sql as sql, sql_template_fragment as sqlFragment, sql_template_join as sqlJoin, sql_template_raw as rawSQL, sql_template_sql as sqlTemplate, sql_template_where as sqlWhere, src_identifier as identifier, src_insertValues as insertValues, src_join as join, src_raw as raw, src_updateSet as updateSet, src_where as where, types_QueryError as QueryError };
@@ -0,0 +1,39 @@
1
+ var __webpack_modules__ = {};
2
+ var __webpack_module_cache__ = {};
3
+ function __webpack_require__(moduleId) {
4
+ var cachedModule = __webpack_module_cache__[moduleId];
5
+ if (void 0 !== cachedModule) return cachedModule.exports;
6
+ var module = __webpack_module_cache__[moduleId] = {
7
+ exports: {}
8
+ };
9
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
10
+ return module.exports;
11
+ }
12
+ __webpack_require__.m = __webpack_modules__;
13
+ (()=>{
14
+ __webpack_require__.add = function(modules) {
15
+ Object.assign(__webpack_require__.m, modules);
16
+ };
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.d = (exports, definition)=>{
20
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
21
+ enumerable: true,
22
+ get: definition[key]
23
+ });
24
+ };
25
+ })();
26
+ (()=>{
27
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
28
+ })();
29
+ (()=>{
30
+ __webpack_require__.r = (exports)=>{
31
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, {
32
+ value: 'Module'
33
+ });
34
+ Object.defineProperty(exports, '__esModule', {
35
+ value: true
36
+ });
37
+ };
38
+ })();
39
+ export { __webpack_require__ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pineliner/odb-client",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
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,12 +14,12 @@
14
14
  "deploy": "bun run build && npm publish --access public"
15
15
  },
16
16
  "dependencies": {
17
- "@libsql/client": "^0.15.15",
17
+ "@libsql/client": "^0.17.0",
18
18
  "node-sql-parser": "^5.3.12"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/bun": "latest",
22
- "@rslib/core": "0.15.0",
22
+ "@rslib/core": "0.19.3",
23
23
  "typescript": "^5.9.2"
24
24
  },
25
25
  "exports": {
@@ -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
  */
@@ -289,61 +289,43 @@ class ODBLiteConnection implements Connection {
289
289
  }
290
290
 
291
291
  /**
292
- * Execute function in transaction using batch endpoint
293
- * All statements are collected and executed atomically
292
+ * Execute function in transaction using immediate execution mode
293
+ *
294
+ * Each statement is executed immediately via the query endpoint,
295
+ * wrapped in BEGIN/COMMIT. This allows lastInsertRowid to be
296
+ * available for subsequent statements within the transaction.
297
+ *
298
+ * NOTE: Atomicity is maintained by the router's transaction lock
299
+ * which ensures all statements from BEGIN to COMMIT run on the
300
+ * same connection without interleaving from other requests.
294
301
  */
295
302
  async transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
296
303
  if (this.inTransaction) {
297
- // Nested transaction - just execute the function
304
+ // Already in transaction - join existing transaction (no savepoint overhead).
305
+ // This allows convenience functions that each wrap in db.transaction()
306
+ // to be freely composed inside an outer transaction.
298
307
  return fn(this)
299
308
  }
300
309
 
301
310
  this.inTransaction = true
302
- const queuedStatements: Array<{ sql: string; params: any[] }> = []
303
311
 
304
- // Create a transactional connection that queues statements
305
- const txConnection: Connection = {
306
- ...this,
307
- execute: async (sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: any) => {
308
- const sqlStr = typeof sql === 'string' ? sql : sql.sql
309
- const sqlParams = typeof sql === 'string' ? params : (sql.args || [])
312
+ try {
313
+ // Start transaction
314
+ await this.execute('BEGIN')
310
315
 
311
- // Queue the statement
312
- queuedStatements.push({ sql: sqlStr, params: sqlParams })
316
+ try {
317
+ // Execute user function - each statement executes immediately
318
+ const result = await fn(this)
313
319
 
314
- // Return placeholder
315
- return { rows: [], rowsAffected: 0 }
316
- },
317
- query: async (sql: string, params: any[] = [], options?: any) => {
318
- queuedStatements.push({ sql, params })
319
- return { rows: [], rowsAffected: 0 }
320
- },
321
- transaction: async (innerFn: any) => {
322
- // For nested transactions, just execute inline
323
- return innerFn(txConnection)
324
- },
325
- begin: async (innerFn: any) => {
326
- return innerFn(txConnection)
327
- }
328
- } as Connection
320
+ // Commit transaction
321
+ await this.execute('COMMIT')
329
322
 
330
- try {
331
- const result = await fn(txConnection)
332
-
333
- // Commit: execute all statements as a batch
334
- if (queuedStatements.length > 0) {
335
- const batchStatements = [
336
- { sql: 'BEGIN', params: [] },
337
- ...queuedStatements,
338
- { sql: 'COMMIT', params: [] }
339
- ]
340
- await this.client.sql.batch(batchStatements)
323
+ return result
324
+ } catch (error) {
325
+ // Rollback on error
326
+ await this.execute('ROLLBACK').catch(() => {})
327
+ throw error
341
328
  }
342
-
343
- return result
344
- } catch (error) {
345
- // On error, batch is never sent - implicit rollback
346
- throw error
347
329
  } finally {
348
330
  this.inTransaction = false
349
331
  }
@@ -356,6 +338,27 @@ class ODBLiteConnection implements Connection {
356
338
  return this.transaction(fn)
357
339
  }
358
340
 
341
+ /**
342
+ * Execute function within an explicit savepoint for partial rollback isolation.
343
+ * If called outside a transaction, behaves like transaction().
344
+ */
345
+ async savepoint<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
346
+ if (!this.inTransaction) {
347
+ return this.transaction(fn)
348
+ }
349
+
350
+ const name = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
351
+ try {
352
+ await this.execute(`SAVEPOINT ${name}`)
353
+ const result = await fn(this)
354
+ await this.execute(`RELEASE SAVEPOINT ${name}`)
355
+ return result
356
+ } catch (error) {
357
+ await this.execute(`ROLLBACK TO SAVEPOINT ${name}`).catch(() => {})
358
+ throw error
359
+ }
360
+ }
361
+
359
362
  /**
360
363
  * Close connection
361
364
  */
@@ -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