sqlstack 3.6.0 → 3.7.0-dev.0

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.
@@ -2,5 +2,5 @@ export { createSqliteDb } from './sqlite';
2
2
  export { createPgDb } from './pg';
3
3
  export { createMysqlDb } from './mysql';
4
4
  export { createSqlegoDb } from './sqlego';
5
- export type { SqlegoClientLike, SqlegoTransactionLike, SqlegoDbOptions } from './sqlego';
5
+ export type { SqlegoClientLike, SqlegoTransactionLike, SqlegoTargetLike, SqlegoDbOptions } from './sqlego';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":";;;AAAA,mCAA0C;AAAjC,wGAAA,cAAc,OAAA;AACvB,2BAAkC;AAAzB,gGAAA,UAAU,OAAA;AACnB,iCAAwC;AAA/B,sGAAA,aAAa,OAAA;AACtB,mCAA0C;AAAjC,wGAAA,cAAc,OAAA","sourcesContent":["export { createSqliteDb } from './sqlite';\nexport { createPgDb } from './pg';\nexport { createMysqlDb } from './mysql';\nexport { createSqlegoDb } from './sqlego';\nexport type { SqlegoClientLike, SqlegoTransactionLike, SqlegoDbOptions } from './sqlego';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":";;;AAAA,mCAA0C;AAAjC,wGAAA,cAAc,OAAA;AACvB,2BAAkC;AAAzB,gGAAA,UAAU,OAAA;AACnB,iCAAwC;AAA/B,sGAAA,aAAa,OAAA;AACtB,mCAA0C;AAAjC,wGAAA,cAAc,OAAA","sourcesContent":["export { createSqliteDb } from './sqlite';\nexport { createPgDb } from './pg';\nexport { createMysqlDb } from './mysql';\nexport { createSqlegoDb } from './sqlego';\nexport type { SqlegoClientLike, SqlegoTransactionLike, SqlegoTargetLike, SqlegoDbOptions } from './sqlego';\n"]}
@@ -1,41 +1,81 @@
1
1
  import type { Database } from '../registry';
2
+ type Row = Record<string, unknown>;
3
+ type RunInfo = {
4
+ changes: number;
5
+ lastInsertRowid: number | bigint;
6
+ };
7
+ /** sqlego protocol 3 `statement`: rows and changes for one statement of any kind. */
8
+ type StatementInfo = {
9
+ reader: boolean;
10
+ rows: Row[];
11
+ changes: number;
12
+ lastInsertRowid: number | bigint;
13
+ };
14
+ /** sqlego protocol 3 `exec`: a whole script, no parameters. */
15
+ type ExecInfo = {
16
+ statements: number;
17
+ changes: number;
18
+ lastInsertRowid: number | bigint;
19
+ };
20
+ /** What both a client and a transaction expose. `statement`/`exec` arrive with protocol 3. */
21
+ export interface SqlegoTargetLike {
22
+ query(sql: string, params?: readonly unknown[]): Promise<Row[]>;
23
+ run(sql: string, params?: readonly unknown[]): Promise<RunInfo>;
24
+ statement?(sql: string, params?: readonly unknown[]): Promise<StatementInfo>;
25
+ exec?(sql: string): Promise<ExecInfo>;
26
+ }
2
27
  /**
3
28
  * The subset of `@noego/sqlego`'s `SqlegoClient` this adapter uses. Typed
4
29
  * structurally so sqlstack carries no hard dependency on sqlego.
5
30
  */
6
- export interface SqlegoClientLike {
7
- query(sql: string, params?: readonly unknown[]): Promise<Record<string, unknown>[]>;
8
- run(sql: string, params?: readonly unknown[]): Promise<{
9
- changes: number;
10
- lastInsertRowid: number | bigint;
11
- }>;
31
+ export interface SqlegoClientLike extends SqlegoTargetLike {
12
32
  begin(options?: {
13
33
  mode?: 'deferred' | 'immediate';
14
34
  leaseMs?: number;
15
35
  }): Promise<SqlegoTransactionLike>;
16
36
  close(): Promise<void> | void;
17
37
  }
18
- export interface SqlegoTransactionLike {
19
- query(sql: string, params?: readonly unknown[]): Promise<Record<string, unknown>[]>;
20
- run(sql: string, params?: readonly unknown[]): Promise<{
21
- changes: number;
22
- lastInsertRowid: number | bigint;
23
- }>;
38
+ export interface SqlegoTransactionLike extends SqlegoTargetLike {
24
39
  commit(): Promise<void>;
25
40
  rollback(): Promise<void>;
26
41
  }
27
42
  export interface SqlegoDbOptions {
28
- /** Lease requested for each transaction (the server caps it). */
43
+ /**
44
+ * Lease requested for each transaction: the server rolls it back when no
45
+ * statement arrives for this long. The server caps it at its own lease
46
+ * (sqlego default 10 000 ms), so it can only be shortened here.
47
+ */
29
48
  leaseMs?: number;
30
49
  }
31
50
  /**
32
- * SQLite through a sqlego owner process. Behaves like a single SQLite
33
- * connection: `BEGIN` opens a leased sqlego transaction and every statement
34
- * runs inside it until `COMMIT`/`ROLLBACK`, so sqlstack transactions,
35
- * savepoints, `rollback()` and `afterCommit` work unchanged.
51
+ * SQLite through a sqlego owner process.
52
+ *
53
+ * sqlego binds a transaction to the connection that opened it, and every
54
+ * statement that connection sends while it is open runs inside it. So each
55
+ * sqlstack transaction gets its own handle (`transactionHandle`): BEGIN opens
56
+ * a leased sqlego transaction there, its statements carry the transaction id,
57
+ * and COMMIT/ROLLBACK end it. Statements other flows send to the shared
58
+ * handle wait until it ends instead of joining it, and transactions from
59
+ * different flows take turns. Savepoints pass through as SQL, so
60
+ * `@transaction`, nested scopes, `rollback()` and `afterCommit` work as on
61
+ * any SQLite connection.
62
+ *
63
+ * Lease failures surface as TransactionExpiredError (`tx-expired`: the server
64
+ * rolled the transaction back — idle past the lease, past the hard cap, or
65
+ * the connection dropped) and TransactionBusyError (`tx-busy`: another
66
+ * process's transaction kept this request waiting past the server's queue
67
+ * timeout; nothing ran). A ROLLBACK of a transaction the server already
68
+ * rolled back succeeds.
69
+ *
70
+ * With a protocol-3 client (`@noego/sqlego` >= 0.0.3) statements run through
71
+ * `statement`, which tells rows from writes using SQLite's own column
72
+ * metadata and returns 64-bit integers as `bigint` and blobs as
73
+ * `Uint8Array`; parameterless multi-statement scripts run through `exec`.
74
+ * Older clients fall back to `query`/`run` chosen from the SQL text.
36
75
  *
37
76
  * Pass a connected `SqlegoClient`, or a database file path (requires the
38
77
  * optional `@noego/sqlego` package).
39
78
  */
40
79
  export declare function createSqlegoDb(fileOrClient: string | SqlegoClientLike, options?: SqlegoDbOptions): Promise<Database>;
80
+ export {};
41
81
  //# sourceMappingURL=sqlego.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlego.d.ts","sourceRoot":"","sources":["../../../src/adapters/sqlego.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AAGzD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACpF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACvG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IACpF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9G,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAeD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkD9H"}
1
+ {"version":3,"file":"sqlego.d.ts","sourceRoot":"","sources":["../../../src/adapters/sqlego.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAe,MAAM,aAAa,CAAC;AAIzD,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACnC,KAAK,OAAO,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AACrE,qFAAqF;AACrF,KAAK,aAAa,GAAG;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AACzG,+DAA+D;AAC/D,KAAK,QAAQ,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE1F,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAChE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,SAAS,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7E,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACvC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACvG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuI9H"}
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createSqlegoDb = createSqlegoDb;
37
+ const errors_1 = require("../core/errors");
37
38
  const sqlite_1 = require("./sqlite");
38
39
  // Top-level transaction control only; `ROLLBACK TO [SAVEPOINT] x`, SAVEPOINT
39
40
  // and RELEASE are ordinary SQL inside a sqlego transaction.
@@ -48,11 +49,71 @@ function transactionControl(sql) {
48
49
  return { kind: 'rollback' };
49
50
  return null;
50
51
  }
52
+ function errorCode(err) {
53
+ return err?.code;
54
+ }
55
+ /** sqlego's lease errors become typed sqlstack errors; anything else passes through. */
56
+ function mapError(err) {
57
+ const code = errorCode(err);
58
+ if (code !== 'tx-expired' && code !== 'tx-busy')
59
+ return err;
60
+ const message = err?.message;
61
+ const text = typeof message === 'string' && message ? message : String(err);
62
+ return code === 'tx-expired' ? new errors_1.TransactionExpiredError(text, err) : new errors_1.TransactionBusyError(text, err);
63
+ }
64
+ /** `statement` refuses text holding several statements, before running any of it. */
65
+ function isSeveralStatementsRefusal(err) {
66
+ const message = err?.message;
67
+ return errorCode(err) === 'bad-request' && typeof message === 'string' && /use exec for several/.test(message);
68
+ }
69
+ async function execute(target, sql, params) {
70
+ if (typeof target.statement === 'function') {
71
+ try {
72
+ const out = await target.statement(sql, params);
73
+ if (out.reader)
74
+ return out.rows;
75
+ return { rowsAffected: Number(out.changes ?? 0), lastInsertId: out.lastInsertRowid };
76
+ }
77
+ catch (err) {
78
+ // Parameterless multi-statement text (a schema or migration script)
79
+ // runs through `exec`, which splits it the way SQLite does.
80
+ if (params.length > 0 || typeof target.exec !== 'function' || !isSeveralStatementsRefusal(err))
81
+ throw err;
82
+ const out = await target.exec(sql);
83
+ return { rowsAffected: Number(out.changes ?? 0), lastInsertId: out.lastInsertRowid };
84
+ }
85
+ }
86
+ // Protocol-2 clients report no column metadata: decide from the SQL text.
87
+ if ((0, sqlite_1.producesRows)(sql))
88
+ return await target.query(sql, params);
89
+ const info = await target.run(sql, params);
90
+ return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
91
+ }
51
92
  /**
52
- * SQLite through a sqlego owner process. Behaves like a single SQLite
53
- * connection: `BEGIN` opens a leased sqlego transaction and every statement
54
- * runs inside it until `COMMIT`/`ROLLBACK`, so sqlstack transactions,
55
- * savepoints, `rollback()` and `afterCommit` work unchanged.
93
+ * SQLite through a sqlego owner process.
94
+ *
95
+ * sqlego binds a transaction to the connection that opened it, and every
96
+ * statement that connection sends while it is open runs inside it. So each
97
+ * sqlstack transaction gets its own handle (`transactionHandle`): BEGIN opens
98
+ * a leased sqlego transaction there, its statements carry the transaction id,
99
+ * and COMMIT/ROLLBACK end it. Statements other flows send to the shared
100
+ * handle wait until it ends instead of joining it, and transactions from
101
+ * different flows take turns. Savepoints pass through as SQL, so
102
+ * `@transaction`, nested scopes, `rollback()` and `afterCommit` work as on
103
+ * any SQLite connection.
104
+ *
105
+ * Lease failures surface as TransactionExpiredError (`tx-expired`: the server
106
+ * rolled the transaction back — idle past the lease, past the hard cap, or
107
+ * the connection dropped) and TransactionBusyError (`tx-busy`: another
108
+ * process's transaction kept this request waiting past the server's queue
109
+ * timeout; nothing ran). A ROLLBACK of a transaction the server already
110
+ * rolled back succeeds.
111
+ *
112
+ * With a protocol-3 client (`@noego/sqlego` >= 0.0.3) statements run through
113
+ * `statement`, which tells rows from writes using SQLite's own column
114
+ * metadata and returns 64-bit integers as `bigint` and blobs as
115
+ * `Uint8Array`; parameterless multi-statement scripts run through `exec`.
116
+ * Older clients fall back to `query`/`run` chosen from the SQL text.
56
117
  *
57
118
  * Pass a connected `SqlegoClient`, or a database file path (requires the
58
119
  * optional `@noego/sqlego` package).
@@ -76,37 +137,127 @@ async function createSqlegoDb(fileOrClient, options = {}) {
76
137
  else {
77
138
  client = fileOrClient;
78
139
  }
79
- let tx;
80
- async function execute(target, sql, params) {
81
- if ((0, sqlite_1.producesRows)(sql))
82
- return await target.query(sql, params);
83
- const info = await target.run(sql, params);
84
- return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
140
+ // Settles when the transaction open on this client ends; null while none is
141
+ // open. A sqlego connection holds at most one transaction at a time.
142
+ let gate = null;
143
+ // Callers re-check `gate` and act in the same synchronous step after the
144
+ // loop: an extra `await` in between would let another flow open a
145
+ // transaction first.
146
+ async function takeTurn() {
147
+ while (gate)
148
+ await gate;
149
+ let open;
150
+ gate = new Promise((resolve) => {
151
+ open = resolve;
152
+ });
153
+ let done = false;
154
+ return () => {
155
+ if (done)
156
+ return;
157
+ done = true;
158
+ gate = null;
159
+ open();
160
+ };
161
+ }
162
+ /**
163
+ * One connection-like handle: statements after its own BEGIN join that
164
+ * transaction; without one open, its statements wait out any other
165
+ * handle's transaction.
166
+ */
167
+ function handle() {
168
+ let tx;
169
+ let endTurn;
170
+ let failedCommit = false;
171
+ const finish = () => {
172
+ tx = undefined;
173
+ const end = endTurn;
174
+ endTurn = undefined;
175
+ end?.();
176
+ };
177
+ return {
178
+ dialect: 'sqlite',
179
+ conn: client,
180
+ close() { },
181
+ async query(sql, params) {
182
+ const control = transactionControl(sql);
183
+ if (control?.kind === 'begin') {
184
+ if (tx)
185
+ throw new Error('cannot start a transaction within a transaction');
186
+ const end = await takeTurn();
187
+ try {
188
+ tx = await client.begin({ mode: control.mode, leaseMs: options.leaseMs });
189
+ }
190
+ catch (err) {
191
+ end();
192
+ throw mapError(err);
193
+ }
194
+ endTurn = end;
195
+ return { rowsAffected: 0 };
196
+ }
197
+ if (control) {
198
+ const open = tx;
199
+ if (!open) {
200
+ // A failed COMMIT already ended the transaction on the server
201
+ // (sqlego rolls it back); the caller's follow-up ROLLBACK is moot.
202
+ if (control.kind === 'rollback' && failedCommit) {
203
+ failedCommit = false;
204
+ return { rowsAffected: 0 };
205
+ }
206
+ throw new Error(`cannot ${control.kind} - no transaction is active`);
207
+ }
208
+ failedCommit = false;
209
+ try {
210
+ if (control.kind === 'commit') {
211
+ try {
212
+ await open.commit();
213
+ }
214
+ catch (err) {
215
+ failedCommit = true;
216
+ throw err;
217
+ }
218
+ }
219
+ else {
220
+ try {
221
+ await open.rollback();
222
+ }
223
+ catch (err) {
224
+ // Already rolled back by the server (lease, cap, abort).
225
+ const code = errorCode(err);
226
+ if (code !== 'tx-expired' && code !== 'tx-invalid')
227
+ throw err;
228
+ }
229
+ }
230
+ }
231
+ catch (err) {
232
+ throw mapError(err);
233
+ }
234
+ finally {
235
+ finish();
236
+ }
237
+ return { rowsAffected: 0 };
238
+ }
239
+ try {
240
+ if (tx)
241
+ return await execute(tx, sql, params);
242
+ // Wait out another flow's transaction, then send at once: requests
243
+ // on one sqlego connection are served in order, so this statement
244
+ // reaches the server before any BEGIN queued after it.
245
+ while (gate)
246
+ await gate;
247
+ return await execute(client, sql, params);
248
+ }
249
+ catch (err) {
250
+ throw mapError(err);
251
+ }
252
+ },
253
+ };
85
254
  }
255
+ const shared = handle();
86
256
  return {
87
257
  dialect: 'sqlite',
88
- async query(sql, params) {
89
- const control = transactionControl(sql);
90
- if (control?.kind === 'begin') {
91
- if (tx)
92
- throw new Error('cannot start a transaction within a transaction');
93
- tx = await client.begin({ mode: control.mode, leaseMs: options.leaseMs });
94
- return { rowsAffected: 0 };
95
- }
96
- if (control) {
97
- const open = tx;
98
- if (!open)
99
- throw new Error(`cannot ${control.kind} - no transaction is active`);
100
- tx = undefined;
101
- if (control.kind === 'commit')
102
- await open.commit();
103
- else
104
- await open.rollback();
105
- return { rowsAffected: 0 };
106
- }
107
- return await execute(tx ?? client, sql, params);
108
- },
109
258
  conn: client,
259
+ query: shared.query,
260
+ transactionHandle: () => handle(),
110
261
  async close() {
111
262
  if (owned)
112
263
  await client.close();
@@ -1 +1 @@
1
- {"version":3,"file":"sqlego.js","sourceRoot":"","sources":["../../../src/adapters/sqlego.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database, QueryResult } from '../registry';\nimport { producesRows, stripLeadingComments } from './sqlite';\n\n/**\n * The subset of `@noego/sqlego`'s `SqlegoClient` this adapter uses. Typed\n * structurally so sqlstack carries no hard dependency on sqlego.\n */\nexport interface SqlegoClientLike {\n query(sql: string, params?: readonly unknown[]): Promise<Record<string, unknown>[]>;\n run(sql: string, params?: readonly unknown[]): Promise<{ changes: number; lastInsertRowid: number | bigint }>;\n begin(options?: { mode?: 'deferred' | 'immediate'; leaseMs?: number }): Promise<SqlegoTransactionLike>;\n close(): Promise<void> | void;\n}\n\nexport interface SqlegoTransactionLike {\n query(sql: string, params?: readonly unknown[]): Promise<Record<string, unknown>[]>;\n run(sql: string, params?: readonly unknown[]): Promise<{ changes: number; lastInsertRowid: number | bigint }>;\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface SqlegoDbOptions {\n /** Lease requested for each transaction (the server caps it). */\n leaseMs?: number;\n}\n\ntype Control = { kind: 'begin'; mode: 'deferred' | 'immediate' } | { kind: 'commit' } | { kind: 'rollback' };\n\n// Top-level transaction control only; `ROLLBACK TO [SAVEPOINT] x`, SAVEPOINT\n// and RELEASE are ordinary SQL inside a sqlego transaction.\nfunction transactionControl(sql: string): Control | null {\n const text = stripLeadingComments(String(sql)).trim().replace(/;\\s*$/, '').trim().toUpperCase().replace(/\\s+/g, ' ');\n const begin = /^BEGIN(?: (DEFERRED|IMMEDIATE|EXCLUSIVE))?(?: TRANSACTION)?$/.exec(text);\n if (begin) return { kind: 'begin', mode: begin[1] === undefined || begin[1] === 'DEFERRED' ? 'deferred' : 'immediate' };\n if (/^(?:COMMIT|END)(?: TRANSACTION)?$/.test(text)) return { kind: 'commit' };\n if (/^ROLLBACK(?: TRANSACTION)?$/.test(text)) return { kind: 'rollback' };\n return null;\n}\n\n/**\n * SQLite through a sqlego owner process. Behaves like a single SQLite\n * connection: `BEGIN` opens a leased sqlego transaction and every statement\n * runs inside it until `COMMIT`/`ROLLBACK`, so sqlstack transactions,\n * savepoints, `rollback()` and `afterCommit` work unchanged.\n *\n * Pass a connected `SqlegoClient`, or a database file path (requires the\n * optional `@noego/sqlego` package).\n */\nexport async function createSqlegoDb(fileOrClient: string | SqlegoClientLike, options: SqlegoDbOptions = {}): Promise<Database> {\n let client: SqlegoClientLike;\n let owned = false;\n if (typeof fileOrClient === 'string') {\n let mod: any;\n try {\n const packageName = '@noego/sqlego';\n mod = await import(/* @vite-ignore */ packageName);\n } catch {\n throw new Error('createSqlegoDb(file) requires the @noego/sqlego package. Install it or pass a connected SqlegoClient.');\n }\n const SqlegoClient = mod.SqlegoClient ?? mod.default?.SqlegoClient;\n client = await SqlegoClient.connect(fileOrClient);\n owned = true;\n } else {\n client = fileOrClient;\n }\n\n let tx: SqlegoTransactionLike | undefined;\n\n async function execute(target: SqlegoClientLike | SqlegoTransactionLike, sql: string, params: unknown[]): Promise<QueryResult> {\n if (producesRows(sql)) return await target.query(sql, params);\n const info = await target.run(sql, params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n }\n\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const control = transactionControl(sql);\n if (control?.kind === 'begin') {\n if (tx) throw new Error('cannot start a transaction within a transaction');\n tx = await client.begin({ mode: control.mode, leaseMs: options.leaseMs });\n return { rowsAffected: 0 };\n }\n if (control) {\n const open = tx;\n if (!open) throw new Error(`cannot ${control.kind} - no transaction is active`);\n tx = undefined;\n if (control.kind === 'commit') await open.commit();\n else await open.rollback();\n return { rowsAffected: 0 };\n }\n return await execute(tx ?? client, sql, params);\n },\n conn: client,\n async close() {\n if (owned) await client.close();\n },\n } as Database;\n}\n"]}
1
+ {"version":3,"file":"sqlego.js","sourceRoot":"","sources":["../../../src/adapters/sqlego.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database, QueryResult } from '../registry';\nimport { TransactionBusyError, TransactionExpiredError } from '../core/errors';\nimport { producesRows, stripLeadingComments } from './sqlite';\n\ntype Row = Record<string, unknown>;\ntype RunInfo = { changes: number; lastInsertRowid: number | bigint };\n/** sqlego protocol 3 `statement`: rows and changes for one statement of any kind. */\ntype StatementInfo = { reader: boolean; rows: Row[]; changes: number; lastInsertRowid: number | bigint };\n/** sqlego protocol 3 `exec`: a whole script, no parameters. */\ntype ExecInfo = { statements: number; changes: number; lastInsertRowid: number | bigint };\n\n/** What both a client and a transaction expose. `statement`/`exec` arrive with protocol 3. */\nexport interface SqlegoTargetLike {\n query(sql: string, params?: readonly unknown[]): Promise<Row[]>;\n run(sql: string, params?: readonly unknown[]): Promise<RunInfo>;\n statement?(sql: string, params?: readonly unknown[]): Promise<StatementInfo>;\n exec?(sql: string): Promise<ExecInfo>;\n}\n\n/**\n * The subset of `@noego/sqlego`'s `SqlegoClient` this adapter uses. Typed\n * structurally so sqlstack carries no hard dependency on sqlego.\n */\nexport interface SqlegoClientLike extends SqlegoTargetLike {\n begin(options?: { mode?: 'deferred' | 'immediate'; leaseMs?: number }): Promise<SqlegoTransactionLike>;\n close(): Promise<void> | void;\n}\n\nexport interface SqlegoTransactionLike extends SqlegoTargetLike {\n commit(): Promise<void>;\n rollback(): Promise<void>;\n}\n\nexport interface SqlegoDbOptions {\n /**\n * Lease requested for each transaction: the server rolls it back when no\n * statement arrives for this long. The server caps it at its own lease\n * (sqlego default 10 000 ms), so it can only be shortened here.\n */\n leaseMs?: number;\n}\n\ntype Control = { kind: 'begin'; mode: 'deferred' | 'immediate' } | { kind: 'commit' } | { kind: 'rollback' };\n\n// Top-level transaction control only; `ROLLBACK TO [SAVEPOINT] x`, SAVEPOINT\n// and RELEASE are ordinary SQL inside a sqlego transaction.\nfunction transactionControl(sql: string): Control | null {\n const text = stripLeadingComments(String(sql)).trim().replace(/;\\s*$/, '').trim().toUpperCase().replace(/\\s+/g, ' ');\n const begin = /^BEGIN(?: (DEFERRED|IMMEDIATE|EXCLUSIVE))?(?: TRANSACTION)?$/.exec(text);\n if (begin) return { kind: 'begin', mode: begin[1] === undefined || begin[1] === 'DEFERRED' ? 'deferred' : 'immediate' };\n if (/^(?:COMMIT|END)(?: TRANSACTION)?$/.test(text)) return { kind: 'commit' };\n if (/^ROLLBACK(?: TRANSACTION)?$/.test(text)) return { kind: 'rollback' };\n return null;\n}\n\nfunction errorCode(err: unknown): unknown {\n return (err as { code?: unknown } | null)?.code;\n}\n\n/** sqlego's lease errors become typed sqlstack errors; anything else passes through. */\nfunction mapError(err: unknown): unknown {\n const code = errorCode(err);\n if (code !== 'tx-expired' && code !== 'tx-busy') return err;\n const message = (err as { message?: unknown } | null)?.message;\n const text = typeof message === 'string' && message ? message : String(err);\n return code === 'tx-expired' ? new TransactionExpiredError(text, err) : new TransactionBusyError(text, err);\n}\n\n/** `statement` refuses text holding several statements, before running any of it. */\nfunction isSeveralStatementsRefusal(err: unknown): boolean {\n const message = (err as { message?: unknown } | null)?.message;\n return errorCode(err) === 'bad-request' && typeof message === 'string' && /use exec for several/.test(message);\n}\n\nasync function execute(target: SqlegoTargetLike, sql: string, params: unknown[]): Promise<QueryResult> {\n if (typeof target.statement === 'function') {\n try {\n const out = await target.statement(sql, params);\n if (out.reader) return out.rows;\n return { rowsAffected: Number(out.changes ?? 0), lastInsertId: out.lastInsertRowid };\n } catch (err) {\n // Parameterless multi-statement text (a schema or migration script)\n // runs through `exec`, which splits it the way SQLite does.\n if (params.length > 0 || typeof target.exec !== 'function' || !isSeveralStatementsRefusal(err)) throw err;\n const out = await target.exec(sql);\n return { rowsAffected: Number(out.changes ?? 0), lastInsertId: out.lastInsertRowid };\n }\n }\n // Protocol-2 clients report no column metadata: decide from the SQL text.\n if (producesRows(sql)) return await target.query(sql, params);\n const info = await target.run(sql, params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n}\n\n/**\n * SQLite through a sqlego owner process.\n *\n * sqlego binds a transaction to the connection that opened it, and every\n * statement that connection sends while it is open runs inside it. So each\n * sqlstack transaction gets its own handle (`transactionHandle`): BEGIN opens\n * a leased sqlego transaction there, its statements carry the transaction id,\n * and COMMIT/ROLLBACK end it. Statements other flows send to the shared\n * handle wait until it ends instead of joining it, and transactions from\n * different flows take turns. Savepoints pass through as SQL, so\n * `@transaction`, nested scopes, `rollback()` and `afterCommit` work as on\n * any SQLite connection.\n *\n * Lease failures surface as TransactionExpiredError (`tx-expired`: the server\n * rolled the transaction back — idle past the lease, past the hard cap, or\n * the connection dropped) and TransactionBusyError (`tx-busy`: another\n * process's transaction kept this request waiting past the server's queue\n * timeout; nothing ran). A ROLLBACK of a transaction the server already\n * rolled back succeeds.\n *\n * With a protocol-3 client (`@noego/sqlego` >= 0.0.3) statements run through\n * `statement`, which tells rows from writes using SQLite's own column\n * metadata and returns 64-bit integers as `bigint` and blobs as\n * `Uint8Array`; parameterless multi-statement scripts run through `exec`.\n * Older clients fall back to `query`/`run` chosen from the SQL text.\n *\n * Pass a connected `SqlegoClient`, or a database file path (requires the\n * optional `@noego/sqlego` package).\n */\nexport async function createSqlegoDb(fileOrClient: string | SqlegoClientLike, options: SqlegoDbOptions = {}): Promise<Database> {\n let client: SqlegoClientLike;\n let owned = false;\n if (typeof fileOrClient === 'string') {\n let mod: any;\n try {\n const packageName = '@noego/sqlego';\n mod = await import(/* @vite-ignore */ packageName);\n } catch {\n throw new Error('createSqlegoDb(file) requires the @noego/sqlego package. Install it or pass a connected SqlegoClient.');\n }\n const SqlegoClient = mod.SqlegoClient ?? mod.default?.SqlegoClient;\n client = await SqlegoClient.connect(fileOrClient);\n owned = true;\n } else {\n client = fileOrClient;\n }\n\n // Settles when the transaction open on this client ends; null while none is\n // open. A sqlego connection holds at most one transaction at a time.\n let gate: Promise<void> | null = null;\n\n // Callers re-check `gate` and act in the same synchronous step after the\n // loop: an extra `await` in between would let another flow open a\n // transaction first.\n async function takeTurn(): Promise<() => void> {\n while (gate) await gate;\n let open!: () => void;\n gate = new Promise<void>((resolve) => {\n open = resolve;\n });\n let done = false;\n return () => {\n if (done) return;\n done = true;\n gate = null;\n open();\n };\n }\n\n /**\n * One connection-like handle: statements after its own BEGIN join that\n * transaction; without one open, its statements wait out any other\n * handle's transaction.\n */\n function handle(): Database {\n let tx: SqlegoTransactionLike | undefined;\n let endTurn: (() => void) | undefined;\n let failedCommit = false;\n\n const finish = () => {\n tx = undefined;\n const end = endTurn;\n endTurn = undefined;\n end?.();\n };\n\n return {\n dialect: 'sqlite',\n conn: client,\n close() {},\n async query(sql: string, params: unknown[]) {\n const control = transactionControl(sql);\n if (control?.kind === 'begin') {\n if (tx) throw new Error('cannot start a transaction within a transaction');\n const end = await takeTurn();\n try {\n tx = await client.begin({ mode: control.mode, leaseMs: options.leaseMs });\n } catch (err) {\n end();\n throw mapError(err);\n }\n endTurn = end;\n return { rowsAffected: 0 };\n }\n if (control) {\n const open = tx;\n if (!open) {\n // A failed COMMIT already ended the transaction on the server\n // (sqlego rolls it back); the caller's follow-up ROLLBACK is moot.\n if (control.kind === 'rollback' && failedCommit) {\n failedCommit = false;\n return { rowsAffected: 0 };\n }\n throw new Error(`cannot ${control.kind} - no transaction is active`);\n }\n failedCommit = false;\n try {\n if (control.kind === 'commit') {\n try {\n await open.commit();\n } catch (err) {\n failedCommit = true;\n throw err;\n }\n } else {\n try {\n await open.rollback();\n } catch (err) {\n // Already rolled back by the server (lease, cap, abort).\n const code = errorCode(err);\n if (code !== 'tx-expired' && code !== 'tx-invalid') throw err;\n }\n }\n } catch (err) {\n throw mapError(err);\n } finally {\n finish();\n }\n return { rowsAffected: 0 };\n }\n try {\n if (tx) return await execute(tx, sql, params);\n // Wait out another flow's transaction, then send at once: requests\n // on one sqlego connection are served in order, so this statement\n // reaches the server before any BEGIN queued after it.\n while (gate) await gate;\n return await execute(client, sql, params);\n } catch (err) {\n throw mapError(err);\n }\n },\n } as Database;\n }\n\n const shared = handle();\n return {\n dialect: 'sqlite',\n conn: client,\n query: shared.query,\n transactionHandle: () => handle(),\n async close() {\n if (owned) await client.close();\n },\n } as Database;\n}\n"]}
@@ -33,6 +33,28 @@ export declare class DatabaseError extends SqlStackError {
33
33
  /** Driver error code copied from `cause` when it is a string. */
34
34
  code?: string;
35
35
  }
36
+ /**
37
+ * The database ended the transaction on its own and rolled it back: a sqlego
38
+ * lease ran out (no statement within the lease), the transaction hit the
39
+ * server's hard cap on its duration, or the client connection dropped.
40
+ * `code` is `'tx-expired'`. Statements fail with it (wrapped in
41
+ * DatabaseError, which copies the code); COMMIT fails with it directly.
42
+ */
43
+ export declare class TransactionExpiredError extends SqlStackError {
44
+ constructor(message: string, cause?: unknown);
45
+ readonly code = "tx-expired";
46
+ cause?: unknown;
47
+ }
48
+ /**
49
+ * Another process held the database in a transaction for longer than the
50
+ * server lets a request wait (sqlego's queue timeout). Nothing ran; the
51
+ * statement or BEGIN can be retried. `code` is `'tx-busy'`.
52
+ */
53
+ export declare class TransactionBusyError extends SqlStackError {
54
+ constructor(message: string, cause?: unknown);
55
+ readonly code = "tx-busy";
56
+ cause?: unknown;
57
+ }
36
58
  export declare class RollbackTransactionError extends SqlStackError {
37
59
  constructor(message?: string, cause?: unknown);
38
60
  cause?: unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,qBAAa,kBAAmB,SAAQ,aAAa;gBACvC,WAAW,EAAE,MAAM;CAIhC;AAED,qBAAa,WAAY,SAAQ,aAAa;;CAK7C;AAED,qBAAa,cAAe,SAAQ,aAAa;gBACnC,WAAW,EAAE,MAAM;CAIhC;AAKD,qBAAa,iBAAkB,SAAQ,aAAa;gBACtC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAUzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAgB,SAAQ,aAAa;gBACpC,OAAO,EAAE,MAAM;IAK3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAQvD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID,qBAAa,wBAAyB,SAAQ,aAAa;gBAC7C,OAAO,SAA4B,EAAE,KAAK,CAAC,EAAE,OAAO;IAKhE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,CAAC,EAAE,MAAM;CAI7B;AAED,qBAAa,kBAAmB,SAAQ,aAAa;gBACvC,WAAW,EAAE,MAAM;CAIhC;AAED,qBAAa,WAAY,SAAQ,aAAa;;CAK7C;AAED,qBAAa,cAAe,SAAQ,aAAa;gBACnC,WAAW,EAAE,MAAM;CAIhC;AAKD,qBAAa,iBAAkB,SAAQ,aAAa;gBACtC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAUzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAgB,SAAQ,aAAa;gBACpC,OAAO,EAAE,MAAM;IAK3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAQvD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,SAAQ,aAAa;gBAC5C,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;IAK5C,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,aAAa;gBACzC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;IAK5C,QAAQ,CAAC,IAAI,aAAa;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,qBAAa,wBAAyB,SAAQ,aAAa;gBAC7C,OAAO,SAA4B,EAAE,KAAK,CAAC,EAAE,OAAO;IAKhE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RollbackTransactionError = exports.DatabaseError = exports.ValidationError = exports.ExpectedRowsError = exports.RowsExistError = exports.NoRowsError = exports.ExactlyOneRowError = exports.SqlStackError = void 0;
3
+ exports.RollbackTransactionError = exports.TransactionBusyError = exports.TransactionExpiredError = exports.DatabaseError = exports.ValidationError = exports.ExpectedRowsError = exports.RowsExistError = exports.NoRowsError = exports.ExactlyOneRowError = exports.SqlStackError = void 0;
4
4
  class SqlStackError extends Error {
5
5
  constructor(message) {
6
6
  super(message);
@@ -70,6 +70,36 @@ class DatabaseError extends SqlStackError {
70
70
  }
71
71
  }
72
72
  exports.DatabaseError = DatabaseError;
73
+ /**
74
+ * The database ended the transaction on its own and rolled it back: a sqlego
75
+ * lease ran out (no statement within the lease), the transaction hit the
76
+ * server's hard cap on its duration, or the client connection dropped.
77
+ * `code` is `'tx-expired'`. Statements fail with it (wrapped in
78
+ * DatabaseError, which copies the code); COMMIT fails with it directly.
79
+ */
80
+ class TransactionExpiredError extends SqlStackError {
81
+ constructor(message, cause) {
82
+ super(message);
83
+ this.code = 'tx-expired';
84
+ this.cause = cause;
85
+ this.name = 'TransactionExpiredError';
86
+ }
87
+ }
88
+ exports.TransactionExpiredError = TransactionExpiredError;
89
+ /**
90
+ * Another process held the database in a transaction for longer than the
91
+ * server lets a request wait (sqlego's queue timeout). Nothing ran; the
92
+ * statement or BEGIN can be retried. `code` is `'tx-busy'`.
93
+ */
94
+ class TransactionBusyError extends SqlStackError {
95
+ constructor(message, cause) {
96
+ super(message);
97
+ this.code = 'tx-busy';
98
+ this.cause = cause;
99
+ this.name = 'TransactionBusyError';
100
+ }
101
+ }
102
+ exports.TransactionBusyError = TransactionBusyError;
73
103
  // Signals that the current transaction should be rolled back.
74
104
  // If `cause` is provided, wrappers may rethrow it after rollback.
75
105
  class RollbackTransactionError extends SqlStackError {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAgB;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AALD,sCAKC;AAED,MAAa,kBAAmB,SAAQ,aAAa;IACnD,YAAY,WAAmB;QAC7B,KAAK,CAAC,iCAAiC,WAAW,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC;AAED,MAAa,WAAY,SAAQ,aAAa;IAC5C;QACE,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AALD,kCAKC;AAED,MAAa,cAAe,SAAQ,aAAa;IAC/C,YAAY,WAAmB;QAC7B,KAAK,CAAC,yBAAyB,WAAW,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AALD,wCAKC;AAED,yEAAyE;AACzE,wEAAwE;AACxE,2DAA2D;AAC3D,MAAa,iBAAkB,SAAQ,aAAa;IAClD,YAAY,MAAc,EAAE,KAAa;QACvC,MAAM,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACvE,KAAK,CACH,GAAG,SAAS,OAAO,MAAM,oEAAoE;YAC3F,2EAA2E,CAC9E,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CAGF;AAbD,8CAaC;AAED,MAAa,eAAgB,SAAQ,aAAa;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CAEF;AAPD,0CAOC;AAED;;;;;;GAMG;AACH,MAAa,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,GAAW,EAAE,KAAa;QACrD,KAAK,CAAC,GAAG,OAAO,UAAU,GAAG,EAAE,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,MAAM,IAAI,GAAI,KAAwC,EAAE,IAAI,CAAC;QAC7D,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAC/C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CAKF;AAbD,sCAaC;AAED,8DAA8D;AAC9D,kEAAkE;AAClE,MAAa,wBAAyB,SAAQ,aAAa;IACzD,YAAY,OAAO,GAAG,yBAAyB,EAAE,KAAe;QAC9D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CAEF;AAPD,4DAOC","sourcesContent":["export class SqlStackError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = 'SqlStackError';\n }\n}\n\nexport class ExactlyOneRowError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected exactly one row, got ${actualCount}`);\n this.name = 'ExactlyOneRowError';\n }\n}\n\nexport class NoRowsError extends SqlStackError {\n constructor() {\n super('Expected at least one row, got 0');\n this.name = 'NoRowsError';\n }\n}\n\nexport class RowsExistError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected no rows, got ${actualCount}`);\n this.name = 'RowsExistError';\n }\n}\n\n// A result-shape decorator (@Single/@Only/@Exist/@None) was applied to a\n// method whose statement produced no result set (a plain write). Shapes\n// require rows; use `... RETURNING` or drop the decorator.\nexport class ExpectedRowsError extends SqlStackError {\n constructor(method: string, shape: string) {\n const decorator = '@' + shape.charAt(0).toUpperCase() + shape.slice(1);\n super(\n `${decorator} on ${method} requires a result set, but the statement produced a write result ` +\n `({ rowsAffected }). Add a RETURNING clause or remove the shape decorator.`\n );\n this.method = method;\n this.shape = shape;\n this.name = 'ExpectedRowsError';\n }\n method: string;\n shape: string;\n}\n\nexport class ValidationError extends SqlStackError {\n constructor(details: string) {\n super(`Validation failed: ${details}`);\n this.details = details;\n this.name = 'ValidationError';\n }\n details: string;\n}\n\n/**\n * A statement failed: the driver rejected it, or (for the `run(...)` forms\n * that take arguments) the SQL could not be compiled, bound, or routed to a\n * database. The original error is kept in `.cause`; when the cause carries a\n * string driver code (`SQLITE_CONSTRAINT_UNIQUE`, `23505`, `ER_DUP_ENTRY`)\n * it is copied to `.code` so existing `err.code` checks keep working.\n */\nexport class DatabaseError extends SqlStackError {\n constructor(message: string, sql: string, cause?: Error) {\n super(`${message}\\nSQL: ${sql}`);\n this.sql = sql;\n this.cause = cause;\n const code = (cause as { code?: unknown } | undefined)?.code;\n if (typeof code === 'string') this.code = code;\n this.name = 'DatabaseError';\n }\n sql: string;\n cause?: Error;\n /** Driver error code copied from `cause` when it is a string. */\n code?: string;\n}\n\n// Signals that the current transaction should be rolled back.\n// If `cause` is provided, wrappers may rethrow it after rollback.\nexport class RollbackTransactionError extends SqlStackError {\n constructor(message = 'Transaction rolled back', cause?: unknown) {\n super(message);\n this.cause = cause;\n this.name = 'RollbackTransactionError';\n }\n cause?: unknown;\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"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","sourcesContent":["export class SqlStackError extends Error {\n constructor(message?: string) {\n super(message);\n this.name = 'SqlStackError';\n }\n}\n\nexport class ExactlyOneRowError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected exactly one row, got ${actualCount}`);\n this.name = 'ExactlyOneRowError';\n }\n}\n\nexport class NoRowsError extends SqlStackError {\n constructor() {\n super('Expected at least one row, got 0');\n this.name = 'NoRowsError';\n }\n}\n\nexport class RowsExistError extends SqlStackError {\n constructor(actualCount: number) {\n super(`Expected no rows, got ${actualCount}`);\n this.name = 'RowsExistError';\n }\n}\n\n// A result-shape decorator (@Single/@Only/@Exist/@None) was applied to a\n// method whose statement produced no result set (a plain write). Shapes\n// require rows; use `... RETURNING` or drop the decorator.\nexport class ExpectedRowsError extends SqlStackError {\n constructor(method: string, shape: string) {\n const decorator = '@' + shape.charAt(0).toUpperCase() + shape.slice(1);\n super(\n `${decorator} on ${method} requires a result set, but the statement produced a write result ` +\n `({ rowsAffected }). Add a RETURNING clause or remove the shape decorator.`\n );\n this.method = method;\n this.shape = shape;\n this.name = 'ExpectedRowsError';\n }\n method: string;\n shape: string;\n}\n\nexport class ValidationError extends SqlStackError {\n constructor(details: string) {\n super(`Validation failed: ${details}`);\n this.details = details;\n this.name = 'ValidationError';\n }\n details: string;\n}\n\n/**\n * A statement failed: the driver rejected it, or (for the `run(...)` forms\n * that take arguments) the SQL could not be compiled, bound, or routed to a\n * database. The original error is kept in `.cause`; when the cause carries a\n * string driver code (`SQLITE_CONSTRAINT_UNIQUE`, `23505`, `ER_DUP_ENTRY`)\n * it is copied to `.code` so existing `err.code` checks keep working.\n */\nexport class DatabaseError extends SqlStackError {\n constructor(message: string, sql: string, cause?: Error) {\n super(`${message}\\nSQL: ${sql}`);\n this.sql = sql;\n this.cause = cause;\n const code = (cause as { code?: unknown } | undefined)?.code;\n if (typeof code === 'string') this.code = code;\n this.name = 'DatabaseError';\n }\n sql: string;\n cause?: Error;\n /** Driver error code copied from `cause` when it is a string. */\n code?: string;\n}\n\n/**\n * The database ended the transaction on its own and rolled it back: a sqlego\n * lease ran out (no statement within the lease), the transaction hit the\n * server's hard cap on its duration, or the client connection dropped.\n * `code` is `'tx-expired'`. Statements fail with it (wrapped in\n * DatabaseError, which copies the code); COMMIT fails with it directly.\n */\nexport class TransactionExpiredError extends SqlStackError {\n constructor(message: string, cause?: unknown) {\n super(message);\n this.cause = cause;\n this.name = 'TransactionExpiredError';\n }\n readonly code = 'tx-expired';\n cause?: unknown;\n}\n\n/**\n * Another process held the database in a transaction for longer than the\n * server lets a request wait (sqlego's queue timeout). Nothing ran; the\n * statement or BEGIN can be retried. `code` is `'tx-busy'`.\n */\nexport class TransactionBusyError extends SqlStackError {\n constructor(message: string, cause?: unknown) {\n super(message);\n this.cause = cause;\n this.name = 'TransactionBusyError';\n }\n readonly code = 'tx-busy';\n cause?: unknown;\n}\n\n// Signals that the current transaction should be rolled back.\n// If `cause` is provided, wrappers may rethrow it after rollback.\nexport class RollbackTransactionError extends SqlStackError {\n constructor(message = 'Transaction rolled back', cause?: unknown) {\n super(message);\n this.cause = cause;\n this.name = 'RollbackTransactionError';\n }\n cause?: unknown;\n}\n"]}
@@ -11,6 +11,14 @@ export interface Database {
11
11
  query(sql: string, params: unknown[]): Promise<QueryResult>;
12
12
  conn: unknown;
13
13
  close: () => Promise<void> | void;
14
+ /**
15
+ * Optional, SQLite dialect only: a fresh handle for one transaction. When
16
+ * present, a transaction runs `BEGIN … COMMIT` on this handle instead of on
17
+ * the shared `Database`, so statements other flows send to the shared
18
+ * handle stay outside it (the role a pooled connection plays for
19
+ * Postgres/MySQL). Called once per transaction.
20
+ */
21
+ transactionHandle?: () => Database;
14
22
  }
15
23
  declare class Registry {
16
24
  private map;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAMtD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wFAAwF;AACxF,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,GAAG,EAAE,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE5D,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACnC;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ;IAKnC,UAAU,CAAC,IAAI,EAAE,MAAM;IAMvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,QAAQ,CAAA;KAAE;IAQvD,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ;CAG7B;AAQD,eAAO,MAAM,UAAU,EAAE,QAAmC,CAAC;AAE7D,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAMtD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wFAAwF;AACxF,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG,GAAG,EAAE,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE5D,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,QAAQ,CAAC;CACpC;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ;IAKnC,UAAU,CAAC,IAAI,EAAE,MAAM;IAMvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,QAAQ,CAAA;KAAE;IAQvD,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ;CAG7B;AAQD,eAAO,MAAM,UAAU,EAAE,QAAmC,CAAC;AAE7D,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":";;;AAwBA,MAAM,QAAQ;IAAd;QACU,QAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IAyB5C,CAAC;IAtBC,QAAQ,CAAC,IAAY,EAAE,EAAY;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,IAAa;QACpB,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,IAAa;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAChC,CAAC;CACF;AAED,8DAA8D;AAC9D,MAAM,CAAC,GAAG,UAAiB,CAAC;AAC5B,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC;IAC9B,CAAC,CAAC,sBAAsB,GAAG,IAAI,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAEY,QAAA,UAAU,GAAa,CAAC,CAAC,sBAAsB,CAAC","sourcesContent":["export type Dialect = 'sqlite' | 'mysql' | 'postgres';\n\n// Result for statements that produce no result set (INSERT/UPDATE/DELETE\n// without RETURNING, DDL). Statements that DO produce a result set — SELECT,\n// CTEs, and `... RETURNING` — return rows (`any[]`) instead; the affected\n// count is `rows.length`.\nexport interface WriteResult {\n rowsAffected: number;\n lastInsertId?: unknown;\n}\n\n/** @deprecated RETURNING now yields rows directly; this is identical to WriteResult. */\nexport type PostgresWriteResult = WriteResult;\n\nexport type QueryResult = any[] | WriteResult;\n\nexport interface Database {\n dialect: Dialect;\n query(sql: string, params: unknown[]): Promise<QueryResult>;\n // Underlying driver connection or pool (driver-specific)\n conn: unknown;\n close: () => Promise<void> | void;\n}\n\nclass Registry {\n private map = new Map<string, Database>();\n private defaultName?: string;\n\n register(name: string, db: Database) {\n this.map.set(name, db);\n return this;\n }\n\n setDefault(name: string) {\n if (!this.map.has(name)) throw new Error(`No database registered under ${name}`);\n this.defaultName = name;\n return this;\n }\n\n getEntry(name?: string): { name: string; db: Database } {\n const key = name ?? this.defaultName;\n if (!key) throw new Error('No default database set');\n const db = this.map.get(key);\n if (!db) throw new Error(`No database registered under ${key}`);\n return { name: key, db };\n }\n\n get(name?: string): Database {\n return this.getEntry(name).db;\n }\n}\n\n// Ensure a single shared instance across module copies/usages\nconst g = globalThis as any;\nif (!g.__SQLSTACK_SINGLETON__) {\n g.__SQLSTACK_SINGLETON__ = new Registry();\n}\n\nexport const SqlStackDB: Registry = g.__SQLSTACK_SINGLETON__;\n\nexport type IStackDB = typeof SqlStackDB;\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":";;;AAgCA,MAAM,QAAQ;IAAd;QACU,QAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IAyB5C,CAAC;IAtBC,QAAQ,CAAC,IAAY,EAAE,EAAY;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,IAAa;QACpB,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,IAAa;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAChC,CAAC;CACF;AAED,8DAA8D;AAC9D,MAAM,CAAC,GAAG,UAAiB,CAAC;AAC5B,IAAI,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC;IAC9B,CAAC,CAAC,sBAAsB,GAAG,IAAI,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAEY,QAAA,UAAU,GAAa,CAAC,CAAC,sBAAsB,CAAC","sourcesContent":["export type Dialect = 'sqlite' | 'mysql' | 'postgres';\n\n// Result for statements that produce no result set (INSERT/UPDATE/DELETE\n// without RETURNING, DDL). Statements that DO produce a result set — SELECT,\n// CTEs, and `... RETURNING` — return rows (`any[]`) instead; the affected\n// count is `rows.length`.\nexport interface WriteResult {\n rowsAffected: number;\n lastInsertId?: unknown;\n}\n\n/** @deprecated RETURNING now yields rows directly; this is identical to WriteResult. */\nexport type PostgresWriteResult = WriteResult;\n\nexport type QueryResult = any[] | WriteResult;\n\nexport interface Database {\n dialect: Dialect;\n query(sql: string, params: unknown[]): Promise<QueryResult>;\n // Underlying driver connection or pool (driver-specific)\n conn: unknown;\n close: () => Promise<void> | void;\n /**\n * Optional, SQLite dialect only: a fresh handle for one transaction. When\n * present, a transaction runs `BEGIN … COMMIT` on this handle instead of on\n * the shared `Database`, so statements other flows send to the shared\n * handle stay outside it (the role a pooled connection plays for\n * Postgres/MySQL). Called once per transaction.\n */\n transactionHandle?: () => Database;\n}\n\nclass Registry {\n private map = new Map<string, Database>();\n private defaultName?: string;\n\n register(name: string, db: Database) {\n this.map.set(name, db);\n return this;\n }\n\n setDefault(name: string) {\n if (!this.map.has(name)) throw new Error(`No database registered under ${name}`);\n this.defaultName = name;\n return this;\n }\n\n getEntry(name?: string): { name: string; db: Database } {\n const key = name ?? this.defaultName;\n if (!key) throw new Error('No default database set');\n const db = this.map.get(key);\n if (!db) throw new Error(`No database registered under ${key}`);\n return { name: key, db };\n }\n\n get(name?: string): Database {\n return this.getEntry(name).db;\n }\n}\n\n// Ensure a single shared instance across module copies/usages\nconst g = globalThis as any;\nif (!g.__SQLSTACK_SINGLETON__) {\n g.__SQLSTACK_SINGLETON__ = new Registry();\n}\n\nexport const SqlStackDB: Registry = g.__SQLSTACK_SINGLETON__;\n\nexport type IStackDB = typeof SqlStackDB;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/transactions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transactions.d.ts","sourceRoot":"","sources":["../../src/transactions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC;AAE5C,8DAA8D;AAC9D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,qCAAqC,CAAC;IACrD,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;AA+BzE,qBAAa,iBAAiB;;IAC5B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;gBAKlB,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO;IAMhE,IAAI,MAAM,IAAI,MAAM,CAEnB;IAEK,aAAa;IAInB,YAAY,CAAC,YAAY,CAAC,EAAE,OAAO;IAO7B,MAAM;IAKN,QAAQ;IAKR,OAAO;IAKP,eAAe;IAMf,aAAa,CAAC,GAAG,EAAE,OAAO;CAKjC;AAED,cAAM,OAAO;IACX,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAClG,KAAK,SAAK;IACV,OAAO,UAAS;IAChB,SAAS,UAAS;IAClB,YAAY,UAAS;IACrB,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,0EAA0E;IAC1E,WAAW,EAAE,OAAO,GAAG,iBAAiB,CAAW;IACnD,oFAAoF;IACpF,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAEpC,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,SAAS,CAAoC;gBAEzC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,kBAAkB,GAAG,SAAS;IASzE,IAAI,QAAQ,IAAI,QAAQ,CAEvB;IAEK,aAAa;IA2Cb,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAK3D,eAAe;IAUf,iBAAiB;IAUjB,OAAO;IAYP,eAAe;IAaf,aAAa,CAAC,GAAG,EAAE,OAAO;CAWjC;AAuBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAO7E;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAKzF;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC,CAkBvG;AAoED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI,CAKjF;AA2KD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAaxF;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,EAAE,EAAE;IAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,GAAG,iBAAiB,GAAG,SAAS,CAe5I;AA4OD,MAAM,MAAM,oBAAoB,GAAG;IACjC,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACrE,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvG;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACzC,+EAA+E;IAC/E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,iEAAiE;IACjE,WAAW,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;IACvC,8DAA8D;IAC9D,YAAY,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;CACzC,CAAC;AAgBF,eAAO,MAAM,WAAW,EAAE,oBAKjB,CAAC;AAwBV,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,iBAAiB,GAAG,SAAS,CAGxF;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAOzF"}
@@ -145,8 +145,12 @@ class TxState {
145
145
  this.started = true;
146
146
  return;
147
147
  }
148
- // sqlite: existing connection is already bound
149
- await this.db.query(this.sqliteBegin, []);
148
+ // sqlite: existing connection is already bound, unless the adapter hands
149
+ // out a per-transaction handle (e.g. sqlego) to keep other flows out.
150
+ if (typeof this.db.transactionHandle === 'function') {
151
+ this.txDb = this.db.transactionHandle();
152
+ }
153
+ await this.activeDb.query(this.sqliteBegin, []);
150
154
  this.started = true;
151
155
  }
152
156
  async query(sql, params) {