@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 +70 -38
- package/dist/adapters/capacitorSqliteAdapter.d.ts +42 -0
- package/dist/adapters/capacitorSqliteAdapter.d.ts.map +1 -1
- package/dist/adapters/capacitorSqliteAdapter.js +139 -21
- package/dist/adapters/capacitorSqliteAdapter.js.map +1 -1
- package/dist/adapters/indexedDbAdapter.d.ts +89 -10
- package/dist/adapters/indexedDbAdapter.d.ts.map +1 -1
- package/dist/adapters/indexedDbAdapter.js +265 -58
- package/dist/adapters/indexedDbAdapter.js.map +1 -1
- package/dist/adapters/postgresAdapter.d.ts +83 -5
- package/dist/adapters/postgresAdapter.d.ts.map +1 -1
- package/dist/adapters/postgresAdapter.js +285 -18
- package/dist/adapters/postgresAdapter.js.map +1 -1
- package/dist/core/database.d.ts +37 -8
- package/dist/core/database.d.ts.map +1 -1
- package/dist/core/database.js +48 -11
- package/dist/core/database.js.map +1 -1
- package/dist/core/resolver.d.ts +34 -5
- package/dist/core/resolver.d.ts.map +1 -1
- package/dist/core/resolver.js +59 -9
- package/dist/core/resolver.js.map +1 -1
- package/dist/shared/connectionGate.d.ts +32 -9
- package/dist/shared/connectionGate.d.ts.map +1 -1
- package/dist/shared/connectionGate.js +31 -12
- package/dist/shared/connectionGate.js.map +1 -1
- package/dist/shared/postgresTarget.d.ts +10 -0
- package/dist/shared/postgresTarget.d.ts.map +1 -0
- package/dist/shared/postgresTarget.js +13 -0
- package/dist/shared/postgresTarget.js.map +1 -0
- package/package.json +2 -1
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
|
-
|
|
124
|
-
|
|
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` |
|
|
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
|
|
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 {
|
|
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:
|
|
338
|
-
const
|
|
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
|
|
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 {
|
|
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
|
-
|
|
391
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"capacitorSqliteAdapter.js","sourceRoot":"","sources":["../../src/adapters/capacitorSqliteAdapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EACL,iCAAiC,EACjC,wCAAwC,GACzC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAsB,MAAM,0BAA0B,CAAC;AAIlG,mFAAmF;AACnF,MAAM,YAAY,GAAG,KAAM,CAAC;AAa5B,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAkC,EAAE;IAC3E,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,SAAS,GAAG,KAAuC,CAAC;IAC1D,OAAO,OAAO,SAAS,CAAC,gBAAgB,KAAK,UAAU,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,KAAK,IAA2C,EAAE;IAC5E,IAAI,CAAC;QACH,MAAM,GAAG,GAAY,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QAEjE,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,GAAG,CAAC;QACb,CAAC;QAED,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,GAGnD,CAAC;YAEF,IAAI,iBAAiB,CAAC,aAAa,CAAC,EAAE,CAAC;gBACrC,OAAO,aAAa,CAAC;YACvB,CAAC;YACD,IAAI,iBAAiB,CAAC,eAAe,CAAC,EAAE,CAAC;gBACvC,OAAO,eAAe,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,oEAAoE,EAAE,KAAK,CAAC,CAAC;QAC1F,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,GAAG,CAAC,MAAe,EAAoB,EAAE;IACxD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAI,EAAE,CAA4C,CAAC;IACxE,MAAM,KAAK,GACT,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI;QACzD,CAAC,CAAE,KAAK,CAAC,OAAmD;QAC5D,CAAC,CAAC,KAAK,CAAC;IACZ,MAAM,OAAO,GAAG,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,OAAO;QACL,OAAO;QACP,eAAe,EAAE,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;KAC1F,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,OAAO,sBAAsB;IA4BjC,YAA6B,UAAmC,EAAE;QAArC,YAAO,GAAP,OAAO,CAA8B;QA3BlD,SAAI,GAAG,kBAAkB,CAAC;QAC1B,iBAAY,GAAmC,IAAI,GAAG,CAAoB;YACxF,cAAc;YACd,KAAK;YACL,OAAO;YACP,aAAa;SACd,CAAC,CAAC;QAEK,WAAM,GAAiC,IAAI,CAAC;QAC5C,eAAU,GAA8B,IAAI,CAAC;QAErD,8EAA8E;QAC7D,6BAAwB,GAAG,iCAAiC,EAAE,CAAC;QAChF;;;;WAIG;QACc,SAAI,GAAG,oBAAoB,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/E,qFAAqF;QACpE,aAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;QAChD,qBAAgB,GAAG,KAAK,CAAC;QAChB,aAAQ,GAAkB;YACzC,OAAO,EAAE,YAAY;YACrB,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE;SACxC,CAAC;QAGA,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC1C,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAA4B;QAC5C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,+FAA+F,CAAC,CAAC;QACnH,CAAC;QAED,MAAM,cAAc,GAAG,OAAO,EAAE,cAAoD,CAAC;QACrF,MAAM,aAAa,GAAG,OAAO,cAAc,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACzG,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QAC7C,MAAM,cAAc,GAAG,aAAa,IAAI,cAAc,IAAI,IAAI,CAAC,MAAM,CAAC;QAEtE,IAAI,CAAC,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;QACvG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAE7B,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,SAAiB,EAAE,UAA8B;QAChE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvH,CAAC;IAEM,KAAK,CAAC,GAAG,CAAI,SAAiB,EAAE,UAA8B;QACnE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAI,SAAS,EAAE,UAAU,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC;IAEM,KAAK,CAAC,GAAG,CAAI,SAAiB,EAAE,UAA8B;QACnE,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAI,SAAS,EAAE,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpH,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,MAAc;QAC9B,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzG,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,WAAW,CAAI,EAAuC;QACjE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,YAAY,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,wBAAwB,CAAC,GAAG,EAAE;YACxC,YAAY,CAAC,MAAM,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;gBACpC,oEAAoE;gBACpE,wCAAwC;gBACxC,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACrC,uEAAuE;gBACvE,6BAA6B;gBAC7B,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC9B,MAAM,kBAAkB,GAAG,wCAAwC,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClG,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,kBAAkB,CAAC,CAAC;oBAC5C,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBAC/B,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,IAAI,CAAC;wBACH,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;oBACnC,CAAC;oBAAC,MAAM,CAAC;wBACP,iCAAiC;oBACnC,CAAC;oBACD,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,gBAAgB,CAAC,KAAa;QACpC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;YACpB,KAAK,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;YACrB,GAAG,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC;YAC/G,GAAG,EAAE,KAAK,EAAK,SAAiB,EAAE,UAA8B,EAAE,EAAE;gBAClE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAI,SAAS,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;gBAC/F,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1C,CAAC;YACD,GAAG,EAAE,CAAI,SAAiB,EAAE,UAA8B,EAAE,EAAE,CAC5D,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAI,SAAS,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC;YAC7E,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC;YACnF,yEAAyE;YACzE,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;SAC9B,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,MAAM,CAClB,SAAiB,EACjB,UAAyC,EACzC,cAAuB;QAEvB,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,MAAM,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QACvD,OAAO,WAAW,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;IAClF,CAAC;IAEO,KAAK,CAAC,MAAM,CAAI,SAAiB,EAAE,UAA8B;QACvE,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,MAAM,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAQ,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,cAAuB;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,CAAC;IAED,wFAAwF;IAChF,KAAK,CAAI,IAAgB;QAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,cAAc;QACpB,IAAI,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAClC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,OAAO,CAAC,IAAI,CACV,4DAA4D,YAAY,GAAG,IAAI,qCAAqC;YAClH,6GAA6G;YAC7G,qEAAqE,CACxE,CAAC;IACJ,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,OAAiC,EAAkB,EAAE,CAChG,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC"}
|