@pineliner/odb-client 1.1.3 → 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":"AAIA;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,cAAc,GACd,WAAW,GACX,WAAW,CAAA;AAEf;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IAEtB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IAGzB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IAG3E,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;IAGvC,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5F,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAClH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAA;IAGvC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC9D,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAGxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAGtB,SAAS,CAAC,IAAI,GAAG,CAAA;IAGjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IAGrB,gBAAgB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC/C,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAA;IACF,aAAa,CAAC,IAAI,OAAO,CAAC;QACxB,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,CAAA;CACH;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7C,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACnC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAG1B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAG5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAEpC,OAAO,EAAE,WAAW,CAAA;IAGpB,YAAY,EAAE,MAAM,CAAA;IAGpB,MAAM,CAAC,EAAE;QACP,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;IAED,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;IAGD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,IAAI,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -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");
@@ -2020,18 +2046,7 @@ var __webpack_exports__ = {};
2020
2046
  };
2021
2047
  }
2022
2048
  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
- }
2049
+ if (this.inTransaction) return fn(this);
2035
2050
  this.inTransaction = true;
2036
2051
  try {
2037
2052
  await this.execute('BEGIN');
@@ -2050,6 +2065,19 @@ var __webpack_exports__ = {};
2050
2065
  async begin(fn) {
2051
2066
  return this.transaction(fn);
2052
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
+ }
2053
2081
  async close() {}
2054
2082
  createORM() {
2055
2083
  const { createORM } = __webpack_require__("./src/orm/index.ts");
package/dist/index.js CHANGED
@@ -1606,6 +1606,19 @@ class BunSQLiteConnection {
1606
1606
  async begin(fn) {
1607
1607
  return this.transaction(fn);
1608
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
+ }
1609
1622
  async close() {
1610
1623
  this.db.close();
1611
1624
  }
@@ -1761,6 +1774,19 @@ class LibSQLConnection {
1761
1774
  async begin(fn) {
1762
1775
  return this.transaction(fn);
1763
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
+ }
1764
1790
  async close() {}
1765
1791
  createORM() {
1766
1792
  const { createORM } = __webpack_require__("./src/orm/index.ts");
@@ -1928,18 +1954,7 @@ class ODBLiteConnection {
1928
1954
  };
1929
1955
  }
1930
1956
  async transaction(fn) {
1931
- if (this.inTransaction) {
1932
- const savepointName = `sp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1933
- 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
- }
1957
+ if (this.inTransaction) return fn(this);
1943
1958
  this.inTransaction = true;
1944
1959
  try {
1945
1960
  await this.execute('BEGIN');
@@ -1958,6 +1973,19 @@ class ODBLiteConnection {
1958
1973
  async begin(fn) {
1959
1974
  return this.transaction(fn);
1960
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
+ }
1961
1989
  async close() {}
1962
1990
  createORM() {
1963
1991
  const { createORM } = __webpack_require__("./src/orm/index.ts");
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.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",
@@ -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
  */
@@ -301,17 +301,10 @@ class ODBLiteConnection implements Connection {
301
301
  */
302
302
  async transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T> {
303
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
- }
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.
307
+ return fn(this)
315
308
  }
316
309
 
317
310
  this.inTransaction = true
@@ -345,6 +338,27 @@ class ODBLiteConnection implements Connection {
345
338
  return this.transaction(fn)
346
339
  }
347
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
+
348
362
  /**
349
363
  * Close connection
350
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