@@ -38,6 +38,25 @@ export interface SqliteError {
3838 message : string
3939}
4040
41+ /**
42+ * Token representing an active pausable transaction.
43+ * Used to continue, commit, or rollback the transaction.
44+ */
45+ export interface TransactionToken {
46+ /** Database path */
47+ dbPath : string
48+ /** Unique transaction ID */
49+ transactionId : string
50+ }
51+
52+ /**
53+ * Actions that can be performed on a pausable transaction.
54+ */
55+ export type TransactionAction =
56+ | { type : 'Continue' ; statements : Array < { query : string ; values : SqlValue [ ] } > }
57+ | { type : 'Commit' }
58+ | { type : 'Rollback' }
59+
4160/**
4261 * Custom configuration for SQLite database connection
4362 */
@@ -196,6 +215,10 @@ export default class Database {
196215 * Executes multiple write statements atomically within a transaction.
197216 * All statements either succeed together or fail together.
198217 *
218+ * **Use this method** when you have a batch of writes to execute and don't need to
219+ * read data mid-transaction. For transactions that require reading uncommitted data
220+ * to decide how to proceed, use `executePausableTransaction()` instead.
221+ *
199222 * The function automatically:
200223 * - Begins a transaction (BEGIN)
201224 * - Executes all statements in order
@@ -365,6 +388,154 @@ export default class Database {
365388 return success
366389 }
367390
391+ /**
392+ * **executePausableTransaction**
393+ *
394+ * Begins a pausable transaction for cases where you need to **read data mid-transaction
395+ * to decide how to proceed**. For example, inserting a record and then reading its
396+ * generated ID or computed values before continuing with related writes.
397+ *
398+ * The transaction remains open, holding a write lock on the database, until you
399+ * commit or rollback using `transactionContinue()`.
400+ *
401+ * **Use this method when:**
402+ * - You need to read back generated IDs (e.g., AUTOINCREMENT columns)
403+ * - You need to see computed values (e.g., triggers, default values)
404+ * - Your next writes depend on data from earlier writes in the same transaction
405+ *
406+ * **Use `executeTransaction()` instead when:**
407+ * - You just need to execute a batch of writes atomically
408+ * - You know all the data upfront and don't need to read mid-transaction
409+ *
410+ * While paused, you can:
411+ * - Read uncommitted data using `transactionRead()`
412+ * - Continue with more statements using `transactionContinue()` with Continue action
413+ * - Commit the transaction using `transactionContinue()` with Commit action
414+ * - Rollback the transaction using `transactionContinue()` with Rollback action
415+ *
416+ * **Important:** Only one transaction can be active per database at a time. The
417+ * writer connection is held for the entire duration - keep transactions short.
418+ *
419+ * @param initialStatements - Array of [query, values?] tuples to execute initially
420+ * @returns Promise that resolves with a transaction token
421+ *
422+ * @example
423+ * ```ts
424+ * // Insert an order and read back its ID
425+ * const token = await db.executePausableTransaction([
426+ * ['INSERT INTO orders (user_id, total) VALUES ($1, $2)', [userId, 0]]
427+ * ]);
428+ *
429+ * // Read the generated order ID
430+ * const orders = await db.transactionRead<Array<{ id: number }>>(
431+ * token,
432+ * 'SELECT id FROM orders WHERE user_id = $1 ORDER BY id DESC LIMIT 1',
433+ * [userId]
434+ * );
435+ * const orderId = orders[0].id;
436+ *
437+ * // Use the ID in subsequent writes
438+ * const newToken = await db.transactionContinue(token, {
439+ * type: 'Continue',
440+ * statements: [
441+ * { query: 'INSERT INTO order_items (order_id, product_id) VALUES ($1, $2)', values: [orderId, productId] }
442+ * ]
443+ * });
444+ *
445+ * await db.transactionContinue(newToken!, { type: 'Commit' });
446+ * ```
447+ */
448+ async executePausableTransaction (
449+ initialStatements : Array < [ string , SqlValue [ ] ?] >
450+ ) : Promise < TransactionToken > {
451+ return await invoke < TransactionToken > ( 'plugin:sqlite|execute_pausable_transaction' , {
452+ db : this . path ,
453+ initialStatements : initialStatements . map ( ( [ query , values ] ) => ( {
454+ query,
455+ values : values ?? [ ]
456+ } ) )
457+ } )
458+ }
459+
460+ /**
461+ * **transactionContinue**
462+ *
463+ * Continue, commit, or rollback a pausable transaction.
464+ *
465+ * Actions:
466+ * - `{ type: 'Continue', statements: [...] }` - Execute more statements and return updated token
467+ * - `{ type: 'Commit' }` - Commit the transaction and release the write lock
468+ * - `{ type: 'Rollback' }` - Rollback the transaction and release the write lock
469+ *
470+ * @param token - Transaction token from executePausableTransaction
471+ * @param action - Action to perform
472+ * @returns Promise that resolves with a new token if continuing, or undefined if committed/rolled back
473+ *
474+ * @example
475+ * ```ts
476+ * const token = await db.executePausableTransaction([
477+ * ['INSERT INTO users (name) VALUES ($1)', ['Alice']]
478+ * ]);
479+ *
480+ * const newToken = await db.transactionContinue(token, {
481+ * type: 'Continue',
482+ * statements: [{ query: 'INSERT INTO users (name) VALUES ($1)', values: ['Bob'] }]
483+ * });
484+ *
485+ * await db.transactionContinue(newToken!, { type: 'Commit' });
486+ * ```
487+ */
488+ async transactionContinue (
489+ token : TransactionToken ,
490+ action : TransactionAction
491+ ) : Promise < TransactionToken | undefined > {
492+ return await invoke < TransactionToken | undefined > ( 'plugin:sqlite|transaction_continue' , {
493+ token,
494+ action
495+ } )
496+ }
497+
498+ /**
499+ * **transactionRead**
500+ *
501+ * Read data from the database within a pausable transaction context.
502+ * This allows you to see uncommitted writes from the current transaction.
503+ *
504+ * The query executes on the same connection as the transaction, so you can
505+ * read data that hasn't been committed yet.
506+ *
507+ * @param token - Transaction token from executePausableTransaction
508+ * @param query - SELECT query to execute
509+ * @param bindValues - Optional parameter values
510+ * @returns Promise that resolves with query results
511+ *
512+ * @example
513+ * ```ts
514+ * const token = await db.executePausableTransaction([
515+ * ['INSERT INTO users (name) VALUES ($1)', ['Alice']]
516+ * ]);
517+ *
518+ * const users = await db.transactionRead<User[]>(
519+ * token,
520+ * 'SELECT * FROM users WHERE name = $1',
521+ * ['Alice']
522+ * );
523+ *
524+ * await db.transactionContinue(token, { type: 'Commit' });
525+ * ```
526+ */
527+ async transactionRead < T > (
528+ token : TransactionToken ,
529+ query : string ,
530+ bindValues ?: SqlValue [ ]
531+ ) : Promise < T > {
532+ return await invoke < T > ( 'plugin:sqlite|transaction_read' , {
533+ token,
534+ query,
535+ values : bindValues ?? [ ]
536+ } )
537+ }
538+
368539 /**
369540 * **getMigrationEvents**
370541 *
0 commit comments