@framers/sql-storage-adapter 0.6.13 → 0.6.15

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/README.md CHANGED
@@ -119,9 +119,11 @@ const desktopDb = await createDatabase({ priority: ['better-sqlite3', 'sqljs'] }
119
119
  // Mobile (Capacitor): Uses native SQLite
120
120
  const mobileDb = await createDatabase({ priority: ['capacitor', 'indexeddb'] });
121
121
 
122
- // Cloud (Node): Uses PostgreSQL
123
- const cloudDb = await createDatabase({
124
- postgres: { connectionString: process.env.DATABASE_URL }
122
+ // Cloud (Node): Uses PostgreSQL. strict fails when DATABASE_URL is unset,
123
+ // where it would otherwise open a local SQLite file.
124
+ const cloudDb = await createDatabase({
125
+ url: process.env.DATABASE_URL,
126
+ strict: true
125
127
  });
126
128
  ```
127
129
 
@@ -131,13 +133,13 @@ See [Platform Strategy Guide](./PLATFORM_STRATEGY.md) for detailed pros/cons and
131
133
 
132
134
  | Adapter | Package | Ideal for | Pros | Considerations |
133
135
  | --- | --- | --- | --- | --- |
134
- | **🆕 `electron`** | bundled | **Electron desktop apps** | IPC bridge, multi-window, WAL, auto-migrations, crash recovery | Requires Electron runtime |
136
+ | **🆕 `electron`** | bundled | **Electron desktop apps** | IPC bridge, multi-window, WAL, auto-migrations, crash recovery | Requires Electron runtime; create it from `@framers/sql-storage-adapter/electron` (the resolver does not build the `electron-main` and `electron-renderer` kinds) |
135
137
  | **🆕 `indexeddb`** | bundled (sql.js) | **Browsers, PWAs** | sql.js + IndexedDB persistence wrapper, browser-native storage, 50MB-1GB+ quota, offline-first | IndexedDB quotas vary, WASM overhead (sql.js), not a separate SQL engine |
136
138
  | `better-sqlite3` | `better-sqlite3` | Node/Electron, CLI, CI | Native performance, transactional semantics, WAL support | Needs native toolchain; version must match Node ABI; 13.x needs Node-API 10 (Node.js 22.14+, Electron 35+) |
137
139
  | `postgres` | `pg` | Hosted or on-prem PostgreSQL | Connection pooling, rich SQL features, cloud friendly | Requires `DATABASE_URL`/credentials |
138
140
  | `sqljs` | bundled | Browsers, serverless edge, native fallback | Pure WASM SQLite, no native deps, optional filesystem persistence | Write performance limited vs native, in-memory by default |
139
141
  | `capacitor` | `@capacitor-community/sqlite` | Mobile (iOS/Android, Capacitor) | Native SQLite on mobile, encryption | Requires Capacitor runtime |
140
- | `memory` | built-in | Unit tests, storybooks, constrained sandboxes | Zero dependencies, instant startup | Non-durable, single-process only |
142
+ | `memory` | bundled (sql.js) | Unit tests, storybooks, constrained sandboxes | Zero dependencies, instant startup | The resolver builds the sql.js adapter for it, as for `sqljs`: in memory in a browser, persisted to the resolver's file path on Node |
141
143
 
142
144
  ### Platform Priorities
143
145
 
@@ -147,7 +149,7 @@ See [Platform Strategy Guide](./PLATFORM_STRATEGY.md) for detailed pros/cons and
147
149
  | **Electron (Desktop)** | electron | better-sqlite3 | Desktop apps, dev tools |
148
150
  | **Capacitor (Mobile)** | capacitor | IndexedDB | iOS/Android native apps |
149
151
  | **Node.js** | better-sqlite3 | Postgres, sql.js | CLI tools, local servers |
150
- | **Cloud (Serverless)** | Postgres | better-sqlite3 | Multi-tenant SaaS, APIs |
152
+ | **Cloud (Serverless)** | Postgres | None with `strict` or `createDatabase({ url })`; better-sqlite3 from `resolveStorageAdapter()` otherwise | Multi-tenant SaaS, APIs |
151
153
 
152
154
  ## Electron Adapter
153
155
 
@@ -243,9 +245,60 @@ await sync.connect();
243
245
  - environment variables (`STORAGE_ADAPTER`, `DATABASE_URL`),
244
246
  - runtime hints (Capacitor detection, browser globals, IndexedDB availability).
245
247
  - Adapters are attempted in priority order until one opens successfully; a `StorageResolutionError` includes the full failure chain.
248
+ - The resolver builds `postgres`, `better-sqlite3`, `capacitor`, `indexeddb` and `sqljs`, and builds sql.js for `memory`. Any other kind (`electron-main`, `electron-renderer`, `supabase`, or an unknown name, from `priority` or `STORAGE_ADAPTER`) fails as a candidate with an error that names it, and the next candidate is tried.
249
+ - On Node, `createDatabase({ url })`, `createDatabase({ postgres })` and `connectDatabase()` open PostgreSQL, with every Postgres option (pool size, SSL, timeouts, application name, or host, port, database, user and password without a connection string) passed on, and do not fall back to SQLite. In a browser and on Deno, `createDatabase()` opens sql.js whatever the options say. `DATABASE_URL` in the environment does not change what `createDatabase()` picks; `resolveStorageAdapter()` without a `priority` tries Postgres first when it is set.
250
+ - `strict: true` (on `createDatabase` and `resolveStorageAdapter`) is for servers: only the database the options name opens. A postgres candidate that fails ends resolution with a `StorageResolutionError`, whatever `priority` lists after it; with `DATABASE_URL` or Postgres options that name a server and no `priority`, `['postgres']` is the whole list; an explicit file path (`file`, `filePath`) outranks `DATABASE_URL`; and a `url` or `postgres` option that names no server fails instead of opening the local default, unless a file is given. Without `strict`, `resolveStorageAdapter()` falls back from a failed Postgres to better-sqlite3, IndexedDB and sql.js, and `DATABASE_URL` comes before a `filePath`.
246
251
  - Provide `priority: ['indexeddb', 'sqljs']` for browser bundles or tests where native modules shouldn't load.
247
252
  - Use `createCloudBackupManager` for S3-compatible backups with gzip compression and retention limits.
248
253
 
254
+ ### Transactions
255
+
256
+ `transaction(fn)` commits when `fn` resolves and rolls back when it throws. Run the callback's statements through the handle it receives:
257
+
258
+ ```typescript
259
+ await db.transaction(async (trx) => {
260
+ await trx.run('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 'a']);
261
+ await trx.run('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 'b']);
262
+ });
263
+ ```
264
+
265
+ The IndexedDB, Capacitor and better-sqlite3 adapters run every transaction on one connection, so they run transactions one at a time, and a statement from another caller waits until the open transaction ends: it neither joins the transaction (and vanishes in its rollback) nor reads what the transaction has not committed. A browser or a WebView has no async context to tell the callback's own statements apart, so on the IndexedDB and Capacitor adapters a statement the callback issues through the adapter itself, not through its handle, waits for the transaction too. The two then wait on each other, and the adapter logs a warning after 10 seconds. (On Node, better-sqlite3 tells them apart with `AsyncLocalStorage`.) Inside a callback, run every statement through the handle, and do not await work that itself needs the database.
266
+
267
+ The PostgreSQL adapter runs each transaction on its own pooled connection, so transactions run side by side. When the connection breaks during a transaction and its ROLLBACK fails too, `transaction()` still throws the transaction's own error (the callback's, or COMMIT's), with the ROLLBACK error on it as a non-enumerable `rollbackError` property. The broken connection is closed, not returned to the pool. A connection the server closes while a callback holds it, between statements, does not end the process. Once a transaction has ended, its handle runs on the pool, as the adapter's own statements do, and its `transaction()` begins a new transaction.
268
+
269
+
270
+ ### PostgreSQL pool control
271
+
272
+ `PostgresAdapter` holds a `pg` pool. Narrow an adapter to it with `db instanceof PostgresAdapter` (or `db.kind === 'postgres'`) to reach these:
273
+
274
+ ```typescript
275
+ import { createDatabase, PostgresAdapter } from '@framers/sql-storage-adapter';
276
+
277
+ const db = await createDatabase({ url: process.env.DATABASE_URL, strict: true, postgres: { max: 10, allowExitOnIdle: true } });
278
+
279
+ if (db instanceof PostgresAdapter) {
280
+ // One connection for the callback: a session advisory lock, then statements on the same session
281
+ await db.withConnection(async (connection) => {
282
+ await connection.get('SELECT pg_advisory_lock(?)', [42]);
283
+ try {
284
+ await connection.run('UPDATE jobs SET owner = ? WHERE owner IS NULL', ['worker-1']);
285
+ } finally {
286
+ await connection.get('SELECT pg_advisory_unlock(?)', [42]);
287
+ }
288
+ });
289
+
290
+ // Isolation level and read-only and deferrable modes
291
+ await db.transaction(async (trx) => trx.all('SELECT * FROM ledger'), { isolationLevel: 'serializable', readOnly: true, deferrable: true });
292
+
293
+ // { total, idle, waiting }, or null before open() and after close()
294
+ console.log(db.getPoolStats());
295
+ }
296
+ ```
297
+
298
+ - `withConnection(fn)` returns the connection to the pool when `fn` resolves, with whatever session state `fn` left (undo `SET` and release session locks before returning, or use `SET LOCAL` inside a transaction). When `fn` throws, the connection is closed, and `fn`'s error is rethrown. When `fn` returns inside a transaction it began and did not end, the connection is closed (rolling it back) and the call rejects; pg 8.21 and later report the transaction status this needs. The connection refuses statements once `fn` has ended, and `connection.transaction()` runs one transaction at a time.
299
+ - `transaction(fn, options)` refuses an isolation level outside `'read uncommitted'`, `'read committed'`, `'repeatable read'` and `'serializable'` before it checks out a connection.
300
+ - `allowExitOnIdle` is pg's option: Node can exit while every pooled connection is idle. Without it, idle connections keep the process running until `idleTimeoutMillis` (10 seconds by default) closes them.
301
+
249
302
  ### IndexedDB-Specific Config
250
303
 
251
304
  ```typescript
@@ -269,6 +322,8 @@ await adapter.open();
269
322
  - ✅ Export/import (Uint8Array SQLite file format)
270
323
  - ✅ Auto-save with batching (reduce IDB overhead)
271
324
 
325
+ **When it saves:** every `saveIntervalMs`, the adapter writes the database image to IndexedDB if anything changed. It skips the tick while a transaction is open, its own or one begun with `BEGIN`, because sql.js's export closes and reopens the connection, which would end the transaction; the next tick saves it once it has committed. A save counts as done when IndexedDB commits it, not when the put request succeeds. A save that fails or aborts is reported once, and the next tick tries again. A write made while a save is in flight is saved by the next one. A transaction's commit marks its writes for saving, including writes made through `all()` or `get()` (`INSERT ... RETURNING`). `close()` rolls back a transaction still open and saves the committed state. `importDatabase()` saves the imported image before it resolves. `exportDatabase()` throws while a transaction is open.
326
+
272
327
  **Why IndexedDB Adapter vs sql.js Adapter?**
273
328
 
274
329
  | Feature | IndexedDB Adapter | sql.js Adapter |
@@ -317,34 +372,13 @@ See [**PLATFORM_STRATEGY.md**](./PLATFORM_STRATEGY.md) for a comprehensive guide
317
372
 
318
373
  ## Performance Tiers & Cost Optimization
319
374
 
320
- The adapter supports configurable performance tiers for different use cases:
375
+ The package exports performance-tier types, the tier presets (`TIER_DEFAULTS`) and `resolvePerformanceConfig()`, which turns a tier and overrides into settings. `createDatabase()` takes no `performance` option, and no adapter applies a tier: a host that wants one reads the settings and acts on them itself.
321
376
 
322
377
  ```typescript
323
- import { createDatabase } from '@framers/sql-storage-adapter';
324
-
325
- // Development: Fast tier - prioritize speed
326
- const devDb = await createDatabase({
327
- type: 'memory',
328
- performance: { tier: 'fast' }
329
- });
330
-
331
- // Production: Balanced tier (default)
332
- const prodDb = await createDatabase({
333
- priority: ['indexeddb', 'sqljs'],
334
- performance: { tier: 'balanced' }
335
- });
378
+ import { resolvePerformanceConfig } from '@framers/sql-storage-adapter';
336
379
 
337
- // Analytics/Reporting: Accurate tier - no caching, full validation
338
- const analyticsDb = await createDatabase({
339
- postgres: { connectionString: process.env.DATABASE_URL },
340
- performance: { tier: 'accurate', trackMetrics: true }
341
- });
342
-
343
- // Mobile: Efficient tier - battery optimization
344
- const mobileDb = await createDatabase({
345
- priority: ['capacitor', 'indexeddb'],
346
- performance: { tier: 'efficient', batchWrites: true }
347
- });
380
+ // Analytics/Reporting: the accurate tier's settings (no caching, full validation)
381
+ const settings = resolvePerformanceConfig({ tier: 'accurate' });
348
382
  ```
349
383
 
350
384
  | Tier | Caching | Batching | Validation | Use Case |
@@ -358,10 +392,10 @@ See [**guides/OPTIMIZATION_GUIDE.md**](./guides/OPTIMIZATION_GUIDE.md) for detai
358
392
 
359
393
  ## Lifecycle Hooks
360
394
 
361
- The adapter provides lifecycle hooks for extending behavior:
395
+ The package exports the `StorageHooks` types and the `combineHooks()` and `filterHooks()` helpers. `createDatabase()` takes no `hooks` option, and no adapter runs hooks: a host that wants them calls them around its own adapter calls.
362
396
 
363
397
  ```typescript
364
- import { createDatabase, type StorageHooks } from '@framers/sql-storage-adapter';
398
+ import { combineHooks, type StorageHooks } from '@framers/sql-storage-adapter';
365
399
 
366
400
  const myHooks: StorageHooks = {
367
401
  // Log all writes
@@ -387,10 +421,8 @@ const myHooks: StorageHooks = {
387
421
  }
388
422
  };
389
423
 
390
- const db = await createDatabase({
391
- performance: { tier: 'balanced' },
392
- hooks: myHooks
393
- });
424
+ // One StorageHooks object from several; the host calls its hooks itself
425
+ const hooks = combineHooks(myHooks);
394
426
  ```
395
427
 
396
428
  ### Available Hooks
@@ -503,7 +535,7 @@ On Postgres, `features.exporter` requires a Node runtime plus an adapter that wa
503
535
  ## CI, Releases, and Badges
504
536
 
505
537
  - GitHub Actions workflows:
506
- - `ci.yml` runs on pushes and pull requests to `master`: lint, type check, tests with coverage and the build, then it packs the package, installs the tarball into a new ES module project and type-checks a consumer with `--module nodenext --moduleResolution nodenext`.
538
+ - `ci.yml` runs on pushes and pull requests to `master`: lint, type check, tests with coverage and the build, then it packs the package, installs the tarball into a new ES module project and type-checks a consumer with `--module nodenext --moduleResolution nodenext`. The job starts a PostgreSQL 16 service, and the specs that need a server read its URL from `POSTGRES_TEST_URL`.
507
539
  - `release.yml` publishes new versions to npm, tags the commit (`vX.Y.Z`), and creates/updates the GitHub Release when `CHANGELOG.md` and `package.json` bump the version.
508
540
  - Check badge health whenever builds fail:
509
541
  - CI badge should be green for `master`.
@@ -11,6 +11,12 @@ export interface CapacitorAdapterOptions {
11
11
  }
12
12
  /**
13
13
  * Adapter using @capacitor-community/sqlite for native mobile targets.
14
+ *
15
+ * Transactions use the plugin's beginTransaction, commitTransaction and
16
+ * rollbackTransaction (plugin 5.0.7 and later). The plugin runs every other
17
+ * execute and run call in a transaction of its own, which cannot nest in an
18
+ * open one, so the statements of a transaction's callback go to the plugin
19
+ * with `transaction` false.
14
20
  */
15
21
  export declare class CapacitorSqliteAdapter implements StorageAdapter {
16
22
  private readonly options;
@@ -19,14 +25,50 @@ export declare class CapacitorSqliteAdapter implements StorageAdapter {
19
25
  private plugin;
20
26
  private connection;
21
27
  private dbName;
28
+ /** The connection is shared, so transaction() runs one callback at a time. */
29
+ private readonly runSerializedTransaction;
30
+ /**
31
+ * Holds statements from outside an open transaction until it ends. A WebView
32
+ * has no async context to tell the callback's own statements apart, so the
33
+ * callback's handle is the only way into an open transaction.
34
+ */
35
+ private readonly gate;
36
+ /** Statements sent to the plugin from outside a transaction and not yet answered. */
37
+ private readonly inFlight;
38
+ private longWaitReported;
39
+ private readonly longWait;
22
40
  constructor(options?: CapacitorAdapterOptions);
23
41
  open(options?: StorageOpenOptions): Promise<void>;
24
42
  run(statement: string, parameters?: StorageParameters): Promise<StorageRunResult>;
25
43
  get<T>(statement: string, parameters?: StorageParameters): Promise<T | null>;
26
44
  all<T>(statement: string, parameters?: StorageParameters): Promise<T[]>;
27
45
  exec(script: string): Promise<void>;
46
+ /**
47
+ * Executes a callback within a database transaction.
48
+ *
49
+ * Callbacks run one at a time, since every transaction shares the one
50
+ * connection. The callback's statements go through the handle it receives;
51
+ * a statement issued through the adapter itself while the callback runs
52
+ * waits until the transaction ends, so a callback must not await one.
53
+ */
28
54
  transaction<T>(fn: (trx: StorageAdapter) => Promise<T>): Promise<T>;
29
55
  close(): Promise<void>;
56
+ /**
57
+ * The statements of one transaction's handle: they carry the transaction's
58
+ * token, so the gate admits them while the transaction is open, and run
59
+ * inside the plugin's open transaction.
60
+ */
61
+ private handleStatements;
62
+ /**
63
+ * @param ownTransaction - true for a statement from outside a transaction:
64
+ * the plugin runs it in a transaction of its own, as it always has.
65
+ */
66
+ private runNow;
67
+ private allNow;
68
+ private execNow;
69
+ /** Keeps a statement sent from outside a transaction in `inFlight` until it answers. */
70
+ private track;
71
+ private reportLongWait;
30
72
  private ensureConnection;
31
73
  }
32
74
  export declare const createCapacitorSqliteAdapter: (options?: CapacitorAdapterOptions) => StorageAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"capacitorSqliteAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/capacitorSqliteAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAK3B,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAuCD;;GAEG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAa/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZpC,SAAgB,IAAI,sBAAsB;IAC1C,SAAgB,YAAY,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAKzD;IAEH,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,MAAM,CAAS;gBAEM,OAAO,GAAE,uBAA4B;IAIrD,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAOjF,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAK5E,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAOvE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAanE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC,OAAO,CAAC,gBAAgB;CAMzB;AAED,eAAO,MAAM,4BAA4B,aAAc,uBAAuB,KAAG,cAC5C,CAAC"}
1
+ {"version":3,"file":"capacitorSqliteAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/capacitorSqliteAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAa3B,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAyDD;;;;;;;;GAQG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IA4B/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IA3BpC,SAAgB,IAAI,sBAAsB;IAC1C,SAAgB,YAAY,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAKzD;IAEH,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,UAAU,CAAmC;IACrD,OAAO,CAAC,MAAM,CAAS;IACvB,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAChF;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0D;IAC/E,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IACxD,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGvB;gBAE2B,OAAO,GAAE,uBAA4B;IAIrD,IAAI,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIjF,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAK5E,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAIvE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD;;;;;;;OAOG;IACU,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA8BnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;OAGG;YACW,MAAM;YAUN,MAAM;YAON,OAAO;IAKrB,wFAAwF;IACxF,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,gBAAgB;CAMzB;AAED,eAAO,MAAM,4BAA4B,aAAc,uBAAuB,KAAG,cAC5C,CAAC"}
@@ -1,4 +1,8 @@
1
1
  import { normaliseParameters } from '../shared/parameterUtils.js';
2
+ import { createSerializedTransactionRunner, createSharedConnectionTransactionAdapter, } from '../shared/serializedTransaction.js';
3
+ import { createConnectionGate, whenAdmitted } from '../shared/connectionGate.js';
4
+ /** How long a statement waits for an open transaction before the adapter warns. */
5
+ const LONG_WAIT_MS = 10000;
2
6
  const isCapacitorPlugin = (value) => {
3
7
  if (!value || typeof value !== 'object') {
4
8
  return false;
@@ -28,8 +32,30 @@ const loadCapacitorSqlite = async () => {
28
32
  return null;
29
33
  }
30
34
  };
35
+ /**
36
+ * The plugin answers run() with `{ changes: { changes, lastId } }`
37
+ * (`capSQLiteChanges`); a flat `{ changes, lastId }` is read as well.
38
+ */
39
+ const toRunResult = (answer) => {
40
+ const outer = (answer ?? {});
41
+ const inner = typeof outer.changes === 'object' && outer.changes !== null
42
+ ? outer.changes
43
+ : outer;
44
+ const changes = typeof inner.changes === 'number' ? inner.changes : 0;
45
+ const lastId = inner.lastId;
46
+ return {
47
+ changes,
48
+ lastInsertRowid: typeof lastId === 'number' || typeof lastId === 'string' ? lastId : null,
49
+ };
50
+ };
31
51
  /**
32
52
  * Adapter using @capacitor-community/sqlite for native mobile targets.
53
+ *
54
+ * Transactions use the plugin's beginTransaction, commitTransaction and
55
+ * rollbackTransaction (plugin 5.0.7 and later). The plugin runs every other
56
+ * execute and run call in a transaction of its own, which cannot nest in an
57
+ * open one, so the statements of a transaction's callback go to the plugin
58
+ * with `transaction` false.
33
59
  */
34
60
  export class CapacitorSqliteAdapter {
35
61
  constructor(options = {}) {
@@ -43,6 +69,21 @@ export class CapacitorSqliteAdapter {
43
69
  ]);
44
70
  this.plugin = null;
45
71
  this.connection = null;
72
+ /** The connection is shared, so transaction() runs one callback at a time. */
73
+ this.runSerializedTransaction = createSerializedTransactionRunner();
74
+ /**
75
+ * Holds statements from outside an open transaction until it ends. A WebView
76
+ * has no async context to tell the callback's own statements apart, so the
77
+ * callback's handle is the only way into an open transaction.
78
+ */
79
+ this.gate = createConnectionGate(null, { withoutContext: 'wait' });
80
+ /** Statements sent to the plugin from outside a transaction and not yet answered. */
81
+ this.inFlight = new Set();
82
+ this.longWaitReported = false;
83
+ this.longWait = {
84
+ afterMs: LONG_WAIT_MS,
85
+ onLongWait: () => this.reportLongWait(),
86
+ };
46
87
  this.dbName = options.database ?? 'app';
47
88
  }
48
89
  async open(options) {
@@ -69,37 +110,56 @@ export class CapacitorSqliteAdapter {
69
110
  }
70
111
  }
71
112
  async run(statement, parameters) {
72
- const conn = this.ensureConnection();
73
- const { positional } = normaliseParameters(parameters);
74
- const result = await conn.run(statement, positional ?? []);
75
- return { changes: result.changes ?? 0, lastInsertRowid: result.lastId ?? null };
113
+ return whenAdmitted(this.gate, () => this.track(this.runNow(statement, parameters, true)), undefined, this.longWait);
76
114
  }
77
115
  async get(statement, parameters) {
78
116
  const rows = await this.all(statement, parameters);
79
117
  return rows.length > 0 ? rows[0] : null;
80
118
  }
81
119
  async all(statement, parameters) {
82
- const conn = this.ensureConnection();
83
- const { positional } = normaliseParameters(parameters);
84
- const result = await conn.query(statement, positional ?? []);
85
- return (result.values ?? []);
120
+ return whenAdmitted(this.gate, () => this.track(this.allNow(statement, parameters)), undefined, this.longWait);
86
121
  }
87
122
  async exec(script) {
88
- const conn = this.ensureConnection();
89
- await conn.execute(script);
123
+ return whenAdmitted(this.gate, () => this.track(this.execNow(script, true)), undefined, this.longWait);
90
124
  }
125
+ /**
126
+ * Executes a callback within a database transaction.
127
+ *
128
+ * Callbacks run one at a time, since every transaction shares the one
129
+ * connection. The callback's statements go through the handle it receives;
130
+ * a statement issued through the adapter itself while the callback runs
131
+ * waits until the transaction ends, so a callback must not await one.
132
+ */
91
133
  async transaction(fn) {
92
- const conn = this.ensureConnection();
93
- await conn.execute('BEGIN TRANSACTION;');
94
- try {
95
- const result = await fn(this);
96
- await conn.execute('COMMIT;');
97
- return result;
98
- }
99
- catch (error) {
100
- await conn.execute('ROLLBACK;');
101
- throw error;
102
- }
134
+ this.ensureConnection();
135
+ const queued = setTimeout(() => this.reportLongWait(), LONG_WAIT_MS);
136
+ return this.runSerializedTransaction(() => {
137
+ clearTimeout(queued);
138
+ return this.gate.hold(async (token) => {
139
+ // The gate now holds new statements back; let the ones already sent
140
+ // answer before the transaction begins.
141
+ await Promise.allSettled([...this.inFlight]);
142
+ const conn = this.ensureConnection();
143
+ // Outside the try: when beginTransaction fails, no transaction of this
144
+ // call is open to roll back.
145
+ await conn.beginTransaction();
146
+ const transactionAdapter = createSharedConnectionTransactionAdapter(this.handleStatements(token));
147
+ try {
148
+ const result = await fn(transactionAdapter);
149
+ await conn.commitTransaction();
150
+ return result;
151
+ }
152
+ catch (error) {
153
+ try {
154
+ await conn.rollbackTransaction();
155
+ }
156
+ catch {
157
+ // The transaction already ended.
158
+ }
159
+ throw error;
160
+ }
161
+ });
162
+ });
103
163
  }
104
164
  async close() {
105
165
  if (this.connection) {
@@ -107,6 +167,64 @@ export class CapacitorSqliteAdapter {
107
167
  this.connection = null;
108
168
  }
109
169
  }
170
+ /**
171
+ * The statements of one transaction's handle: they carry the transaction's
172
+ * token, so the gate admits them while the transaction is open, and run
173
+ * inside the plugin's open transaction.
174
+ */
175
+ handleStatements(token) {
176
+ return {
177
+ kind: this.kind,
178
+ capabilities: this.capabilities,
179
+ open: async () => { },
180
+ close: async () => { },
181
+ run: (statement, parameters) => whenAdmitted(this.gate, () => this.runNow(statement, parameters, false), token),
182
+ get: async (statement, parameters) => {
183
+ const rows = await whenAdmitted(this.gate, () => this.allNow(statement, parameters), token);
184
+ return rows.length > 0 ? rows[0] : null;
185
+ },
186
+ all: (statement, parameters) => whenAdmitted(this.gate, () => this.allNow(statement, parameters), token),
187
+ exec: (script) => whenAdmitted(this.gate, () => this.execNow(script, false), token),
188
+ // The handle answers a nested transaction() itself and never calls this.
189
+ transaction: (fn) => fn(this),
190
+ };
191
+ }
192
+ /**
193
+ * @param ownTransaction - true for a statement from outside a transaction:
194
+ * the plugin runs it in a transaction of its own, as it always has.
195
+ */
196
+ async runNow(statement, parameters, ownTransaction) {
197
+ const conn = this.ensureConnection();
198
+ const { positional } = normaliseParameters(parameters);
199
+ return toRunResult(await conn.run(statement, positional ?? [], ownTransaction));
200
+ }
201
+ async allNow(statement, parameters) {
202
+ const conn = this.ensureConnection();
203
+ const { positional } = normaliseParameters(parameters);
204
+ const result = await conn.query(statement, positional ?? []);
205
+ return (result.values ?? []);
206
+ }
207
+ async execNow(script, ownTransaction) {
208
+ const conn = this.ensureConnection();
209
+ await conn.execute(script, ownTransaction);
210
+ }
211
+ /** Keeps a statement sent from outside a transaction in `inFlight` until it answers. */
212
+ track(sent) {
213
+ this.inFlight.add(sent);
214
+ const forget = () => {
215
+ this.inFlight.delete(sent);
216
+ };
217
+ sent.then(forget, forget);
218
+ return sent;
219
+ }
220
+ reportLongWait() {
221
+ if (this.longWaitReported)
222
+ return;
223
+ this.longWaitReported = true;
224
+ console.warn(`[StorageAdapter] A Capacitor SQLite statement has waited ${LONG_WAIT_MS / 1000} s for an open transaction to end. ` +
225
+ "Inside a transaction() callback, run statements through the callback's handle: one run through the adapter " +
226
+ 'itself waits for the transaction, and the transaction waits for it.');
227
+ }
110
228
  ensureConnection() {
111
229
  if (!this.connection) {
112
230
  throw new Error('Storage adapter not opened. Call open() before executing statements.');
@@ -1 +1 @@
1
- {"version":3,"file":"capacitorSqliteAdapter.js","sourceRoot":"","sources":["../../src/adapters/capacitorSqliteAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capacitorSqliteAdapter.js","sourceRoot":"","sources":["../../src/adapters/capacitorSqliteAdapter.ts"],"names":[],"mappings":"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"}