@nextlyhq/adapter-sqlite 0.0.2-alpha.73 → 0.0.2-alpha.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DrizzleAdapter } from "@nextlyhq/adapter-drizzle";
2
- import { AdapterLogger, BaseAdapterConfig, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, OrderBySpec, PoolStats, SelectOptions, SessionOptions, SqlParam, SqliteAdapterConfig, StatementSession, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from "@nextlyhq/adapter-drizzle/types";
2
+ import { AdapterLogger, BaseAdapterConfig, ConnectionInspection, DatabaseCapabilities, DatabaseError, DatabaseErrorKind, DeleteOptions, InsertOptions, JoinSpec, OrderBySpec, PoolStats, SelectOptions, SessionOptions, SqlParam, SqliteAdapterConfig, StatementSession, TransactionContext, TransactionOptions, UpdateOptions, UpsertOptions, WhereClause, WhereCondition, WhereOperator } from "@nextlyhq/adapter-drizzle/types";
3
3
  import { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
4
4
  import { AnyRelations } from "drizzle-orm";
5
5
  /**
@@ -18,7 +18,8 @@ export declare const VERSION = "0.1.0";
18
18
  * - No connection pooling (single file/connection)
19
19
  * - RETURNING clause supported (SQLite 3.35+)
20
20
  * - Savepoints supported via nested transactions
21
- * - No native ILIKE (uses LOWER() LIKE workaround)
21
+ * - No native ILIKE (both sides case-folded, then LIKE; the fold is Nextly's own
22
+ * `nextly_fold_case`, registered on connect, since SQLite's `lower()` folds ASCII only)
22
23
  * - JSON support (not JSONB)
23
24
  * - No array types
24
25
  *
@@ -73,18 +74,6 @@ export declare class SqliteAdapter extends DrizzleAdapter {
73
74
  * surface area without changing the achievable concurrency.
74
75
  */
75
76
  private transactionQueue;
76
- /**
77
- * The transaction the current async chain is running inside, if any.
78
- *
79
- * A `transaction()` call made from INSIDE a transaction's work — a store
80
- * writing on behalf of the caller, a plugin's write inside its own
81
- * `ctx.db.transaction` — used to queue behind the very transaction that
82
- * was waiting for it, and neither ever settled: that hung every later
83
- * transaction on the instance too. Such a call now runs as a SAVEPOINT of
84
- * the active transaction instead, so it commits or rolls back with it and
85
- * its own failure undoes only its own part.
86
- */
87
- private readonly transactionScope;
88
77
  /**
89
78
  * Creates a new SQLite adapter instance.
90
79
  *
@@ -121,6 +110,12 @@ export declare class SqliteAdapter extends DrizzleAdapter {
121
110
  * The database is single-file with a single connection.
122
111
  */
123
112
  getPoolStats(): PoolStats | null;
113
+ /**
114
+ * The connection as a health check reads it: a file on this machine, with
115
+ * no network connection to encrypt, and the library's version, timed for
116
+ * the round trip. A read that fails leaves the version and time out.
117
+ */
118
+ override inspectConnection(): Promise<ConnectionInspection>;
124
119
  /**
125
120
  * Execute a raw SQL query.
126
121
  *
@@ -131,6 +126,8 @@ export declare class SqliteAdapter extends DrizzleAdapter {
131
126
  * @throws {DatabaseError} If query execution fails
132
127
  */
133
128
  executeQuery<T = unknown>(sql: string, params?: SqlParam[]): Promise<T[]>;
129
+ /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
130
+ private transactionWaitMs;
134
131
  /**
135
132
  * Execute work within a transaction.
136
133
  *
@@ -154,9 +151,7 @@ export declare class SqliteAdapter extends DrizzleAdapter {
154
151
  * Note: SQLite uses DEFERRED, IMMEDIATE, or EXCLUSIVE transaction modes
155
152
  * rather than isolation levels. This adapter uses IMMEDIATE.
156
153
  */
157
- /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
158
- private transactionWaitMs;
159
- transaction<T>(work: (tx: TransactionContext) => Promise<T>, _options?: TransactionOptions): Promise<T>;
154
+ transaction<T>(work: (tx: TransactionContext) => Promise<T>, options?: TransactionOptions): Promise<T>;
160
155
  /**
161
156
  * Runs `work` outside a transaction, each statement committing as it runs,
162
157
  * once no other flow's transaction is open on the one connection.
@@ -174,90 +169,18 @@ export declare class SqliteAdapter extends DrizzleAdapter {
174
169
  * done, and holds the queue until `run` is done too.
175
170
  */
176
171
  private takeTurn;
172
+ /** A held effect's failure that no reporter took goes to the adapter's logger. */
173
+ protected override logHeldEffectFailure(failure: Error): void;
177
174
  /**
178
- * Hold `effect` until the transaction enclosing this async context commits,
179
- * or run it now when none does.
180
- *
181
- * The effect belongs to the scope this async context runs in, even one
182
- * that has already released or rolled back: a service whose own
183
- * transaction nested as a savepoint registers from its caller's scope once
184
- * that savepoint has released, and the change is undoable for as long as
185
- * any scope around it is. The outermost transaction runs the effect once
186
- * its COMMIT succeeds, unless a scope between the effect's scope and the
187
- * outermost transaction rolled back, and drops every effect on ROLLBACK or
188
- * a failed COMMIT. A context whose outermost transaction has already
189
- * finished is outside any transaction, so its effect runs now, unless what
190
- * it announces was undone and it has run no transaction since.
191
- */
192
- override afterCommit(effect: () => unknown, onDeferredFailure?: (error: unknown) => void): Promise<void>;
193
- /**
194
- * `effect`, bound to the async context it is registered from.
195
- *
196
- * A held effect runs after the outermost COMMIT, from the code that ran the
197
- * outermost transaction, so without this it sees that code's context rather
198
- * than its own: whatever its caller pinned around the write is gone. That
199
- * includes who is calling. A scope pinned for a scoped API key lost there
200
- * leaves a hook's nested call judged by the key's owner instead of the key,
201
- * and a person's frame lost there leaves the hook told of nobody.
202
- *
203
- * The adapter's own transaction scope is the one store it leaves: the
204
- * effect runs once that transaction has finished, outside it, as an effect
205
- * held to the outermost commit always has.
206
- */
207
- private inRegisteringContext;
208
- /**
209
- * Run the effects a committed transaction held, in order. A failure is
210
- * reported, never thrown: the change is durable, and reporting the
211
- * transaction as failed would invite a retry of something already done.
212
- */
213
- private runHeldEffects;
214
- /** Hand a held effect's failure to its reporter, or to the adapter's log. */
215
- private reportHeldEffectFailure;
216
- /**
217
- * Whether a `transaction()` call made here would join an open transaction
218
- * as a savepoint. On SQLite it would: the connection is the only one, so a
219
- * transaction open on it encloses whatever the async chain that opened it
220
- * starts.
221
- */
222
- override inTransaction(): boolean;
223
- /**
224
- * Whether the calling async flow is inside a transaction that has not
225
- * finished, judged from the transaction this flow was started inside,
226
- * never from the connection: another request's open transaction answers
227
- * false here, where its own `transaction()` queues behind that one.
228
- *
229
- * A flow started inside a transaction, even inside a savepoint that has
230
- * since released, stays inside it until its outermost transaction has
231
- * committed or rolled back: until then whatever it writes on the one
232
- * connection lands in that transaction.
233
- */
234
- override transactionOpenInFlow(): boolean;
235
- /**
236
- * The scope a `transaction()` call made here nests in, or none when it
237
- * opens a transaction of its own.
238
- *
239
- * A call made later from inside a savepoint that has since released runs
240
- * inside the innermost scope open on the connection. Queued on an enclosing
241
- * scope, or on the instance, it would wait behind the scope running there,
242
- * which may be the one awaiting it. Savepoints on one connection form a
243
- * stack, so whatever opens now is inside that innermost scope anyway; when
244
- * none is open the call queues on the instance.
245
- *
246
- * The innermost open scope need not be related to the call. When it is a
247
- * sibling savepoint that later rolls back, the call's write rolls back with
248
- * it, although the call itself already resolved. Waiting for an unrelated
249
- * scope instead could wait on the very scope that awaits the call.
250
- */
251
- private nestingScope;
252
- /**
253
- * Run `work` as a savepoint of the active transaction: released when it
254
- * resolves, rolled back to when it throws.
175
+ * The statements of a savepoint `depth` deep on the one connection:
176
+ * released when its work resolves, rolled back to when it throws.
255
177
  */
256
- private runSavepoint;
178
+ private savepointSteps;
257
179
  /**
258
- * Inner transaction body. Kept private so callers always go through
259
- * the serialized `transaction()` queue above and the BEGIN/COMMIT
260
- * pair is never invoked outside of it.
180
+ * One attempt's transaction body, settled rather than rejected for the
181
+ * transaction's own failure (`runOutermost`). Kept private so callers always
182
+ * go through the serialized `transaction()` queue above and the
183
+ * BEGIN/COMMIT pair is never invoked outside of it.
261
184
  */
262
185
  private runTransaction;
263
186
  /**
package/dist/index.mjs CHANGED
@@ -1,9 +1,25 @@
1
- import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
2
1
  import { DrizzleAdapter } from "@nextlyhq/adapter-drizzle";
3
2
  import { createDatabaseError, isApplicationError, isDatabaseError, poolWaitTimeoutError } from "@nextlyhq/adapter-drizzle/types";
4
3
  import { checkDialectVersion } from "@nextlyhq/adapter-drizzle/version-check";
5
4
  import { drizzle } from "drizzle-orm/better-sqlite3";
6
5
  import { getTableConfig } from "drizzle-orm/sqlite-core";
6
+ import { registerSqliteFoldCase } from "@nextlyhq/adapter-drizzle/text-match";
7
+ // src/sql-functions.ts
8
+ /**
9
+ * The SQL functions a SQLite connection needs before Nextly's statements run on it.
10
+ *
11
+ * @module sql-functions
12
+ */
13
+ /**
14
+ * Register the SQL functions Nextly's statements call on SQLite, on `db`: the Unicode case fold
15
+ * (`nextly_fold_case`), since SQLite's own `lower()` folds ASCII letters only.
16
+ *
17
+ * Every connection the adapter opens is handed here as it opens, before anything runs on it, so
18
+ * no statement can reach a connection without the function.
19
+ */
20
+ function registerSqliteFunctions(db) {
21
+ registerSqliteFoldCase(db);
22
+ }
7
23
  // src/index.ts
8
24
  /**
9
25
  * @nextlyhq/adapter-sqlite
@@ -105,7 +121,8 @@ function sanitizeSqliteValue(v) {
105
121
  * - No connection pooling (single file/connection)
106
122
  * - RETURNING clause supported (SQLite 3.35+)
107
123
  * - Savepoints supported via nested transactions
108
- * - No native ILIKE (uses LOWER() LIKE workaround)
124
+ * - No native ILIKE (both sides case-folded, then LIKE; the fold is Nextly's own
125
+ * `nextly_fold_case`, registered on connect, since SQLite's `lower()` folds ASCII only)
109
126
  * - JSON support (not JSONB)
110
127
  * - No array types
111
128
  *
@@ -161,18 +178,6 @@ var SqliteAdapter = class extends DrizzleAdapter {
161
178
  */
162
179
  transactionQueue = Promise.resolve();
163
180
  /**
164
- * The transaction the current async chain is running inside, if any.
165
- *
166
- * A `transaction()` call made from INSIDE a transaction's work — a store
167
- * writing on behalf of the caller, a plugin's write inside its own
168
- * `ctx.db.transaction` — used to queue behind the very transaction that
169
- * was waiting for it, and neither ever settled: that hung every later
170
- * transaction on the instance too. Such a call now runs as a SAVEPOINT of
171
- * the active transaction instead, so it commits or rolls back with it and
172
- * its own failure undoes only its own part.
173
- */
174
- transactionScope = new AsyncLocalStorage();
175
- /**
176
181
  * Creates a new SQLite adapter instance.
177
182
  *
178
183
  * @param config - Adapter configuration
@@ -209,6 +214,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
209
214
  readonly: this.config.readonly ?? false,
210
215
  timeout: this.config.busyTimeout ?? DEFAULT_CONFIG.busyTimeout
211
216
  });
217
+ registerSqliteFunctions(this.db);
212
218
  if ((this.config.wal ?? DEFAULT_CONFIG.wal) && dbPath !== ":memory:" && !this.config.readonly) this.db.pragma("journal_mode = WAL");
213
219
  if (this.config.foreignKeys ?? DEFAULT_CONFIG.foreignKeys) this.db.pragma("foreign_keys = ON");
214
220
  await checkDialectVersion(this.db, "sqlite");
@@ -263,6 +269,30 @@ var SqliteAdapter = class extends DrizzleAdapter {
263
269
  return null;
264
270
  }
265
271
  /**
272
+ * The connection as a health check reads it: a file on this machine, with
273
+ * no network connection to encrypt, and the library's version, timed for
274
+ * the round trip. A read that fails leaves the version and time out.
275
+ */
276
+ inspectConnection() {
277
+ const inspection = {
278
+ encryption: "not-applicable",
279
+ local: true
280
+ };
281
+ if (!this.db) return Promise.resolve(inspection);
282
+ try {
283
+ const started = performance.now();
284
+ const row = this.db.prepare("SELECT sqlite_version() AS version").get();
285
+ const roundTripMs = Math.max(0, Math.round(performance.now() - started));
286
+ return Promise.resolve({
287
+ ...inspection,
288
+ roundTripMs,
289
+ ...typeof row?.version === "string" && row.version !== "" ? { serverVersion: row.version } : {}
290
+ });
291
+ } catch {
292
+ return Promise.resolve(inspection);
293
+ }
294
+ }
295
+ /**
266
296
  * Execute a raw SQL query.
267
297
  *
268
298
  * @param sql - SQL query string with $1, $2 placeholders (converted to ? for SQLite)
@@ -299,6 +329,10 @@ var SqliteAdapter = class extends DrizzleAdapter {
299
329
  throw this.classifyError(error, sql);
300
330
  }
301
331
  }
332
+ /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
333
+ transactionWaitMs() {
334
+ return this.config.pool?.connectionTimeoutMs ?? DEFAULT_CONFIG.connectionTimeoutMs;
335
+ }
302
336
  /**
303
337
  * Execute work within a transaction.
304
338
  *
@@ -322,18 +356,16 @@ var SqliteAdapter = class extends DrizzleAdapter {
322
356
  * Note: SQLite uses DEFERRED, IMMEDIATE, or EXCLUSIVE transaction modes
323
357
  * rather than isolation levels. This adapter uses IMMEDIATE.
324
358
  */
325
- /** How long a transaction waits for the connection: `pool.connectionTimeoutMs`. */
326
- transactionWaitMs() {
327
- return this.config.pool?.connectionTimeoutMs ?? DEFAULT_CONFIG.connectionTimeoutMs;
328
- }
329
- async transaction(work, _options) {
330
- const own = this.transactionScope.getStore();
331
- const parent = this.nestingScope();
332
- if (own && parent !== own) own.writesAfterClosing += 1;
333
- if (parent) return enqueueIn(parent, () => this.runSavepoint(work, parent));
334
- const { result, effects } = await this.takeTurn(() => this.runTransaction(work));
335
- await this.runHeldEffects(effects);
336
- return result;
359
+ transaction(work, options) {
360
+ return this.openOrJoin(work, options, {
361
+ run: () => this.takeTurn(() => this.runTransaction(work, options)),
362
+ retryable: () => false,
363
+ describe: () => ({
364
+ label: "a retryable error",
365
+ detail: {}
366
+ }),
367
+ logger: this.config.logger
368
+ });
337
369
  }
338
370
  /**
339
371
  * Runs `work` outside a transaction, each statement committing as it runs,
@@ -360,189 +392,58 @@ var SqliteAdapter = class extends DrizzleAdapter {
360
392
  this.transactionQueue = previous.then(() => next).catch(() => void 0);
361
393
  return next;
362
394
  }
363
- /**
364
- * Hold `effect` until the transaction enclosing this async context commits,
365
- * or run it now when none does.
366
- *
367
- * The effect belongs to the scope this async context runs in, even one
368
- * that has already released or rolled back: a service whose own
369
- * transaction nested as a savepoint registers from its caller's scope once
370
- * that savepoint has released, and the change is undoable for as long as
371
- * any scope around it is. The outermost transaction runs the effect once
372
- * its COMMIT succeeds, unless a scope between the effect's scope and the
373
- * outermost transaction rolled back, and drops every effect on ROLLBACK or
374
- * a failed COMMIT. A context whose outermost transaction has already
375
- * finished is outside any transaction, so its effect runs now, unless what
376
- * it announces was undone and it has run no transaction since.
377
- */
378
- async afterCommit(effect, onDeferredFailure) {
379
- const scope = this.transactionScope.getStore();
380
- if (!scope) {
381
- await effect();
382
- return;
383
- }
384
- const root = outermost(scope);
385
- if (!root.finished) {
386
- root.held.push({
387
- effect: this.inRegisteringContext(effect),
388
- onDeferredFailure,
389
- scope,
390
- writesBefore: scope.writesAfterClosing
391
- });
392
- return;
393
- }
394
- if (!undone(scope, scope.writesAfterClosing)) await effect();
395
- }
396
- /**
397
- * `effect`, bound to the async context it is registered from.
398
- *
399
- * A held effect runs after the outermost COMMIT, from the code that ran the
400
- * outermost transaction, so without this it sees that code's context rather
401
- * than its own: whatever its caller pinned around the write is gone. That
402
- * includes who is calling. A scope pinned for a scoped API key lost there
403
- * leaves a hook's nested call judged by the key's owner instead of the key,
404
- * and a person's frame lost there leaves the hook told of nobody.
405
- *
406
- * The adapter's own transaction scope is the one store it leaves: the
407
- * effect runs once that transaction has finished, outside it, as an effect
408
- * held to the outermost commit always has.
409
- */
410
- inRegisteringContext(effect) {
411
- return AsyncResource.bind(() => this.transactionScope.exit(effect));
412
- }
413
- /**
414
- * Run the effects a committed transaction held, in order. A failure is
415
- * reported, never thrown: the change is durable, and reporting the
416
- * transaction as failed would invite a retry of something already done.
417
- */
418
- async runHeldEffects(effects) {
419
- for (const { effect, onDeferredFailure } of effects) try {
420
- await effect();
421
- } catch (error) {
422
- this.reportHeldEffectFailure(error, onDeferredFailure);
423
- }
424
- }
425
- /** Hand a held effect's failure to its reporter, or to the adapter's log. */
426
- reportHeldEffectFailure(error, onDeferredFailure) {
427
- try {
428
- if (onDeferredFailure) {
429
- onDeferredFailure(error);
430
- return;
431
- }
432
- } catch {}
433
- const failure = error instanceof Error ? error : new Error(String(error));
395
+ /** A held effect's failure that no reporter took goes to the adapter's logger. */
396
+ logHeldEffectFailure(failure) {
434
397
  if (this.config.logger?.error) this.config.logger.error(failure, { phase: "after-commit" });
435
- else console.error("An effect held until commit failed:", failure);
436
- }
437
- /**
438
- * Whether a `transaction()` call made here would join an open transaction
439
- * as a savepoint. On SQLite it would: the connection is the only one, so a
440
- * transaction open on it encloses whatever the async chain that opened it
441
- * starts.
442
- */
443
- inTransaction() {
444
- return this.nestingScope() !== void 0;
398
+ else super.logHeldEffectFailure(failure);
445
399
  }
446
400
  /**
447
- * Whether the calling async flow is inside a transaction that has not
448
- * finished, judged from the transaction this flow was started inside,
449
- * never from the connection: another request's open transaction answers
450
- * false here, where its own `transaction()` queues behind that one.
451
- *
452
- * A flow started inside a transaction, even inside a savepoint that has
453
- * since released, stays inside it until its outermost transaction has
454
- * committed or rolled back: until then whatever it writes on the one
455
- * connection lands in that transaction.
456
- */
457
- transactionOpenInFlow() {
458
- const own = this.transactionScope.getStore();
459
- return own !== void 0 && !outermost(own).finished;
460
- }
461
- /**
462
- * The scope a `transaction()` call made here nests in, or none when it
463
- * opens a transaction of its own.
464
- *
465
- * A call made later from inside a savepoint that has since released runs
466
- * inside the innermost scope open on the connection. Queued on an enclosing
467
- * scope, or on the instance, it would wait behind the scope running there,
468
- * which may be the one awaiting it. Savepoints on one connection form a
469
- * stack, so whatever opens now is inside that innermost scope anyway; when
470
- * none is open the call queues on the instance.
471
- *
472
- * The innermost open scope need not be related to the call. When it is a
473
- * sibling savepoint that later rolls back, the call's write rolls back with
474
- * it, although the call itself already resolved. Waiting for an unrelated
475
- * scope instead could wait on the very scope that awaits the call.
401
+ * The statements of a savepoint `depth` deep on the one connection:
402
+ * released when its work resolves, rolled back to when it throws.
476
403
  */
477
- nestingScope() {
478
- const own = this.transactionScope.getStore();
479
- return own?.active ? own : own?.open.at(-1);
480
- }
481
- /**
482
- * Run `work` as a savepoint of the active transaction: released when it
483
- * resolves, rolled back to when it throws.
484
- */
485
- async runSavepoint(work, parent) {
404
+ savepointSteps(depth) {
486
405
  const db = this.ensureDb();
487
- const name = `nextly_sp_${parent.depth + 1}`;
488
- try {
489
- const ctx = this.createTransactionContext(db);
490
- db.exec(`SAVEPOINT ${name}`);
491
- const scope = openScope(parent);
492
- try {
493
- const result = await this.transactionScope.run(scope, () => work(ctx));
494
- await settleNested(scope);
406
+ const name = `nextly_sp_${depth}`;
407
+ return {
408
+ context: this.createTransactionContext(db),
409
+ begin: () => {
410
+ db.exec(`SAVEPOINT ${name}`);
411
+ },
412
+ commit: () => {
413
+ db.exec(`RELEASE ${name}`);
414
+ },
415
+ rollback: () => {
416
+ db.exec(`ROLLBACK TO ${name}`);
495
417
  db.exec(`RELEASE ${name}`);
496
- return result;
497
- } catch (error) {
498
- await settleNested(scope);
499
- scope.rolledBack = true;
500
- try {
501
- db.exec(`ROLLBACK TO ${name}`);
502
- db.exec(`RELEASE ${name}`);
503
- } catch {}
504
- throw error;
505
418
  }
506
- } catch (error) {
507
- if (isApplicationError(error)) throw error;
508
- throw this.classifyError(error);
509
- }
419
+ };
510
420
  }
511
421
  /**
512
- * Inner transaction body. Kept private so callers always go through
513
- * the serialized `transaction()` queue above and the BEGIN/COMMIT
514
- * pair is never invoked outside of it.
422
+ * One attempt's transaction body, settled rather than rejected for the
423
+ * transaction's own failure (`runOutermost`). Kept private so callers always
424
+ * go through the serialized `transaction()` queue above and the
425
+ * BEGIN/COMMIT pair is never invoked outside of it.
515
426
  */
516
- async runTransaction(work) {
427
+ async runTransaction(work, options) {
517
428
  const db = this.ensureDb();
518
- const startTime = Date.now();
519
429
  try {
520
- const ctx = this.createTransactionContext(db);
521
- db.exec("BEGIN IMMEDIATE");
522
- const scope = openScope();
523
- try {
524
- const result = await this.transactionScope.run(scope, () => work(ctx));
525
- await settleNested(scope);
526
- db.exec("COMMIT");
527
- scope.finished = true;
528
- if (this.config.logger?.debug) {
529
- const durationMs = Date.now() - startTime;
530
- this.config.logger.debug("Transaction committed", { durationMs });
430
+ return await this.settleOutermost({
431
+ context: this.createTransactionContext(db),
432
+ join: { adapter: (depth) => this.savepointSteps(depth) },
433
+ begin: () => {
434
+ db.exec("BEGIN IMMEDIATE");
435
+ },
436
+ commit: () => {
437
+ db.exec("COMMIT");
438
+ },
439
+ rollback: () => {
440
+ try {
441
+ db.exec("ROLLBACK");
442
+ } catch (error) {
443
+ if (db.inTransaction) throw error;
444
+ }
531
445
  }
532
- return {
533
- result,
534
- effects: scope.held.filter((held) => !undone(held.scope, held.writesBefore))
535
- };
536
- } catch (error) {
537
- await settleNested(scope);
538
- scope.rolledBack = true;
539
- scope.finished = true;
540
- scope.held.length = 0;
541
- try {
542
- db.exec("ROLLBACK");
543
- } catch {}
544
- throw error;
545
- }
446
+ }, work, options);
546
447
  } catch (error) {
547
448
  if (isApplicationError(error)) throw error;
548
449
  throw this.classifyError(error);
@@ -586,6 +487,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
586
487
  if (data.length === 0) return [];
587
488
  const db = this.ensureDb();
588
489
  if (data.length === 1) return [await this.insert(table, data[0], options)];
490
+ const tableObj = this.resolvedTableObject(table);
589
491
  const columns = Object.keys(data[0]);
590
492
  const params = [];
591
493
  const valuesClauses = [];
@@ -604,9 +506,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
604
506
  sql += ` RETURNING ${returning}`;
605
507
  } else sql += " RETURNING *";
606
508
  try {
607
- const rows = db.prepare(sql).all(...params);
608
- const tableObj = this.getTableObject(table);
609
- return rows.map((r) => this.mapRowFromRawSql(tableObj, r));
509
+ return db.prepare(sql).all(...params).map((r) => this.mapRowFromRawSql(tableObj, r));
610
510
  } catch (error) {
611
511
  throw this.handleQueryError(error, "insertMany", table);
612
512
  }
@@ -693,7 +593,8 @@ var SqliteAdapter = class extends DrizzleAdapter {
693
593
  }
694
594
  },
695
595
  insert: async (table, data, options) => {
696
- const mapped = this.mapKeysToSqlColumns(this.getTableObject(table), data);
596
+ const tableObj = this.resolvedTableObject(table);
597
+ const mapped = this.mapKeysToSqlColumns(tableObj, data);
697
598
  const columns = Object.keys(mapped);
698
599
  const values = Object.values(mapped).map(sanitizeSqliteValue);
699
600
  const placeholders = values.map(() => "?").join(", ");
@@ -701,7 +602,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
701
602
  const ret = options?.returning;
702
603
  const returningEmpty = Array.isArray(ret) && ret.length === 0;
703
604
  if (!returningEmpty) {
704
- const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(this.getTableObject(table), ret).map((col) => this.escapeIdentifier(col)).join(", ");
605
+ const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(tableObj, ret).map((col) => this.escapeIdentifier(col)).join(", ");
705
606
  sql += ` RETURNING ${returning}`;
706
607
  }
707
608
  const stmt = db.prepare(sql);
@@ -710,11 +611,11 @@ var SqliteAdapter = class extends DrizzleAdapter {
710
611
  return;
711
612
  }
712
613
  const rows = stmt.all(...values);
713
- return this.mapRowFromRawSql(this.getTableObject(table), rows[0]);
614
+ return this.mapRowFromRawSql(tableObj, rows[0]);
714
615
  },
715
616
  insertMany: async (table, data, options) => {
716
617
  if (data.length === 0) return [];
717
- const tableObj = this.getTableObject(table);
618
+ const tableObj = this.resolvedTableObject(table);
718
619
  const mappedRecords = data.map((r) => this.mapKeysToSqlColumns(tableObj, r));
719
620
  const columns = Object.keys(mappedRecords[0]);
720
621
  const allValues = [];
@@ -731,7 +632,7 @@ var SqliteAdapter = class extends DrizzleAdapter {
731
632
  const ret = options?.returning;
732
633
  const returningEmpty = Array.isArray(ret) && ret.length === 0;
733
634
  if (!returningEmpty) {
734
- const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(this.getTableObject(table), ret).map((col) => this.escapeIdentifier(col)).join(", ");
635
+ const returning = !ret || ret === "*" ? "*" : this.mapColumnNamesToSql(tableObj, ret).map((col) => this.escapeIdentifier(col)).join(", ");
735
636
  sql += ` RETURNING ${returning}`;
736
637
  }
737
638
  const stmt = db.prepare(sql);
@@ -838,44 +739,6 @@ function isSqliteAdapter(value) {
838
739
  return value instanceof SqliteAdapter;
839
740
  }
840
741
  /**
841
- * Open the scope of a savepoint inside `parent`, or of an outermost
842
- * transaction when there is none, and record it among the connection's open
843
- * scopes.
844
- */
845
- function openScope(parent) {
846
- const scope = {
847
- active: true,
848
- depth: parent ? parent.depth + 1 : 0,
849
- parent,
850
- rolledBack: false,
851
- writesAfterClosing: 0,
852
- finished: false,
853
- held: [],
854
- queue: Promise.resolve(),
855
- open: parent ? parent.open : []
856
- };
857
- scope.open.push(scope);
858
- return scope;
859
- }
860
- /** The outermost transaction `scope` runs inside; itself for that one. */
861
- function outermost(scope) {
862
- let at = scope;
863
- while (at.parent) at = at.parent;
864
- return at;
865
- }
866
- /**
867
- * Whether an effect registered from `scope` describes a write that was
868
- * undone: `scope`, or a scope it runs inside, rolled back, and no
869
- * transaction had run from its context since it closed when the effect was
870
- * registered (`writesBefore`). An effect registered before such a write
871
- * still describes the undone one.
872
- */
873
- function undone(scope, writesBefore) {
874
- if (writesBefore > 0) return false;
875
- for (let at = scope; at; at = at.parent) if (at.rolledBack) return true;
876
- return false;
877
- }
878
- /**
879
742
  * Settles once `turn` does, or rejects with the pool-wait timeout error once
880
743
  * `boundMs` passes first. A bound of 0 waits without limit, as pg-pool reads a
881
744
  * `connectionTimeoutMillis` of 0.
@@ -891,23 +754,6 @@ function waitForTurn(turn, boundMs) {
891
754
  });
892
755
  });
893
756
  }
894
- /** Chain `run` onto a scope's queue, keeping the queue itself unrejected. */
895
- function enqueueIn(scope, run) {
896
- const next = scope.queue.then(run, run);
897
- scope.queue = next.catch(() => void 0);
898
- return next;
899
- }
900
- /**
901
- * Close a scope to new nested calls and wait for those already queued. A call
902
- * arriving after this joins the innermost scope still open on the connection,
903
- * or, when none is, goes to the instance queue behind the transaction.
904
- */
905
- async function settleNested(scope) {
906
- scope.active = false;
907
- const index = scope.open.indexOf(scope);
908
- if (index !== -1) scope.open.splice(index, 1);
909
- await scope.queue;
910
- }
911
757
  export { SqliteAdapter, VERSION, createSqliteAdapter, isSqliteAdapter };
912
758
 
913
759
  //# sourceMappingURL=index.mjs.map