@bastani/atomic 0.9.48 → 0.9.49

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.49] - 2026-10-27
6
+
7
+ Faster workflow journals on remote databases.
8
+
9
+ - **One request per journal batch:** workflow journals on a remote database write a whole batch of events in one write transaction and one request, saving a network round trip per statement ([#4083](https://github.com/bastani-inc/atomic/issues/4083), [#4084](https://github.com/bastani-inc/atomic/pull/4084))
10
+
11
+ ### Added
12
+
13
+ - `createSqliteDatabase` accepts an optional `batch` driver call that sends several statements in one write transaction and one request, exposed as `SqliteDatabase.batch`. Workflow journals use it to write a batch of events in one request, which saves a network round trip per statement on remote databases ([#4083](https://github.com/bastani-inc/atomic/issues/4083))
14
+
15
+ ## [0.9.49-alpha.1] - 2026-10-27
16
+
17
+ Faster workflow journals on remote databases.
18
+
19
+ - **One request per journal batch:** `SqliteDatabase.batch` sends several statements in one write transaction and one request, so workflow journals on a remote database save a network round trip per statement ([#4083](https://github.com/bastani-inc/atomic/issues/4083), [#4084](https://github.com/bastani-inc/atomic/pull/4084))
20
+
21
+ ### Added
22
+
23
+ - `createSqliteDatabase` accepts an optional `batch` driver call that sends several statements in one write transaction and one request, exposed as `SqliteDatabase.batch`. Workflow journals use it to write a batch of events in one request, which saves a network round trip per statement on remote databases ([#4083](https://github.com/bastani-inc/atomic/issues/4083))
24
+
5
25
  ## [0.9.47] - 2026-10-27
6
26
 
7
27
  Two fixes for model errors and MCP tool names.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/mcp",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "description": "Atomic's pi-compatible MCP integration for server connections, OAuth, tools, resources, and terminal management.",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/subagents",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "description": "Atomic extension for delegating tasks to subagents with parallel execution. Fork of: https://github.com/nicobailon/pi-subagents",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/web-access",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
6
6
  "contributors": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -60,6 +60,12 @@ export declare class SqlRunJournal implements RunJournal {
60
60
  * failure rejects the whole batch, with nothing written.
61
61
  */
62
62
  appendMany(lease: Lease, appends: readonly JournalBatchAppend[]): Promise<readonly PromiseSettledResult<AppendOutcome>[]>;
63
+ /**
64
+ * `appendMany` as one `SqliteDatabase.batch` request, for a batch with no pool guards and no payload errors. Every
65
+ * write checks the lease, the key and the sequence limit in SQL, so the outcomes match the savepoint path: a stale
66
+ * lease writes nothing and rejects the batch, a known key reports `exists`, and each append settles alone, in order.
67
+ */
68
+ private appendInOneRequest;
63
69
  /** Validate an append outside any transaction; its payload error is raised where `append` always raised it. */
64
70
  private prepare;
65
71
  private insert;
@@ -9491,6 +9491,9 @@ class SqlRunJournal {
9491
9491
  return error;
9492
9492
  }
9493
9493
  });
9494
+ const batch = this.database.batch?.bind(this.database);
9495
+ if (batch && prepared.every((append) => append instanceof Error || append.guard === undefined && typeof append.payload === "string"))
9496
+ return this.operation(() => this.appendInOneRequest(batch, owned, prepared));
9494
9497
  return this.operation(() => this.database.transaction(async (tx) => {
9495
9498
  await this.checkLease(tx, owned);
9496
9499
  const results = [];
@@ -9511,6 +9514,72 @@ class SqlRunJournal {
9511
9514
  return results;
9512
9515
  }));
9513
9516
  }
9517
+ async appendInOneRequest(batch, owned, prepared) {
9518
+ const t = this.tables;
9519
+ const holds = `EXISTS (SELECT 1 FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.holderId} = ? AND ${JOURNAL_COLUMNS.epoch} = ?)`;
9520
+ const held = [owned.runId, owned.holderId, owned.epoch];
9521
+ const latest = `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.seq} = (SELECT MAX(${JOURNAL_COLUMNS.seq}) FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?)`;
9522
+ const statements = [
9523
+ { sql: `SELECT * FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ?`, params: [owned.runId] }
9524
+ ];
9525
+ const at = (statement) => statements.push(statement) - 1;
9526
+ const plans = prepared.map((append) => {
9527
+ if (append instanceof Error)
9528
+ return append;
9529
+ const { key, type, payload, status } = append;
9530
+ const existing = key === undefined ? undefined : at({
9531
+ sql: `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?`,
9532
+ params: [owned.runId, key]
9533
+ });
9534
+ at({
9535
+ sql: `INSERT INTO ${t.events}
9536
+ SELECT ?, current.latest + 1, ?, ?, ?, ?, ${STORE_NOW}
9537
+ FROM (SELECT COALESCE(MAX(${JOURNAL_COLUMNS.seq}), 0) AS latest FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?) AS current
9538
+ WHERE current.latest < ? AND ${holds}${key === undefined ? "" : ` AND NOT EXISTS (SELECT 1 FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?)`}`,
9539
+ params: [
9540
+ owned.runId,
9541
+ owned.epoch,
9542
+ key ?? null,
9543
+ type,
9544
+ payload,
9545
+ owned.runId,
9546
+ Number.MAX_SAFE_INTEGER,
9547
+ ...held,
9548
+ ...key === undefined ? [] : [owned.runId, key]
9549
+ ]
9550
+ });
9551
+ const inserted = at({ sql: "SELECT changes() AS inserted", params: [] });
9552
+ if (status !== undefined)
9553
+ at({
9554
+ sql: `UPDATE ${t.runs} SET ${JOURNAL_COLUMNS.status} = ? WHERE ${JOURNAL_COLUMNS.runId} = ? AND changes() = 1`,
9555
+ params: [status, owned.runId]
9556
+ });
9557
+ const row = at({ sql: latest, params: [owned.runId, owned.runId] });
9558
+ return { existing, inserted, row };
9559
+ });
9560
+ const rows = await batch(statements);
9561
+ const lease = rows[0][0];
9562
+ const epoch = lease === undefined ? undefined : journalCounter(owned.runId, "epoch", lease[JOURNAL_COLUMNS.epoch]);
9563
+ if (lease?.[JOURNAL_COLUMNS.holderId] !== owned.holderId || epoch !== owned.epoch)
9564
+ throw new StaleLeaseError(owned.runId, owned.epoch, epoch ?? JOURNAL_LEASE.absentEpoch);
9565
+ return plans.map((plan) => {
9566
+ if (plan instanceof Error)
9567
+ return { status: "rejected", reason: plan };
9568
+ try {
9569
+ const existing = plan.existing === undefined ? undefined : rows[plan.existing][0];
9570
+ if (existing)
9571
+ return { status: "fulfilled", value: { status: "exists", entry: entryFromRow(existing) } };
9572
+ const row = rows[plan.row][0];
9573
+ if (Number(rows[plan.inserted][0].inserted) === 1)
9574
+ return { status: "fulfilled", value: { status: "appended", entry: entryFromRow(row) } };
9575
+ const actual = journalCounter(owned.runId, "seq", row?.[JOURNAL_COLUMNS.seq] ?? 0);
9576
+ journalCounter(owned.runId, "seq", actual + 1, "overflow");
9577
+ throw new StorageUnavailableError2(this.descriptor, new Error("The batched append was not written."));
9578
+ } catch (error) {
9579
+ return { status: "rejected", reason: error };
9580
+ }
9581
+ });
9582
+ }
9514
9583
  prepare(event, key, appendGuard) {
9515
9584
  if (key !== undefined)
9516
9585
  validateText("key", key);
@@ -3570,6 +3570,9 @@ class SqlRunJournal {
3570
3570
  return error;
3571
3571
  }
3572
3572
  });
3573
+ const batch = this.database.batch?.bind(this.database);
3574
+ if (batch && prepared.every((append) => append instanceof Error || append.guard === undefined && typeof append.payload === "string"))
3575
+ return this.operation(() => this.appendInOneRequest(batch, owned, prepared));
3573
3576
  return this.operation(() => this.database.transaction(async (tx) => {
3574
3577
  await this.checkLease(tx, owned);
3575
3578
  const results = [];
@@ -3590,6 +3593,72 @@ class SqlRunJournal {
3590
3593
  return results;
3591
3594
  }));
3592
3595
  }
3596
+ async appendInOneRequest(batch, owned, prepared) {
3597
+ const t = this.tables;
3598
+ const holds = `EXISTS (SELECT 1 FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.holderId} = ? AND ${JOURNAL_COLUMNS.epoch} = ?)`;
3599
+ const held = [owned.runId, owned.holderId, owned.epoch];
3600
+ const latest = `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.seq} = (SELECT MAX(${JOURNAL_COLUMNS.seq}) FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?)`;
3601
+ const statements = [
3602
+ { sql: `SELECT * FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ?`, params: [owned.runId] }
3603
+ ];
3604
+ const at = (statement) => statements.push(statement) - 1;
3605
+ const plans = prepared.map((append) => {
3606
+ if (append instanceof Error)
3607
+ return append;
3608
+ const { key, type, payload, status } = append;
3609
+ const existing = key === undefined ? undefined : at({
3610
+ sql: `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?`,
3611
+ params: [owned.runId, key]
3612
+ });
3613
+ at({
3614
+ sql: `INSERT INTO ${t.events}
3615
+ SELECT ?, current.latest + 1, ?, ?, ?, ?, ${STORE_NOW}
3616
+ FROM (SELECT COALESCE(MAX(${JOURNAL_COLUMNS.seq}), 0) AS latest FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?) AS current
3617
+ WHERE current.latest < ? AND ${holds}${key === undefined ? "" : ` AND NOT EXISTS (SELECT 1 FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?)`}`,
3618
+ params: [
3619
+ owned.runId,
3620
+ owned.epoch,
3621
+ key ?? null,
3622
+ type,
3623
+ payload,
3624
+ owned.runId,
3625
+ Number.MAX_SAFE_INTEGER,
3626
+ ...held,
3627
+ ...key === undefined ? [] : [owned.runId, key]
3628
+ ]
3629
+ });
3630
+ const inserted = at({ sql: "SELECT changes() AS inserted", params: [] });
3631
+ if (status !== undefined)
3632
+ at({
3633
+ sql: `UPDATE ${t.runs} SET ${JOURNAL_COLUMNS.status} = ? WHERE ${JOURNAL_COLUMNS.runId} = ? AND changes() = 1`,
3634
+ params: [status, owned.runId]
3635
+ });
3636
+ const row = at({ sql: latest, params: [owned.runId, owned.runId] });
3637
+ return { existing, inserted, row };
3638
+ });
3639
+ const rows = await batch(statements);
3640
+ const lease = rows[0][0];
3641
+ const epoch = lease === undefined ? undefined : journalCounter(owned.runId, "epoch", lease[JOURNAL_COLUMNS.epoch]);
3642
+ if (lease?.[JOURNAL_COLUMNS.holderId] !== owned.holderId || epoch !== owned.epoch)
3643
+ throw new StaleLeaseError(owned.runId, owned.epoch, epoch ?? JOURNAL_LEASE.absentEpoch);
3644
+ return plans.map((plan) => {
3645
+ if (plan instanceof Error)
3646
+ return { status: "rejected", reason: plan };
3647
+ try {
3648
+ const existing = plan.existing === undefined ? undefined : rows[plan.existing][0];
3649
+ if (existing)
3650
+ return { status: "fulfilled", value: { status: "exists", entry: entryFromRow(existing) } };
3651
+ const row = rows[plan.row][0];
3652
+ if (Number(rows[plan.inserted][0].inserted) === 1)
3653
+ return { status: "fulfilled", value: { status: "appended", entry: entryFromRow(row) } };
3654
+ const actual = journalCounter(owned.runId, "seq", row?.[JOURNAL_COLUMNS.seq] ?? 0);
3655
+ journalCounter(owned.runId, "seq", actual + 1, "overflow");
3656
+ throw new StorageUnavailableError2(this.descriptor, new Error("The batched append was not written."));
3657
+ } catch (error) {
3658
+ return { status: "rejected", reason: error };
3659
+ }
3660
+ });
3661
+ }
3593
3662
  prepare(event, key, appendGuard) {
3594
3663
  if (key !== undefined)
3595
3664
  validateText("key", key);
@@ -1742,6 +1742,9 @@ class SqlRunJournal {
1742
1742
  return error;
1743
1743
  }
1744
1744
  });
1745
+ const batch = this.database.batch?.bind(this.database);
1746
+ if (batch && prepared.every((append) => append instanceof Error || append.guard === undefined && typeof append.payload === "string"))
1747
+ return this.operation(() => this.appendInOneRequest(batch, owned, prepared));
1745
1748
  return this.operation(() => this.database.transaction(async (tx) => {
1746
1749
  await this.checkLease(tx, owned);
1747
1750
  const results = [];
@@ -1762,6 +1765,72 @@ class SqlRunJournal {
1762
1765
  return results;
1763
1766
  }));
1764
1767
  }
1768
+ async appendInOneRequest(batch, owned, prepared) {
1769
+ const t = this.tables;
1770
+ const holds = `EXISTS (SELECT 1 FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.holderId} = ? AND ${JOURNAL_COLUMNS.epoch} = ?)`;
1771
+ const held = [owned.runId, owned.holderId, owned.epoch];
1772
+ const latest = `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.seq} = (SELECT MAX(${JOURNAL_COLUMNS.seq}) FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?)`;
1773
+ const statements = [
1774
+ { sql: `SELECT * FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ?`, params: [owned.runId] }
1775
+ ];
1776
+ const at = (statement) => statements.push(statement) - 1;
1777
+ const plans = prepared.map((append) => {
1778
+ if (append instanceof Error)
1779
+ return append;
1780
+ const { key, type, payload, status } = append;
1781
+ const existing = key === undefined ? undefined : at({
1782
+ sql: `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?`,
1783
+ params: [owned.runId, key]
1784
+ });
1785
+ at({
1786
+ sql: `INSERT INTO ${t.events}
1787
+ SELECT ?, current.latest + 1, ?, ?, ?, ?, ${STORE_NOW}
1788
+ FROM (SELECT COALESCE(MAX(${JOURNAL_COLUMNS.seq}), 0) AS latest FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?) AS current
1789
+ WHERE current.latest < ? AND ${holds}${key === undefined ? "" : ` AND NOT EXISTS (SELECT 1 FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?)`}`,
1790
+ params: [
1791
+ owned.runId,
1792
+ owned.epoch,
1793
+ key ?? null,
1794
+ type,
1795
+ payload,
1796
+ owned.runId,
1797
+ Number.MAX_SAFE_INTEGER,
1798
+ ...held,
1799
+ ...key === undefined ? [] : [owned.runId, key]
1800
+ ]
1801
+ });
1802
+ const inserted = at({ sql: "SELECT changes() AS inserted", params: [] });
1803
+ if (status !== undefined)
1804
+ at({
1805
+ sql: `UPDATE ${t.runs} SET ${JOURNAL_COLUMNS.status} = ? WHERE ${JOURNAL_COLUMNS.runId} = ? AND changes() = 1`,
1806
+ params: [status, owned.runId]
1807
+ });
1808
+ const row = at({ sql: latest, params: [owned.runId, owned.runId] });
1809
+ return { existing, inserted, row };
1810
+ });
1811
+ const rows = await batch(statements);
1812
+ const lease = rows[0][0];
1813
+ const epoch = lease === undefined ? undefined : journalCounter(owned.runId, "epoch", lease[JOURNAL_COLUMNS.epoch]);
1814
+ if (lease?.[JOURNAL_COLUMNS.holderId] !== owned.holderId || epoch !== owned.epoch)
1815
+ throw new StaleLeaseError(owned.runId, owned.epoch, epoch ?? JOURNAL_LEASE.absentEpoch);
1816
+ return plans.map((plan) => {
1817
+ if (plan instanceof Error)
1818
+ return { status: "rejected", reason: plan };
1819
+ try {
1820
+ const existing = plan.existing === undefined ? undefined : rows[plan.existing][0];
1821
+ if (existing)
1822
+ return { status: "fulfilled", value: { status: "exists", entry: entryFromRow(existing) } };
1823
+ const row = rows[plan.row][0];
1824
+ if (Number(rows[plan.inserted][0].inserted) === 1)
1825
+ return { status: "fulfilled", value: { status: "appended", entry: entryFromRow(row) } };
1826
+ const actual = journalCounter(owned.runId, "seq", row?.[JOURNAL_COLUMNS.seq] ?? 0);
1827
+ journalCounter(owned.runId, "seq", actual + 1, "overflow");
1828
+ throw new StorageUnavailableError2(this.descriptor, new Error("The batched append was not written."));
1829
+ } catch (error) {
1830
+ return { status: "rejected", reason: error };
1831
+ }
1832
+ });
1833
+ }
1765
1834
  prepare(event, key, appendGuard) {
1766
1835
  if (key !== undefined)
1767
1836
  validateText("key", key);
@@ -3570,6 +3570,9 @@ class SqlRunJournal {
3570
3570
  return error;
3571
3571
  }
3572
3572
  });
3573
+ const batch = this.database.batch?.bind(this.database);
3574
+ if (batch && prepared.every((append) => append instanceof Error || append.guard === undefined && typeof append.payload === "string"))
3575
+ return this.operation(() => this.appendInOneRequest(batch, owned, prepared));
3573
3576
  return this.operation(() => this.database.transaction(async (tx) => {
3574
3577
  await this.checkLease(tx, owned);
3575
3578
  const results = [];
@@ -3590,6 +3593,72 @@ class SqlRunJournal {
3590
3593
  return results;
3591
3594
  }));
3592
3595
  }
3596
+ async appendInOneRequest(batch, owned, prepared) {
3597
+ const t = this.tables;
3598
+ const holds = `EXISTS (SELECT 1 FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.holderId} = ? AND ${JOURNAL_COLUMNS.epoch} = ?)`;
3599
+ const held = [owned.runId, owned.holderId, owned.epoch];
3600
+ const latest = `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.seq} = (SELECT MAX(${JOURNAL_COLUMNS.seq}) FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?)`;
3601
+ const statements = [
3602
+ { sql: `SELECT * FROM ${t.leases} WHERE ${JOURNAL_COLUMNS.runId} = ?`, params: [owned.runId] }
3603
+ ];
3604
+ const at = (statement) => statements.push(statement) - 1;
3605
+ const plans = prepared.map((append) => {
3606
+ if (append instanceof Error)
3607
+ return append;
3608
+ const { key, type, payload, status } = append;
3609
+ const existing = key === undefined ? undefined : at({
3610
+ sql: `SELECT * FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?`,
3611
+ params: [owned.runId, key]
3612
+ });
3613
+ at({
3614
+ sql: `INSERT INTO ${t.events}
3615
+ SELECT ?, current.latest + 1, ?, ?, ?, ?, ${STORE_NOW}
3616
+ FROM (SELECT COALESCE(MAX(${JOURNAL_COLUMNS.seq}), 0) AS latest FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ?) AS current
3617
+ WHERE current.latest < ? AND ${holds}${key === undefined ? "" : ` AND NOT EXISTS (SELECT 1 FROM ${t.events} WHERE ${JOURNAL_COLUMNS.runId} = ? AND ${JOURNAL_COLUMNS.key} = ?)`}`,
3618
+ params: [
3619
+ owned.runId,
3620
+ owned.epoch,
3621
+ key ?? null,
3622
+ type,
3623
+ payload,
3624
+ owned.runId,
3625
+ Number.MAX_SAFE_INTEGER,
3626
+ ...held,
3627
+ ...key === undefined ? [] : [owned.runId, key]
3628
+ ]
3629
+ });
3630
+ const inserted = at({ sql: "SELECT changes() AS inserted", params: [] });
3631
+ if (status !== undefined)
3632
+ at({
3633
+ sql: `UPDATE ${t.runs} SET ${JOURNAL_COLUMNS.status} = ? WHERE ${JOURNAL_COLUMNS.runId} = ? AND changes() = 1`,
3634
+ params: [status, owned.runId]
3635
+ });
3636
+ const row = at({ sql: latest, params: [owned.runId, owned.runId] });
3637
+ return { existing, inserted, row };
3638
+ });
3639
+ const rows = await batch(statements);
3640
+ const lease = rows[0][0];
3641
+ const epoch = lease === undefined ? undefined : journalCounter(owned.runId, "epoch", lease[JOURNAL_COLUMNS.epoch]);
3642
+ if (lease?.[JOURNAL_COLUMNS.holderId] !== owned.holderId || epoch !== owned.epoch)
3643
+ throw new StaleLeaseError(owned.runId, owned.epoch, epoch ?? JOURNAL_LEASE.absentEpoch);
3644
+ return plans.map((plan) => {
3645
+ if (plan instanceof Error)
3646
+ return { status: "rejected", reason: plan };
3647
+ try {
3648
+ const existing = plan.existing === undefined ? undefined : rows[plan.existing][0];
3649
+ if (existing)
3650
+ return { status: "fulfilled", value: { status: "exists", entry: entryFromRow(existing) } };
3651
+ const row = rows[plan.row][0];
3652
+ if (Number(rows[plan.inserted][0].inserted) === 1)
3653
+ return { status: "fulfilled", value: { status: "appended", entry: entryFromRow(row) } };
3654
+ const actual = journalCounter(owned.runId, "seq", row?.[JOURNAL_COLUMNS.seq] ?? 0);
3655
+ journalCounter(owned.runId, "seq", actual + 1, "overflow");
3656
+ throw new StorageUnavailableError2(this.descriptor, new Error("The batched append was not written."));
3657
+ } catch (error) {
3658
+ return { status: "rejected", reason: error };
3659
+ }
3660
+ });
3661
+ }
3593
3662
  prepare(event, key, appendGuard) {
3594
3663
  if (key !== undefined)
3595
3664
  validateText("key", key);
@@ -13,9 +13,20 @@ export interface StorageDescriptor {
13
13
  readonly adapter: string;
14
14
  readonly location: string;
15
15
  }
16
+ /** One statement of a batch: SQL with positional `?` parameters. */
17
+ export interface SqliteStatement {
18
+ readonly sql: string;
19
+ readonly params: readonly SqliteValue[];
20
+ }
16
21
  export interface SqliteDatabase extends SqliteExecutor {
17
22
  readonly descriptor: StorageDescriptor;
18
23
  transaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T>;
24
+ /**
25
+ * Runs the statements in order, in one write transaction, as one call to the database: all of them commit or none
26
+ * do. Resolves with each statement's rows. Present only when the driver can send a batch in one request, which
27
+ * saves a round trip per statement against a remote database.
28
+ */
29
+ batch?(statements: readonly SqliteStatement[]): Promise<SqliteRow[][]>;
19
30
  close(): Promise<void>;
20
31
  }
21
32
  //# sourceMappingURL=database.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/core/storage/database.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AACvE,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9E,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACjG,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACvF;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAe,SAAQ,cAAc;IACrD,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/core/storage/database.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AACvE,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC9E,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACjG,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACvF;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,cAAe,SAAQ,cAAc;IACrD,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClF;;;;OAIG;IACH,KAAK,CAAC,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACvE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/core/storage/database.ts"],"names":[],"mappings":"","sourcesContent":["export type SqliteValue = null | number | bigint | string | Uint8Array;\nexport type SqliteRow = Readonly<Record<string, SqliteValue>>;\n\nexport interface SqliteRunResult {\n\treadonly changes: number;\n}\n\nexport interface SqliteExecutor {\n\texec(sql: string): Promise<void>;\n\trun(sql: string, ...params: readonly SqliteValue[]): Promise<SqliteRunResult>;\n\tget<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T | undefined>;\n\tall<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T[]>;\n}\n\nexport interface StorageDescriptor {\n\treadonly adapter: string;\n\treadonly location: string;\n}\n\nexport interface SqliteDatabase extends SqliteExecutor {\n\treadonly descriptor: StorageDescriptor;\n\ttransaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T>;\n\tclose(): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/core/storage/database.ts"],"names":[],"mappings":"","sourcesContent":["export type SqliteValue = null | number | bigint | string | Uint8Array;\nexport type SqliteRow = Readonly<Record<string, SqliteValue>>;\n\nexport interface SqliteRunResult {\n\treadonly changes: number;\n}\n\nexport interface SqliteExecutor {\n\texec(sql: string): Promise<void>;\n\trun(sql: string, ...params: readonly SqliteValue[]): Promise<SqliteRunResult>;\n\tget<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T | undefined>;\n\tall<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T[]>;\n}\n\nexport interface StorageDescriptor {\n\treadonly adapter: string;\n\treadonly location: string;\n}\n\n/** One statement of a batch: SQL with positional `?` parameters. */\nexport interface SqliteStatement {\n\treadonly sql: string;\n\treadonly params: readonly SqliteValue[];\n}\n\nexport interface SqliteDatabase extends SqliteExecutor {\n\treadonly descriptor: StorageDescriptor;\n\ttransaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T>;\n\t/**\n\t * Runs the statements in order, in one write transaction, as one call to the database: all of them commit or none\n\t * do. Resolves with each statement's rows. Present only when the driver can send a batch in one request, which\n\t * saves a round trip per statement against a remote database.\n\t */\n\tbatch?(statements: readonly SqliteStatement[]): Promise<SqliteRow[][]>;\n\tclose(): Promise<void>;\n}\n"]}
@@ -1,4 +1,4 @@
1
- export type { SqliteDatabase, SqliteExecutor, SqliteRow, SqliteRunResult, SqliteValue, StorageDescriptor, } from "./database.js";
1
+ export type { SqliteDatabase, SqliteExecutor, SqliteRow, SqliteRunResult, SqliteStatement, SqliteValue, StorageDescriptor, } from "./database.js";
2
2
  export * from "./errors.js";
3
3
  export { openNodeSqliteDatabase, wrapNodeSqliteDatabase } from "./node-sqlite.js";
4
4
  export { renderStorageIdentifiers, validateStoragePrefix } from "./prefix.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/storage/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,cAAc,EACd,cAAc,EACd,SAAS,EACT,eAAe,EACf,WAAW,EACX,iBAAiB,GACjB,MAAM,eAAe,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC9E,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,gBAAgB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/storage/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,cAAc,EACd,cAAc,EACd,SAAS,EACT,eAAe,EACf,eAAe,EACf,WAAW,EACX,iBAAiB,GACjB,MAAM,eAAe,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC9E,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,gBAAgB,CAAC;AAC/B,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,cAAc,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/storage/index.ts"],"names":[],"mappings":"AAQA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC9E,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,gBAAgB,CAAC;AAE/B,cAAc,oBAAoB,CAAC","sourcesContent":["export type {\n\tSqliteDatabase,\n\tSqliteExecutor,\n\tSqliteRow,\n\tSqliteRunResult,\n\tSqliteValue,\n\tStorageDescriptor,\n} from \"./database.js\";\nexport * from \"./errors.js\";\nexport { openNodeSqliteDatabase, wrapNodeSqliteDatabase } from \"./node-sqlite.js\";\nexport { renderStorageIdentifiers, validateStoragePrefix } from \"./prefix.js\";\nexport * from \"./provider.js\";\nexport * from \"./run-journal.js\";\nexport { assertStorageSchemaVersion } from \"./schema.js\";\nexport * from \"./selection.js\";\nexport type { SessionStore } from \"./session/types.js\";\nexport * from \"./sqlite-facade.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/storage/index.ts"],"names":[],"mappings":"AASA,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC9E,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,gBAAgB,CAAC;AAE/B,cAAc,oBAAoB,CAAC","sourcesContent":["export type {\n\tSqliteDatabase,\n\tSqliteExecutor,\n\tSqliteRow,\n\tSqliteRunResult,\n\tSqliteStatement,\n\tSqliteValue,\n\tStorageDescriptor,\n} from \"./database.js\";\nexport * from \"./errors.js\";\nexport { openNodeSqliteDatabase, wrapNodeSqliteDatabase } from \"./node-sqlite.js\";\nexport { renderStorageIdentifiers, validateStoragePrefix } from \"./prefix.js\";\nexport * from \"./provider.js\";\nexport * from \"./run-journal.js\";\nexport { assertStorageSchemaVersion } from \"./schema.js\";\nexport * from \"./selection.js\";\nexport type { SessionStore } from \"./session/types.js\";\nexport * from \"./sqlite-facade.js\";\n"]}
@@ -1,4 +1,4 @@
1
- import type { SqliteDatabase, SqliteValue, StorageDescriptor } from "./database.js";
1
+ import type { SqliteDatabase, SqliteStatement, SqliteValue, StorageDescriptor } from "./database.js";
2
2
  type Awaitable<T> = T | Promise<T>;
3
3
  /** A value as a driver returns it. Blobs may be any binary view; the facade returns them as `Uint8Array` copies. */
4
4
  export type SqliteDriverValue = SqliteValue | ArrayBuffer | ArrayBufferView;
@@ -27,6 +27,11 @@ export interface SqliteDriver extends SqliteDriverExecutor {
27
27
  readonly descriptor: StorageDescriptor;
28
28
  /** Begins a write transaction that holds the database's write lock, like `BEGIN IMMEDIATE`. */
29
29
  beginTransaction(): Awaitable<SqliteDriverTransaction>;
30
+ /**
31
+ * Optional. Runs the statements in order in one write transaction, in one request: all commit or none do. Provide it
32
+ * only when the driver sends the whole batch at once; the facade then offers `SqliteDatabase.batch`.
33
+ */
34
+ batch?(statements: readonly SqliteStatement[]): Awaitable<readonly SqliteDriverResult[]>;
30
35
  /** Releases the connection. Called once, after accepted work drains. */
31
36
  close(): Awaitable<void>;
32
37
  /** Removes credentials from diagnostic text: the descriptor location and driver failure messages and stacks. */
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-facade.d.ts","sourceRoot":"","sources":["../../../src/core/storage/sqlite-facade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6B,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAW/G,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnC,oHAAoH;AACpH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC;AAE5E,6GAA6G;AAC7G,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACpF,4DAA4D;IAC5D,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACpE,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,+DAA+D;IAC/D,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAa,SAAQ,oBAAoB;IACzD,gGAAgG;IAChG,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,+FAA+F;IAC/F,gBAAgB,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACvD,wEAAwE;IACxE,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,gHAAgH;IAChH,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAajF;AAmKD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAEzE"}
1
+ {"version":3,"file":"sqlite-facade.d.ts","sourceRoot":"","sources":["../../../src/core/storage/sqlite-facade.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,cAAc,EAGd,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,MAAM,eAAe,CAAC;AAWvB,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnC,oHAAoH;AACpH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,GAAG,eAAe,CAAC;AAE5E,6GAA6G;AAC7G,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACpC,yDAAyD;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC,kBAAkB,CAAC,CAAC;IACpF,4DAA4D;IAC5D,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACpE,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,+DAA+D;IAC/D,KAAK,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAa,SAAQ,oBAAoB;IACzD,gGAAgG;IAChG,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,+FAA+F;IAC/F,gBAAgB,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACvD;;;OAGG;IACH,KAAK,CAAC,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzF,wEAAwE;IACxE,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,gHAAgH;IAChH,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAajF;AAwLD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAEzE"}
@@ -71,6 +71,7 @@ class DriverDatabase {
71
71
  queue = new CallQueue();
72
72
  closePromise;
73
73
  executor;
74
+ batch;
74
75
  constructor(driver, redact) {
75
76
  this.driver = driver;
76
77
  this.redact = redact;
@@ -80,6 +81,23 @@ class DriverDatabase {
80
81
  return Promise.reject(new StoreClosedError(this.descriptor));
81
82
  return this.queue.push(operation);
82
83
  });
84
+ const batch = driver.batch?.bind(driver);
85
+ if (batch)
86
+ this.batch = (statements) => this.runBatch(batch, statements);
87
+ }
88
+ runBatch(batch, statements) {
89
+ if (this.closePromise)
90
+ return Promise.reject(new StoreClosedError(this.descriptor));
91
+ const bound = statements.map((statement) => ({ sql: statement.sql, params: snapshotParams(statement.params) }));
92
+ return this.queue.push(async () => {
93
+ for (const statement of bound) {
94
+ const invalidIndex = statement.params.findIndex((value) => typeof value === "string" && value.includes("\0"));
95
+ if (invalidIndex !== -1)
96
+ throw new StorageInvalidTextError(invalidIndex);
97
+ }
98
+ const results = await this.call(() => batch(bound));
99
+ return results.map((result) => result.rows.map((row) => normalizeRow(row, result.columns)));
100
+ });
83
101
  }
84
102
  /** Maps a driver failure to `StorageUnavailableError` with a credential-free cause. */
85
103
  async call(operation) {
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-facade.js","sourceRoot":"","sources":["../../../src/core/storage/sqlite-facade.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SqliteDatabase, SqliteExecutor, SqliteRow, SqliteValue, StorageDescriptor } from \"./database.js\";\nimport {\n\tStorageError,\n\ttype StorageFailureCause,\n\tStorageInvalidTextError,\n\tStorageRollbackError,\n\tStorageTransactionClosedError,\n\tStorageUnavailableError,\n\tStoreClosedError,\n} from \"./errors.js\";\n\ntype Awaitable<T> = T | Promise<T>;\n\n/** A value as a driver returns it. Blobs may be any binary view; the facade returns them as `Uint8Array` copies. */\nexport type SqliteDriverValue = SqliteValue | ArrayBuffer | ArrayBufferView;\n\n/** One statement's result as a driver returns it: column names, positional rows, and the rows it changed. */\nexport interface SqliteDriverResult {\n\treadonly columns: readonly string[];\n\treadonly rows: readonly ArrayLike<SqliteDriverValue>[];\n\treadonly changes: number;\n}\n\n/** Runs SQL on a driver connection or on an open driver transaction. */\nexport interface SqliteDriverExecutor {\n\t/** Runs one statement with positional `?` parameters. */\n\texecute(sql: string, params: readonly SqliteValue[]): Awaitable<SqliteDriverResult>;\n\t/** Runs zero or more statements that take no parameters. */\n\texecuteMultiple(sql: string): Awaitable<void>;\n}\n\nexport interface SqliteDriverTransaction extends SqliteDriverExecutor {\n\tcommit(): Awaitable<void>;\n\trollback(): Awaitable<void>;\n\t/** Releases the transaction after it commits or rolls back. */\n\tclose?(): Awaitable<void>;\n}\n\n/** The driver calls `createSqliteDatabase` turns into a `SqliteDatabase`. */\nexport interface SqliteDriver extends SqliteDriverExecutor {\n\t/** Diagnostic identity. Its `location` passes through `redact` before the facade exposes it. */\n\treadonly descriptor: StorageDescriptor;\n\t/** Begins a write transaction that holds the database's write lock, like `BEGIN IMMEDIATE`. */\n\tbeginTransaction(): Awaitable<SqliteDriverTransaction>;\n\t/** Releases the connection. Called once, after accepted work drains. */\n\tclose(): Awaitable<void>;\n\t/** Removes credentials from diagnostic text: the descriptor location and driver failure messages and stacks. */\n\tredact?(text: string): string;\n}\n\n/**\n * A redactor that replaces every non-empty secret, and its URI-encoded and URI-decoded forms, with `[redacted]`.\n * Longer secrets are replaced first, so a secret that contains another is removed whole.\n */\nexport function redactSecrets(secrets: Iterable<string>): (text: string) => string {\n\tconst variants = new Set<string>();\n\tfor (const secret of secrets) {\n\t\tvariants.add(secret);\n\t\ttry {\n\t\t\tvariants.add(encodeURIComponent(secret));\n\t\t} catch {}\n\t\ttry {\n\t\t\tvariants.add(decodeURIComponent(secret));\n\t\t} catch {}\n\t}\n\tconst ordered = [...variants].filter(Boolean).sort((first, second) => second.length - first.length);\n\treturn (text) => ordered.reduce((value, secret) => value.replaceAll(secret, \"[redacted]\"), text);\n}\n\nfunction redactCause(cause: StorageFailureCause, redact: (text: string) => string): StorageFailureCause {\n\tif (typeof cause === \"string\") return redact(cause);\n\tif (!(cause instanceof Error)) return cause;\n\tconst nested = cause.cause as StorageFailureCause;\n\tconst safeCause = redactCause(nested, redact);\n\tconst message = redact(cause.message);\n\tconst stack = cause.stack === undefined ? undefined : redact(cause.stack);\n\tif (message === cause.message && stack === cause.stack && safeCause === nested) return cause;\n\tconst safe = new StorageError(message, { cause: safeCause });\n\tsafe.stack = stack;\n\treturn safe;\n}\n\nfunction snapshotParams(params: readonly SqliteValue[]): SqliteValue[] {\n\treturn params.map((value) => (value instanceof Uint8Array ? new Uint8Array(value) : value));\n}\n\nfunction normalizeValue(value: SqliteDriverValue): SqliteValue {\n\tif (typeof value === \"bigint\") {\n\t\treturn value >= -BigInt(Number.MAX_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER)\n\t\t\t? Number(value)\n\t\t\t: value;\n\t}\n\tif (value instanceof ArrayBuffer) return new Uint8Array(value.slice(0));\n\tif (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();\n\treturn value;\n}\n\nfunction normalizeRow(row: ArrayLike<SqliteDriverValue>, columns: readonly string[]): SqliteRow {\n\treturn Object.fromEntries(columns.map((column, index) => [column, normalizeValue(row[index] ?? null)]));\n}\n\n/** Serializes operations in call order; a failed operation does not stop the ones queued after it. */\nclass CallQueue {\n\tprivate tail: Promise<void> = Promise.resolve();\n\n\tpush<T>(operation: () => Promise<T>): Promise<T> {\n\t\tconst result = this.tail.then(operation);\n\t\tthis.tail = result.then(\n\t\t\t() => {},\n\t\t\t() => {},\n\t\t);\n\t\treturn result;\n\t}\n\n\tdrained(): Promise<void> {\n\t\treturn this.tail;\n\t}\n}\n\nclass DriverDatabase implements SqliteDatabase {\n\treadonly descriptor: StorageDescriptor;\n\tprivate readonly driver: SqliteDriver;\n\tprivate readonly redact: (text: string) => string;\n\tprivate readonly queue = new CallQueue();\n\tprivate closePromise?: Promise<void>;\n\tprivate readonly executor: SqliteExecutor;\n\n\tconstructor(driver: SqliteDriver, redact: (text: string) => string) {\n\t\tthis.driver = driver;\n\t\tthis.redact = redact;\n\t\tthis.descriptor = { adapter: driver.descriptor.adapter, location: redact(driver.descriptor.location) };\n\t\tthis.executor = this.createExecutor(driver, (operation) => {\n\t\t\tif (this.closePromise) return Promise.reject(new StoreClosedError(this.descriptor));\n\t\t\treturn this.queue.push(operation);\n\t\t});\n\t}\n\n\t/** Maps a driver failure to `StorageUnavailableError` with a credential-free cause. */\n\tprivate async call<T>(operation: () => Awaitable<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} catch (cause) {\n\t\t\tthrow new StorageUnavailableError(this.descriptor, redactCause(cause as StorageFailureCause, this.redact));\n\t\t}\n\t}\n\n\tprivate createExecutor(\n\t\tconnection: SqliteDriverExecutor,\n\t\tadmit: <T>(operation: () => Promise<T>) => Promise<T>,\n\t): SqliteExecutor {\n\t\tconst statement = (sql: string, params: readonly SqliteValue[]) => {\n\t\t\tconst bound = snapshotParams(params);\n\t\t\treturn admit(async () => {\n\t\t\t\tconst invalidIndex = bound.findIndex((value) => typeof value === \"string\" && value.includes(\"\\0\"));\n\t\t\t\tif (invalidIndex !== -1) throw new StorageInvalidTextError(invalidIndex);\n\t\t\t\treturn this.call(() => connection.execute(sql, bound));\n\t\t\t});\n\t\t};\n\t\treturn {\n\t\t\texec: (sql) => admit(() => this.call(() => connection.executeMultiple(sql))),\n\t\t\trun: async (sql, ...params) => ({ changes: (await statement(sql, params)).changes }),\n\t\t\tget: async <T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]) => {\n\t\t\t\tconst result = await statement(sql, params);\n\t\t\t\tconst row = result.rows[0];\n\t\t\t\treturn row === undefined ? undefined : (normalizeRow(row, result.columns) as T);\n\t\t\t},\n\t\t\tall: async <T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]) => {\n\t\t\t\tconst result = await statement(sql, params);\n\t\t\t\treturn result.rows.map((row) => normalizeRow(row, result.columns) as T);\n\t\t\t},\n\t\t};\n\t}\n\n\texec(sql: string): Promise<void> {\n\t\treturn this.executor.exec(sql);\n\t}\n\n\trun(sql: string, ...params: readonly SqliteValue[]) {\n\t\treturn this.executor.run(sql, ...params);\n\t}\n\n\tget<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T | undefined> {\n\t\treturn this.executor.get<T>(sql, ...params);\n\t}\n\n\tall<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T[]> {\n\t\treturn this.executor.all<T>(sql, ...params);\n\t}\n\n\ttransaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T> {\n\t\tif (this.closePromise) return Promise.reject(new StoreClosedError(this.descriptor));\n\t\treturn this.queue.push(async () => {\n\t\t\tconst connection = await this.call(() => this.driver.beginTransaction());\n\t\t\tconst handleQueue = new CallQueue();\n\t\t\tlet active = true;\n\t\t\tlet result: T;\n\t\t\tconst handle = this.createExecutor(connection, (operation) =>\n\t\t\t\tactive ? handleQueue.push(operation) : Promise.reject(new StorageTransactionClosedError()),\n\t\t\t);\n\t\t\tconst close = () => this.call(() => connection.close?.());\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tresult = await callback(handle);\n\t\t\t\t} finally {\n\t\t\t\t\tactive = false;\n\t\t\t\t\tawait handleQueue.drained();\n\t\t\t\t}\n\t\t\t\tawait this.call(() => connection.commit());\n\t\t\t} catch (cause) {\n\t\t\t\tlet failure = cause as StorageFailureCause;\n\t\t\t\ttry {\n\t\t\t\t\tawait this.call(() => connection.rollback());\n\t\t\t\t} catch (rollbackCause) {\n\t\t\t\t\tfailure = new StorageRollbackError(failure, rollbackCause as StorageFailureCause);\n\t\t\t\t}\n\t\t\t\t// Cleanup must not replace a callback, commit, or rollback failure.\n\t\t\t\tawait close().catch(() => {});\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t\tawait close();\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tclose(): Promise<void> {\n\t\tthis.closePromise ??= this.queue.drained().then(() => this.call(() => this.driver.close()));\n\t\treturn this.closePromise;\n\t}\n}\n\n/**\n * Wraps a SQLite-compatible driver as a `SqliteDatabase`. The facade queues operations in call order and keeps them\n * outside active transactions, snapshots binary parameters at call time, rejects NUL text, returns safe-range integers\n * as numbers and blobs as `Uint8Array` copies, expires transaction handles when their callback settles, maps driver\n * failures to `StorageUnavailableError` with credentials redacted, and reports failed rollbacks with\n * `StorageRollbackError`. Closing rejects new calls, drains accepted work, then closes the driver once.\n */\nexport function createSqliteDatabase(driver: SqliteDriver): SqliteDatabase {\n\treturn new DriverDatabase(driver, driver.redact?.bind(driver) ?? ((text) => text));\n}\n"]}
1
+ {"version":3,"file":"sqlite-facade.js","sourceRoot":"","sources":["../../../src/core/storage/sqlite-facade.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tSqliteDatabase,\n\tSqliteExecutor,\n\tSqliteRow,\n\tSqliteStatement,\n\tSqliteValue,\n\tStorageDescriptor,\n} from \"./database.js\";\nimport {\n\tStorageError,\n\ttype StorageFailureCause,\n\tStorageInvalidTextError,\n\tStorageRollbackError,\n\tStorageTransactionClosedError,\n\tStorageUnavailableError,\n\tStoreClosedError,\n} from \"./errors.js\";\n\ntype Awaitable<T> = T | Promise<T>;\n\n/** A value as a driver returns it. Blobs may be any binary view; the facade returns them as `Uint8Array` copies. */\nexport type SqliteDriverValue = SqliteValue | ArrayBuffer | ArrayBufferView;\n\n/** One statement's result as a driver returns it: column names, positional rows, and the rows it changed. */\nexport interface SqliteDriverResult {\n\treadonly columns: readonly string[];\n\treadonly rows: readonly ArrayLike<SqliteDriverValue>[];\n\treadonly changes: number;\n}\n\n/** Runs SQL on a driver connection or on an open driver transaction. */\nexport interface SqliteDriverExecutor {\n\t/** Runs one statement with positional `?` parameters. */\n\texecute(sql: string, params: readonly SqliteValue[]): Awaitable<SqliteDriverResult>;\n\t/** Runs zero or more statements that take no parameters. */\n\texecuteMultiple(sql: string): Awaitable<void>;\n}\n\nexport interface SqliteDriverTransaction extends SqliteDriverExecutor {\n\tcommit(): Awaitable<void>;\n\trollback(): Awaitable<void>;\n\t/** Releases the transaction after it commits or rolls back. */\n\tclose?(): Awaitable<void>;\n}\n\n/** The driver calls `createSqliteDatabase` turns into a `SqliteDatabase`. */\nexport interface SqliteDriver extends SqliteDriverExecutor {\n\t/** Diagnostic identity. Its `location` passes through `redact` before the facade exposes it. */\n\treadonly descriptor: StorageDescriptor;\n\t/** Begins a write transaction that holds the database's write lock, like `BEGIN IMMEDIATE`. */\n\tbeginTransaction(): Awaitable<SqliteDriverTransaction>;\n\t/**\n\t * Optional. Runs the statements in order in one write transaction, in one request: all commit or none do. Provide it\n\t * only when the driver sends the whole batch at once; the facade then offers `SqliteDatabase.batch`.\n\t */\n\tbatch?(statements: readonly SqliteStatement[]): Awaitable<readonly SqliteDriverResult[]>;\n\t/** Releases the connection. Called once, after accepted work drains. */\n\tclose(): Awaitable<void>;\n\t/** Removes credentials from diagnostic text: the descriptor location and driver failure messages and stacks. */\n\tredact?(text: string): string;\n}\n\n/**\n * A redactor that replaces every non-empty secret, and its URI-encoded and URI-decoded forms, with `[redacted]`.\n * Longer secrets are replaced first, so a secret that contains another is removed whole.\n */\nexport function redactSecrets(secrets: Iterable<string>): (text: string) => string {\n\tconst variants = new Set<string>();\n\tfor (const secret of secrets) {\n\t\tvariants.add(secret);\n\t\ttry {\n\t\t\tvariants.add(encodeURIComponent(secret));\n\t\t} catch {}\n\t\ttry {\n\t\t\tvariants.add(decodeURIComponent(secret));\n\t\t} catch {}\n\t}\n\tconst ordered = [...variants].filter(Boolean).sort((first, second) => second.length - first.length);\n\treturn (text) => ordered.reduce((value, secret) => value.replaceAll(secret, \"[redacted]\"), text);\n}\n\nfunction redactCause(cause: StorageFailureCause, redact: (text: string) => string): StorageFailureCause {\n\tif (typeof cause === \"string\") return redact(cause);\n\tif (!(cause instanceof Error)) return cause;\n\tconst nested = cause.cause as StorageFailureCause;\n\tconst safeCause = redactCause(nested, redact);\n\tconst message = redact(cause.message);\n\tconst stack = cause.stack === undefined ? undefined : redact(cause.stack);\n\tif (message === cause.message && stack === cause.stack && safeCause === nested) return cause;\n\tconst safe = new StorageError(message, { cause: safeCause });\n\tsafe.stack = stack;\n\treturn safe;\n}\n\nfunction snapshotParams(params: readonly SqliteValue[]): SqliteValue[] {\n\treturn params.map((value) => (value instanceof Uint8Array ? new Uint8Array(value) : value));\n}\n\nfunction normalizeValue(value: SqliteDriverValue): SqliteValue {\n\tif (typeof value === \"bigint\") {\n\t\treturn value >= -BigInt(Number.MAX_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER)\n\t\t\t? Number(value)\n\t\t\t: value;\n\t}\n\tif (value instanceof ArrayBuffer) return new Uint8Array(value.slice(0));\n\tif (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();\n\treturn value;\n}\n\nfunction normalizeRow(row: ArrayLike<SqliteDriverValue>, columns: readonly string[]): SqliteRow {\n\treturn Object.fromEntries(columns.map((column, index) => [column, normalizeValue(row[index] ?? null)]));\n}\n\n/** Serializes operations in call order; a failed operation does not stop the ones queued after it. */\nclass CallQueue {\n\tprivate tail: Promise<void> = Promise.resolve();\n\n\tpush<T>(operation: () => Promise<T>): Promise<T> {\n\t\tconst result = this.tail.then(operation);\n\t\tthis.tail = result.then(\n\t\t\t() => {},\n\t\t\t() => {},\n\t\t);\n\t\treturn result;\n\t}\n\n\tdrained(): Promise<void> {\n\t\treturn this.tail;\n\t}\n}\n\nclass DriverDatabase implements SqliteDatabase {\n\treadonly descriptor: StorageDescriptor;\n\tprivate readonly driver: SqliteDriver;\n\tprivate readonly redact: (text: string) => string;\n\tprivate readonly queue = new CallQueue();\n\tprivate closePromise?: Promise<void>;\n\tprivate readonly executor: SqliteExecutor;\n\treadonly batch?: (statements: readonly SqliteStatement[]) => Promise<SqliteRow[][]>;\n\n\tconstructor(driver: SqliteDriver, redact: (text: string) => string) {\n\t\tthis.driver = driver;\n\t\tthis.redact = redact;\n\t\tthis.descriptor = { adapter: driver.descriptor.adapter, location: redact(driver.descriptor.location) };\n\t\tthis.executor = this.createExecutor(driver, (operation) => {\n\t\t\tif (this.closePromise) return Promise.reject(new StoreClosedError(this.descriptor));\n\t\t\treturn this.queue.push(operation);\n\t\t});\n\t\tconst batch = driver.batch?.bind(driver);\n\t\tif (batch) this.batch = (statements) => this.runBatch(batch, statements);\n\t}\n\n\tprivate runBatch(\n\t\tbatch: NonNullable<SqliteDriver[\"batch\"]>,\n\t\tstatements: readonly SqliteStatement[],\n\t): Promise<SqliteRow[][]> {\n\t\tif (this.closePromise) return Promise.reject(new StoreClosedError(this.descriptor));\n\t\tconst bound = statements.map((statement) => ({ sql: statement.sql, params: snapshotParams(statement.params) }));\n\t\treturn this.queue.push(async () => {\n\t\t\tfor (const statement of bound) {\n\t\t\t\tconst invalidIndex = statement.params.findIndex(\n\t\t\t\t\t(value) => typeof value === \"string\" && value.includes(\"\\0\"),\n\t\t\t\t);\n\t\t\t\tif (invalidIndex !== -1) throw new StorageInvalidTextError(invalidIndex);\n\t\t\t}\n\t\t\tconst results = await this.call(() => batch(bound));\n\t\t\treturn results.map((result) => result.rows.map((row) => normalizeRow(row, result.columns)));\n\t\t});\n\t}\n\n\t/** Maps a driver failure to `StorageUnavailableError` with a credential-free cause. */\n\tprivate async call<T>(operation: () => Awaitable<T>): Promise<T> {\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} catch (cause) {\n\t\t\tthrow new StorageUnavailableError(this.descriptor, redactCause(cause as StorageFailureCause, this.redact));\n\t\t}\n\t}\n\n\tprivate createExecutor(\n\t\tconnection: SqliteDriverExecutor,\n\t\tadmit: <T>(operation: () => Promise<T>) => Promise<T>,\n\t): SqliteExecutor {\n\t\tconst statement = (sql: string, params: readonly SqliteValue[]) => {\n\t\t\tconst bound = snapshotParams(params);\n\t\t\treturn admit(async () => {\n\t\t\t\tconst invalidIndex = bound.findIndex((value) => typeof value === \"string\" && value.includes(\"\\0\"));\n\t\t\t\tif (invalidIndex !== -1) throw new StorageInvalidTextError(invalidIndex);\n\t\t\t\treturn this.call(() => connection.execute(sql, bound));\n\t\t\t});\n\t\t};\n\t\treturn {\n\t\t\texec: (sql) => admit(() => this.call(() => connection.executeMultiple(sql))),\n\t\t\trun: async (sql, ...params) => ({ changes: (await statement(sql, params)).changes }),\n\t\t\tget: async <T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]) => {\n\t\t\t\tconst result = await statement(sql, params);\n\t\t\t\tconst row = result.rows[0];\n\t\t\t\treturn row === undefined ? undefined : (normalizeRow(row, result.columns) as T);\n\t\t\t},\n\t\t\tall: async <T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]) => {\n\t\t\t\tconst result = await statement(sql, params);\n\t\t\t\treturn result.rows.map((row) => normalizeRow(row, result.columns) as T);\n\t\t\t},\n\t\t};\n\t}\n\n\texec(sql: string): Promise<void> {\n\t\treturn this.executor.exec(sql);\n\t}\n\n\trun(sql: string, ...params: readonly SqliteValue[]) {\n\t\treturn this.executor.run(sql, ...params);\n\t}\n\n\tget<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T | undefined> {\n\t\treturn this.executor.get<T>(sql, ...params);\n\t}\n\n\tall<T extends SqliteRow>(sql: string, ...params: readonly SqliteValue[]): Promise<T[]> {\n\t\treturn this.executor.all<T>(sql, ...params);\n\t}\n\n\ttransaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T> {\n\t\tif (this.closePromise) return Promise.reject(new StoreClosedError(this.descriptor));\n\t\treturn this.queue.push(async () => {\n\t\t\tconst connection = await this.call(() => this.driver.beginTransaction());\n\t\t\tconst handleQueue = new CallQueue();\n\t\t\tlet active = true;\n\t\t\tlet result: T;\n\t\t\tconst handle = this.createExecutor(connection, (operation) =>\n\t\t\t\tactive ? handleQueue.push(operation) : Promise.reject(new StorageTransactionClosedError()),\n\t\t\t);\n\t\t\tconst close = () => this.call(() => connection.close?.());\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tresult = await callback(handle);\n\t\t\t\t} finally {\n\t\t\t\t\tactive = false;\n\t\t\t\t\tawait handleQueue.drained();\n\t\t\t\t}\n\t\t\t\tawait this.call(() => connection.commit());\n\t\t\t} catch (cause) {\n\t\t\t\tlet failure = cause as StorageFailureCause;\n\t\t\t\ttry {\n\t\t\t\t\tawait this.call(() => connection.rollback());\n\t\t\t\t} catch (rollbackCause) {\n\t\t\t\t\tfailure = new StorageRollbackError(failure, rollbackCause as StorageFailureCause);\n\t\t\t\t}\n\t\t\t\t// Cleanup must not replace a callback, commit, or rollback failure.\n\t\t\t\tawait close().catch(() => {});\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t\tawait close();\n\t\t\treturn result;\n\t\t});\n\t}\n\n\tclose(): Promise<void> {\n\t\tthis.closePromise ??= this.queue.drained().then(() => this.call(() => this.driver.close()));\n\t\treturn this.closePromise;\n\t}\n}\n\n/**\n * Wraps a SQLite-compatible driver as a `SqliteDatabase`. The facade queues operations in call order and keeps them\n * outside active transactions, snapshots binary parameters at call time, rejects NUL text, returns safe-range integers\n * as numbers and blobs as `Uint8Array` copies, expires transaction handles when their callback settles, maps driver\n * failures to `StorageUnavailableError` with credentials redacted, and reports failed rollbacks with\n * `StorageRollbackError`. Closing rejects new calls, drains accepted work, then closes the driver once.\n */\nexport function createSqliteDatabase(driver: SqliteDriver): SqliteDatabase {\n\treturn new DriverDatabase(driver, driver.redact?.bind(driver) ?? ((text) => text));\n}\n"]}
package/docs/storage.md CHANGED
@@ -53,6 +53,8 @@ Then select it in `~/.atomic/agent/settings.json`:
53
53
  }
54
54
  ```
55
55
 
56
+ For a self-hosted sqld started with `--enable-namespaces`, add `"namespace": "<name>"` to `options`; the adapter sends it as the `x-namespace` header, so each namespace is its own database with its own token. `namespace` needs a `libsql:`, `https:`, or `http:` URL. SDK callers can also pass extra request `headers` to `openLibsqlDatabase`; their values are redacted from errors.
57
+
56
58
  Save the token with `turso db tokens create <db> > turso-token` and keep it readable only by you (`chmod 600`); a trailing newline is ignored. A relative `file` resolves against Atomic home, normally `~/.atomic`. Use `{ "env": "TURSO_TOKEN" }` instead to read it from an environment variable. For a local database file, use a `file:` URL such as `file:/home/me/shared.sqlite` and omit `secrets`. Every machine that uses the same `storage` block shares the same sessions, workflow runs, and Intercom routing; add a `prefix` to keep projects or teams apart. See [Sharing storage across hosts](#sharing-storage-across-hosts).
57
59
 
58
60
  Like any [adapter](#selecting-an-adapter), it runs with Atomic's privileges. To use a different driver, adapt it with [Bring your own driver](#bring-your-own-driver).
@@ -108,6 +110,7 @@ Atomic does not bundle remote database drivers. Install your chosen SQLite-compa
108
110
  - `executeMultiple(sql)`: runs zero or more statements that take no parameters.
109
111
  - `beginTransaction()`: begins a write transaction and returns its own `execute` and `executeMultiple`, plus `commit()`, `rollback()`, and an optional `close()` that releases it afterward.
110
112
  - `close()`: releases the connection.
113
+ - `batch(statements)`, optional: runs `{ sql, params }` statements in order in one write transaction, sent as one request, and returns each statement's result; all of them commit or none do. Provide it only when your driver sends the whole batch at once. The facade then offers `SqliteDatabase.batch`, and workflow journals write a batch of events with one request instead of one per statement.
111
114
  - `redact(text)`, optional: removes credentials from diagnostic text. `redactSecrets(secrets)` builds one that also removes each secret's URI-encoded and decoded forms, longest first.
112
115
 
113
116
  Driver calls may return values or promises. The returned facade follows the rules described above: it queues operations in call order and keeps them outside active transactions, snapshots binary parameters when a call is made, rejects NUL text with `StorageInvalidTextError` before SQL runs, returns safe-range integers as numbers and blobs as `Uint8Array` copies, expires transaction handles when their callback settles, and closes the driver once after draining accepted work. A failed driver call rejects with `StorageUnavailableError`, whose `cause` is the driver error with credentials redacted. A failed rollback rejects with `StorageRollbackError`, and callback failures are preserved. Do not fall back to memory after an opening failure.
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bastani/atomic",
9
- "version": "0.9.48",
9
+ "version": "0.9.49",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@bastani/atomic-natives": "0.9.48",
13
- "@bastani/pi-ai": "0.9.48",
12
+ "@bastani/atomic-natives": "0.9.49",
13
+ "@bastani/pi-ai": "0.9.49",
14
14
  "@earendil-works/pi-agent-core": "1.4.0",
15
15
  "@earendil-works/pi-codemode": "1.4.0",
16
16
  "@earendil-works/pi-tui": "1.4.0",
@@ -427,18 +427,18 @@
427
427
  }
428
428
  },
429
429
  "node_modules/@bastani/atomic-natives": {
430
- "version": "0.9.48",
431
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.48.tgz",
430
+ "version": "0.9.49",
431
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.49.tgz",
432
432
  "license": "MIT",
433
433
  "optionalDependencies": {
434
- "@bastani/atomic-natives-darwin-arm64": "0.9.48",
435
- "@bastani/atomic-natives-darwin-x64": "0.9.48",
436
- "@bastani/atomic-natives-linux-arm64-gnu": "0.9.48",
437
- "@bastani/atomic-natives-linux-arm64-musl": "0.9.48",
438
- "@bastani/atomic-natives-linux-x64-gnu": "0.9.48",
439
- "@bastani/atomic-natives-linux-x64-musl": "0.9.48",
440
- "@bastani/atomic-natives-win32-arm64-msvc": "0.9.48",
441
- "@bastani/atomic-natives-win32-x64-msvc": "0.9.48"
434
+ "@bastani/atomic-natives-darwin-arm64": "0.9.49",
435
+ "@bastani/atomic-natives-darwin-x64": "0.9.49",
436
+ "@bastani/atomic-natives-linux-arm64-gnu": "0.9.49",
437
+ "@bastani/atomic-natives-linux-arm64-musl": "0.9.49",
438
+ "@bastani/atomic-natives-linux-x64-gnu": "0.9.49",
439
+ "@bastani/atomic-natives-linux-x64-musl": "0.9.49",
440
+ "@bastani/atomic-natives-win32-arm64-msvc": "0.9.49",
441
+ "@bastani/atomic-natives-win32-x64-msvc": "0.9.49"
442
442
  },
443
443
  "engines": {
444
444
  "bun": ">=1.4.2",
@@ -446,8 +446,8 @@
446
446
  }
447
447
  },
448
448
  "node_modules/@bastani/atomic-natives-darwin-arm64": {
449
- "version": "0.9.48",
450
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.48.tgz",
449
+ "version": "0.9.49",
450
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.49.tgz",
451
451
  "license": "MIT",
452
452
  "os": [
453
453
  "darwin"
@@ -458,8 +458,8 @@
458
458
  "optional": true
459
459
  },
460
460
  "node_modules/@bastani/atomic-natives-darwin-x64": {
461
- "version": "0.9.48",
462
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.48.tgz",
461
+ "version": "0.9.49",
462
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.49.tgz",
463
463
  "license": "MIT",
464
464
  "os": [
465
465
  "darwin"
@@ -470,8 +470,8 @@
470
470
  "optional": true
471
471
  },
472
472
  "node_modules/@bastani/atomic-natives-linux-arm64-gnu": {
473
- "version": "0.9.48",
474
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.48.tgz",
473
+ "version": "0.9.49",
474
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.49.tgz",
475
475
  "license": "MIT",
476
476
  "os": [
477
477
  "linux"
@@ -485,8 +485,8 @@
485
485
  "optional": true
486
486
  },
487
487
  "node_modules/@bastani/atomic-natives-linux-arm64-musl": {
488
- "version": "0.9.48",
489
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.48.tgz",
488
+ "version": "0.9.49",
489
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.49.tgz",
490
490
  "license": "MIT",
491
491
  "os": [
492
492
  "linux"
@@ -500,8 +500,8 @@
500
500
  "optional": true
501
501
  },
502
502
  "node_modules/@bastani/atomic-natives-linux-x64-gnu": {
503
- "version": "0.9.48",
504
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.48.tgz",
503
+ "version": "0.9.49",
504
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.49.tgz",
505
505
  "license": "MIT",
506
506
  "os": [
507
507
  "linux"
@@ -515,8 +515,8 @@
515
515
  "optional": true
516
516
  },
517
517
  "node_modules/@bastani/atomic-natives-linux-x64-musl": {
518
- "version": "0.9.48",
519
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.48.tgz",
518
+ "version": "0.9.49",
519
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.49.tgz",
520
520
  "license": "MIT",
521
521
  "os": [
522
522
  "linux"
@@ -530,8 +530,8 @@
530
530
  "optional": true
531
531
  },
532
532
  "node_modules/@bastani/atomic-natives-win32-arm64-msvc": {
533
- "version": "0.9.48",
534
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.48.tgz",
533
+ "version": "0.9.49",
534
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.49.tgz",
535
535
  "license": "MIT",
536
536
  "os": [
537
537
  "win32"
@@ -542,8 +542,8 @@
542
542
  "optional": true
543
543
  },
544
544
  "node_modules/@bastani/atomic-natives-win32-x64-msvc": {
545
- "version": "0.9.48",
546
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.48.tgz",
545
+ "version": "0.9.49",
546
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.49.tgz",
547
547
  "license": "MIT",
548
548
  "os": [
549
549
  "win32"
@@ -554,8 +554,8 @@
554
554
  "optional": true
555
555
  },
556
556
  "node_modules/@bastani/pi-ai": {
557
- "version": "0.9.48",
558
- "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.48.tgz",
557
+ "version": "0.9.49",
558
+ "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.49.tgz",
559
559
  "license": "MIT",
560
560
  "dependencies": {
561
561
  "@anthropic-ai/sdk": "0.129.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.48",
3
+ "version": "0.9.49",
4
4
  "description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "atomicConfig": {
@@ -89,8 +89,8 @@
89
89
  "prepublishOnly": "bun run clean && bun run build && bun run shrinkwrap"
90
90
  },
91
91
  "dependencies": {
92
- "@bastani/atomic-natives": "0.9.48",
93
- "@bastani/pi-ai": "0.9.48",
92
+ "@bastani/atomic-natives": "0.9.49",
93
+ "@bastani/pi-ai": "0.9.49",
94
94
  "@earendil-works/pi-agent-core": "1.4.0",
95
95
  "@earendil-works/pi-codemode": "1.4.0",
96
96
  "@earendil-works/pi-tui": "1.4.0",