@quereus/plugin-nativescript-sqlite 4.10.0 → 4.12.0
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 +34 -2
- package/dist/src/provider.d.ts +46 -15
- package/dist/src/provider.d.ts.map +1 -1
- package/dist/src/provider.js +115 -47
- package/dist/src/provider.js.map +1 -1
- package/dist/src/store.d.ts +56 -1
- package/dist/src/store.d.ts.map +1 -1
- package/dist/src/store.js +87 -25
- package/dist/src/store.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -15,6 +15,37 @@ SQLite storage plugin for Quereus on NativeScript. Provides persistent storage f
|
|
|
15
15
|
- **ACID transactions**: SQLite transactions for atomic batch writes
|
|
16
16
|
- **WITHOUT ROWID**: Optimized key-value table structure
|
|
17
17
|
|
|
18
|
+
## Table naming
|
|
19
|
+
|
|
20
|
+
Each logical store — a table's row data, or one of its secondary indexes — gets its own
|
|
21
|
+
SQLite table. The name comes from `@quereus/store`'s shared builders and is then escaped
|
|
22
|
+
into a legal bare SQLite identifier: digits and `a`-`z` pass through, every other byte
|
|
23
|
+
becomes `_XX` (uppercase hex of the UTF-8 byte). `_` is therefore always an escape
|
|
24
|
+
introducer and never a literal, which is what makes the escape reversible in principle — so
|
|
25
|
+
two differently-named tables can never land on one SQLite table.
|
|
26
|
+
|
|
27
|
+
| Logical store | SQLite table |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| table `t` in schema `main` | `quereus_main_2Et` |
|
|
30
|
+
| table `a-b` in schema `main` | `quereus_main_2Ea_2Db` |
|
|
31
|
+
| table `a b` in schema `main` | `quereus_main_2Ea_20b` |
|
|
32
|
+
| index `x` on table `t` | `quereus_main_2Et_5Fidx_5Fx` |
|
|
33
|
+
| table `café` in schema `main` | `quereus_main_2Ecaf_C3_A9` |
|
|
34
|
+
| unified stats | `quereus___stats__` |
|
|
35
|
+
| catalog (DDL) | `quereus___catalog__` |
|
|
36
|
+
|
|
37
|
+
The two reserved tables are deliberately **not** escaped, which is what keeps them
|
|
38
|
+
unreachable from any user table: an escaped name can never contain a bare `_` followed by a
|
|
39
|
+
non-hex character, so no table name can produce `__stats__` or `__catalog__`. The
|
|
40
|
+
`quereus_` part is the configurable `tablePrefix`.
|
|
41
|
+
|
|
42
|
+
> **Hard cutover (no on-disk migration).** An earlier version named its tables
|
|
43
|
+
> `{prefix}{schema}_{table}` with every character outside `[a-zA-Z0-9_]` folded to `_`.
|
|
44
|
+
> That mapping was many-to-one — tables `a-b` and `a b` shared one SQLite table and
|
|
45
|
+
> interleaved their rows — so it has been replaced outright. Databases written by the older
|
|
46
|
+
> version are **not** read by this version and must be re-created. Pre-1.0 dev data is
|
|
47
|
+
> expected to be thrown away; there is no migration importer.
|
|
48
|
+
|
|
18
49
|
## Installation
|
|
19
50
|
|
|
20
51
|
```bash
|
|
@@ -134,7 +165,8 @@ import { createSQLiteProvider } from '@quereus/plugin-nativescript-sqlite';
|
|
|
134
165
|
const sqliteDb = openOrCreate('quereus.db');
|
|
135
166
|
const provider = createSQLiteProvider({ db: sqliteDb });
|
|
136
167
|
|
|
137
|
-
// Each store gets its own table:
|
|
168
|
+
// Each store gets its own table: quereus_main_2Eusers, quereus_main_2Eorders, etc.
|
|
169
|
+
// (see "Table naming" above for how the name is escaped)
|
|
138
170
|
const userStore = await provider.getStore('main', 'users');
|
|
139
171
|
const orderStore = await provider.getStore('main', 'orders');
|
|
140
172
|
const catalogStore = await provider.getCatalogStore();
|
|
@@ -171,7 +203,7 @@ Keys and values are stored as BLOBs (binary). SQLite compares BLOBs using `memcm
|
|
|
171
203
|
Each store creates a table with this schema:
|
|
172
204
|
|
|
173
205
|
```sql
|
|
174
|
-
create table
|
|
206
|
+
create table quereus_main_2Eusers (
|
|
175
207
|
key blob primary key,
|
|
176
208
|
value blob
|
|
177
209
|
) without rowid
|
package/dist/src/provider.d.ts
CHANGED
|
@@ -4,11 +4,25 @@
|
|
|
4
4
|
* Manages SQLite-backed KV stores for the StoreModule.
|
|
5
5
|
* Uses a single SQLite database with multiple tables (one per logical store).
|
|
6
6
|
*
|
|
7
|
-
* Storage naming convention
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* {prefix}
|
|
7
|
+
* Storage naming convention. Every logical store name comes from the shared builders
|
|
8
|
+
* (`buildDataStoreName` / `buildIndexStoreName`), then {@link encodeSqliteName} escapes it
|
|
9
|
+
* into a legal bare SQLite identifier:
|
|
10
|
+
*
|
|
11
|
+
* {prefix}{encoded {schema}.{table}} - Data store (row data)
|
|
12
|
+
* {prefix}{encoded {schema}.{table}_idx_{name}} - Index store (secondary indexes)
|
|
13
|
+
* {prefix}__stats__ - Unified stats store (row counts)
|
|
14
|
+
* {prefix}__catalog__ - Catalog store (DDL metadata)
|
|
15
|
+
*
|
|
16
|
+
* e.g. table `users` in schema `main` → `quereus_main_2Eusers`.
|
|
17
|
+
*
|
|
18
|
+
* The two reserved stores are NOT run through the encoder, and that is exactly what keeps
|
|
19
|
+
* them in a namespace no table can reach — see {@link encodeSqliteName}.
|
|
20
|
+
*
|
|
21
|
+
* HARD CUTOVER: an earlier version derived its own table names (`{prefix}{schema}_{table}`,
|
|
22
|
+
* with every character outside `[a-zA-Z0-9_]` folded to `_`). Those tables are NOT read
|
|
23
|
+
* here and databases written by it must be re-created; there is no on-disk migration. The
|
|
24
|
+
* old scheme was many-to-one, so two differently-named tables could share one SQLite table
|
|
25
|
+
* and interleave their rows.
|
|
12
26
|
*/
|
|
13
27
|
import type { KVStore, KVStoreProvider } from '@quereus/store';
|
|
14
28
|
import { type SQLiteDatabase } from './store.js';
|
|
@@ -40,15 +54,6 @@ export declare class SQLiteProvider implements KVStoreProvider {
|
|
|
40
54
|
private catalogStore;
|
|
41
55
|
private statsStore;
|
|
42
56
|
constructor(options: SQLiteProviderOptions);
|
|
43
|
-
/**
|
|
44
|
-
* Get the table name for a store.
|
|
45
|
-
* Sanitizes schema/table names to valid SQLite identifiers.
|
|
46
|
-
*/
|
|
47
|
-
private getTableName;
|
|
48
|
-
/**
|
|
49
|
-
* Get the key for the store cache.
|
|
50
|
-
*/
|
|
51
|
-
private getStoreKey;
|
|
52
57
|
getStore(schemaName: string, tableName: string, _options?: Record<string, unknown>): Promise<KVStore>;
|
|
53
58
|
getIndexStore(schemaName: string, tableName: string, indexName: string): Promise<KVStore>;
|
|
54
59
|
getStatsStore(_schemaName: string, _tableName: string): Promise<KVStore>;
|
|
@@ -58,8 +63,34 @@ export declare class SQLiteProvider implements KVStoreProvider {
|
|
|
58
63
|
closeAll(): Promise<void>;
|
|
59
64
|
deleteIndexStore(schemaName: string, tableName: string, indexName: string): Promise<void>;
|
|
60
65
|
deleteTableStores(schemaName: string, tableName: string, indexNames: readonly string[]): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* The SQLite table backing a logical store.
|
|
68
|
+
*
|
|
69
|
+
* The ONE derivation, shared by every caller: a second hand-derived spelling of this is
|
|
70
|
+
* precisely the defect class the naming work had to remove from this file once (a store
|
|
71
|
+
* opened under one name and reclaimed under another leaves the rows in place).
|
|
72
|
+
*/
|
|
73
|
+
private physicalTableName;
|
|
74
|
+
/**
|
|
75
|
+
* Get or open the store for a logical store name, caching by that CANONICAL name —
|
|
76
|
+
* the same string the shared builders produce and `StoreModule` reasons about. Keying
|
|
77
|
+
* the cache by anything else (a separately-derived spelling) risks two cache entries
|
|
78
|
+
* for one physical table, or one entry shared by two.
|
|
79
|
+
*/
|
|
61
80
|
private getOrCreateStore;
|
|
62
|
-
private
|
|
81
|
+
private closeStoreByName;
|
|
82
|
+
/**
|
|
83
|
+
* Erase a store by dropping its backing table, after closing and evicting any cached
|
|
84
|
+
* handle — so a store re-opened later under the same name is EMPTY, which is the reclaim
|
|
85
|
+
* contract on {@link KVStoreProvider.deleteTableStores}. `if exists` makes deleting a
|
|
86
|
+
* store that was never created a no-op that creates nothing, as that contract also asks.
|
|
87
|
+
*
|
|
88
|
+
* The table name is interpolated rather than bound because SQLite cannot parameterize an
|
|
89
|
+
* identifier; {@link encodeSqliteName} restricts every produced name to `[0-9a-z_]` plus
|
|
90
|
+
* the uppercase hex of its escapes, so there is no quoting or injection surface — the
|
|
91
|
+
* same reason `SQLiteStore` interpolates it.
|
|
92
|
+
*/
|
|
93
|
+
private dropStoreByName;
|
|
63
94
|
}
|
|
64
95
|
/**
|
|
65
96
|
* Create a SQLite provider with the given options.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA2C9D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,EAAE,EAAE,cAAc,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,eAAe;IACrD,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,UAAU,CAA4B;gBAElC,OAAO,EAAE,qBAAqB;IAKpC,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrG,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzF,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQxE,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAOnC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxF,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBzB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzF,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5G;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;YAWV,gBAAgB;IAQ9B;;;;;;;;;;OAUG;YACW,eAAe;CAQ7B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAEnF"}
|
package/dist/src/provider.js
CHANGED
|
@@ -4,14 +4,67 @@
|
|
|
4
4
|
* Manages SQLite-backed KV stores for the StoreModule.
|
|
5
5
|
* Uses a single SQLite database with multiple tables (one per logical store).
|
|
6
6
|
*
|
|
7
|
-
* Storage naming convention
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* {prefix}
|
|
7
|
+
* Storage naming convention. Every logical store name comes from the shared builders
|
|
8
|
+
* (`buildDataStoreName` / `buildIndexStoreName`), then {@link encodeSqliteName} escapes it
|
|
9
|
+
* into a legal bare SQLite identifier:
|
|
10
|
+
*
|
|
11
|
+
* {prefix}{encoded {schema}.{table}} - Data store (row data)
|
|
12
|
+
* {prefix}{encoded {schema}.{table}_idx_{name}} - Index store (secondary indexes)
|
|
13
|
+
* {prefix}__stats__ - Unified stats store (row counts)
|
|
14
|
+
* {prefix}__catalog__ - Catalog store (DDL metadata)
|
|
15
|
+
*
|
|
16
|
+
* e.g. table `users` in schema `main` → `quereus_main_2Eusers`.
|
|
17
|
+
*
|
|
18
|
+
* The two reserved stores are NOT run through the encoder, and that is exactly what keeps
|
|
19
|
+
* them in a namespace no table can reach — see {@link encodeSqliteName}.
|
|
20
|
+
*
|
|
21
|
+
* HARD CUTOVER: an earlier version derived its own table names (`{prefix}{schema}_{table}`,
|
|
22
|
+
* with every character outside `[a-zA-Z0-9_]` folded to `_`). Those tables are NOT read
|
|
23
|
+
* here and databases written by it must be re-created; there is no on-disk migration. The
|
|
24
|
+
* old scheme was many-to-one, so two differently-named tables could share one SQLite table
|
|
25
|
+
* and interleave their rows.
|
|
12
26
|
*/
|
|
13
|
-
import {
|
|
27
|
+
import { buildDataStoreName, buildIndexStoreName, CATALOG_STORE_NAME, STATS_STORE_NAME } from '@quereus/store';
|
|
14
28
|
import { SQLiteStore } from './store.js';
|
|
29
|
+
const textEncoder = new TextEncoder();
|
|
30
|
+
/**
|
|
31
|
+
* Escape a logical store name into a legal bare SQLite identifier.
|
|
32
|
+
*
|
|
33
|
+
* Bytes `0`-`9` and `a`-`z` pass through; every other byte becomes `_XX` (uppercase hex of
|
|
34
|
+
* the UTF-8 byte). `_` is therefore ALWAYS an escape introducer and never a literal, which
|
|
35
|
+
* is what makes the mapping injective: the output can be parsed back unambiguously, so two
|
|
36
|
+
* different store names can never produce the same identifier. That matters because
|
|
37
|
+
* `StoreModule.assertStoreNameFree` compares LOGICAL names and only stands in for a
|
|
38
|
+
* physical collision check while this mapping is one-to-one.
|
|
39
|
+
*
|
|
40
|
+
* Percent-escaping (what `@quereus/plugin-leveldb` uses) is deliberately not reused here:
|
|
41
|
+
* `%` is illegal in a bare SQLite identifier, and `SQLiteStore` interpolates the table name
|
|
42
|
+
* straight into its SQL unquoted — so escaping into `[a-z0-9_]` keeps every produced name a
|
|
43
|
+
* legal bare identifier and adds no quoting/injection surface.
|
|
44
|
+
*
|
|
45
|
+
* Two properties the callers rely on:
|
|
46
|
+
*
|
|
47
|
+
* - Injective under SQLite's ASCII-case-insensitive identifier comparison too. The input
|
|
48
|
+
* is already lowercased by the shared builders, so no ASCII uppercase survives to be
|
|
49
|
+
* escaped and the escape hex is the only uppercase in the output; case-folding it back
|
|
50
|
+
* to `_2e` still decodes to the same byte.
|
|
51
|
+
* - Disjoint from the reserved store names. `{prefix}__stats__` and `{prefix}__catalog__`
|
|
52
|
+
* are used UNESCAPED; an encoded name can never contain a bare `_` followed by a
|
|
53
|
+
* non-hex character, so no table name can produce `__stats__` (its second `_` is not a
|
|
54
|
+
* hex digit) and no table can spoof a reserved store. Do not run the reserved names
|
|
55
|
+
* through this function.
|
|
56
|
+
*/
|
|
57
|
+
function encodeSqliteName(name) {
|
|
58
|
+
let out = '';
|
|
59
|
+
for (const byte of textEncoder.encode(name)) {
|
|
60
|
+
const isDigit = byte >= 0x30 && byte <= 0x39;
|
|
61
|
+
const isLower = byte >= 0x61 && byte <= 0x7a;
|
|
62
|
+
out += (isDigit || isLower)
|
|
63
|
+
? String.fromCharCode(byte)
|
|
64
|
+
: '_' + byte.toString(16).toUpperCase().padStart(2, '0');
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
15
68
|
/**
|
|
16
69
|
* SQLite implementation of KVStoreProvider for NativeScript.
|
|
17
70
|
*
|
|
@@ -28,28 +81,11 @@ export class SQLiteProvider {
|
|
|
28
81
|
this.db = options.db;
|
|
29
82
|
this.tablePrefix = options.tablePrefix ?? 'quereus_';
|
|
30
83
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Get the table name for a store.
|
|
33
|
-
* Sanitizes schema/table names to valid SQLite identifiers.
|
|
34
|
-
*/
|
|
35
|
-
getTableName(schemaName, tableName) {
|
|
36
|
-
const sanitized = `${schemaName}_${tableName}`.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
37
|
-
return `${this.tablePrefix}${sanitized}`;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get the key for the store cache.
|
|
41
|
-
*/
|
|
42
|
-
getStoreKey(schemaName, tableName) {
|
|
43
|
-
return `${schemaName}.${tableName}`.toLowerCase();
|
|
44
|
-
}
|
|
45
84
|
async getStore(schemaName, tableName, _options) {
|
|
46
|
-
|
|
47
|
-
return this.getOrCreateStore(key, this.getTableName(schemaName, tableName));
|
|
85
|
+
return this.getOrCreateStore(buildDataStoreName(schemaName, tableName));
|
|
48
86
|
}
|
|
49
87
|
async getIndexStore(schemaName, tableName, indexName) {
|
|
50
|
-
|
|
51
|
-
const sqliteTableName = `${this.getTableName(schemaName, tableName)}${STORE_SUFFIX.INDEX}${indexName}`.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
52
|
-
return this.getOrCreateStore(key, sqliteTableName);
|
|
88
|
+
return this.getOrCreateStore(buildIndexStoreName(schemaName, tableName, indexName));
|
|
53
89
|
}
|
|
54
90
|
async getStatsStore(_schemaName, _tableName) {
|
|
55
91
|
// Use the unified __stats__ store for all tables
|
|
@@ -60,17 +96,15 @@ export class SQLiteProvider {
|
|
|
60
96
|
}
|
|
61
97
|
async getCatalogStore() {
|
|
62
98
|
if (!this.catalogStore) {
|
|
63
|
-
this.catalogStore = SQLiteStore.create(this.db, `${this.tablePrefix}
|
|
99
|
+
this.catalogStore = SQLiteStore.create(this.db, `${this.tablePrefix}${CATALOG_STORE_NAME}`);
|
|
64
100
|
}
|
|
65
101
|
return this.catalogStore;
|
|
66
102
|
}
|
|
67
103
|
async closeStore(schemaName, tableName) {
|
|
68
|
-
|
|
69
|
-
await this.closeStoreByKey(key);
|
|
104
|
+
await this.closeStoreByName(buildDataStoreName(schemaName, tableName));
|
|
70
105
|
}
|
|
71
106
|
async closeIndexStore(schemaName, tableName, indexName) {
|
|
72
|
-
|
|
73
|
-
await this.closeStoreByKey(key);
|
|
107
|
+
await this.closeStoreByName(buildIndexStoreName(schemaName, tableName, indexName));
|
|
74
108
|
}
|
|
75
109
|
async closeAll() {
|
|
76
110
|
for (const store of this.stores.values()) {
|
|
@@ -89,38 +123,72 @@ export class SQLiteProvider {
|
|
|
89
123
|
this.db.close();
|
|
90
124
|
}
|
|
91
125
|
async deleteIndexStore(schemaName, tableName, indexName) {
|
|
92
|
-
|
|
93
|
-
await this.closeStoreByKey(key);
|
|
94
|
-
// Note: SQLite doesn't need explicit store deletion - table is dropped when closed
|
|
126
|
+
await this.dropStoreByName(buildIndexStoreName(schemaName, tableName, indexName));
|
|
95
127
|
}
|
|
96
128
|
async deleteTableStores(schemaName, tableName, indexNames) {
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
129
|
+
// Drop the data store's backing table.
|
|
130
|
+
await this.dropStoreByName(buildDataStoreName(schemaName, tableName));
|
|
131
|
+
// Stats live in the unified __stats__ store, so there is no per-table stats store to
|
|
132
|
+
// drop here. (The table's ENTRY in that store is not reclaimed by anyone today —
|
|
133
|
+
// tracked as `bug-drop-table-leaves-stale-stats-entry`; it is a stale row-count
|
|
134
|
+
// estimate, not stale rows.)
|
|
135
|
+
// Drop exactly the table's index stores (by name), not every store matching
|
|
103
136
|
// the `{table}_idx_` prefix — that prefix also matches a sibling table
|
|
104
137
|
// literally named `{table}_idx_<x>`.
|
|
105
138
|
for (const indexName of indexNames) {
|
|
106
|
-
await this.
|
|
139
|
+
await this.dropStoreByName(buildIndexStoreName(schemaName, tableName, indexName));
|
|
107
140
|
}
|
|
108
141
|
}
|
|
109
|
-
|
|
110
|
-
|
|
142
|
+
/**
|
|
143
|
+
* The SQLite table backing a logical store.
|
|
144
|
+
*
|
|
145
|
+
* The ONE derivation, shared by every caller: a second hand-derived spelling of this is
|
|
146
|
+
* precisely the defect class the naming work had to remove from this file once (a store
|
|
147
|
+
* opened under one name and reclaimed under another leaves the rows in place).
|
|
148
|
+
*/
|
|
149
|
+
physicalTableName(storeName) {
|
|
150
|
+
return `${this.tablePrefix}${encodeSqliteName(storeName)}`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get or open the store for a logical store name, caching by that CANONICAL name —
|
|
154
|
+
* the same string the shared builders produce and `StoreModule` reasons about. Keying
|
|
155
|
+
* the cache by anything else (a separately-derived spelling) risks two cache entries
|
|
156
|
+
* for one physical table, or one entry shared by two.
|
|
157
|
+
*/
|
|
158
|
+
getOrCreateStore(storeName) {
|
|
159
|
+
let store = this.stores.get(storeName);
|
|
111
160
|
if (!store) {
|
|
112
|
-
store = SQLiteStore.create(this.db,
|
|
113
|
-
this.stores.set(
|
|
161
|
+
store = SQLiteStore.create(this.db, this.physicalTableName(storeName));
|
|
162
|
+
this.stores.set(storeName, store);
|
|
114
163
|
}
|
|
115
164
|
return store;
|
|
116
165
|
}
|
|
117
|
-
async
|
|
118
|
-
const store = this.stores.get(
|
|
166
|
+
async closeStoreByName(storeName) {
|
|
167
|
+
const store = this.stores.get(storeName);
|
|
119
168
|
if (store) {
|
|
120
169
|
await store.close();
|
|
121
|
-
this.stores.delete(
|
|
170
|
+
this.stores.delete(storeName);
|
|
122
171
|
}
|
|
123
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Erase a store by dropping its backing table, after closing and evicting any cached
|
|
175
|
+
* handle — so a store re-opened later under the same name is EMPTY, which is the reclaim
|
|
176
|
+
* contract on {@link KVStoreProvider.deleteTableStores}. `if exists` makes deleting a
|
|
177
|
+
* store that was never created a no-op that creates nothing, as that contract also asks.
|
|
178
|
+
*
|
|
179
|
+
* The table name is interpolated rather than bound because SQLite cannot parameterize an
|
|
180
|
+
* identifier; {@link encodeSqliteName} restricts every produced name to `[0-9a-z_]` plus
|
|
181
|
+
* the uppercase hex of its escapes, so there is no quoting or injection surface — the
|
|
182
|
+
* same reason `SQLiteStore` interpolates it.
|
|
183
|
+
*/
|
|
184
|
+
async dropStoreByName(storeName) {
|
|
185
|
+
await this.closeStoreByName(storeName);
|
|
186
|
+
// NOTE: dropping a table returns its pages to this SQLite file's freelist for reuse,
|
|
187
|
+
// but does not shrink the file on disk — that needs a `vacuum`, which is not run here.
|
|
188
|
+
// If a mobile app's database file growing and never shrinking becomes a complaint,
|
|
189
|
+
// that is the thread to pull.
|
|
190
|
+
this.db.execute(`drop table if exists ${this.physicalTableName(storeName)}`);
|
|
191
|
+
}
|
|
124
192
|
}
|
|
125
193
|
/**
|
|
126
194
|
* Create a SQLite provider with the given options.
|
package/dist/src/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/provider.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/src/store.d.ts
CHANGED
|
@@ -4,7 +4,24 @@
|
|
|
4
4
|
* Uses @nativescript-community/sqlite for SQLite bindings on iOS/Android.
|
|
5
5
|
* Keys and values are stored as BLOBs for correct lexicographic ordering.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type KVStore, type KVEntry, type WriteBatch, type IterateOptions } from '@quereus/store';
|
|
8
|
+
/**
|
|
9
|
+
* Entries per `select` when {@link SQLiteStore.iterate} pages a range.
|
|
10
|
+
*
|
|
11
|
+
* A SQL `select` hands back a whole result set, so this backend cannot stream: it reads
|
|
12
|
+
* one bounded batch, yields it, and resumes past the last key seen (see `pagedIterate`).
|
|
13
|
+
* The size is the peak-memory / round-trip tradeoff — small enough that a full table
|
|
14
|
+
* scan on a phone holds one page rather than the table, large enough that a big scan is
|
|
15
|
+
* not one query per handful of rows. Exported so the conformance adapter's read meter
|
|
16
|
+
* reports the real read-ahead instead of duplicating the number.
|
|
17
|
+
*
|
|
18
|
+
* NOTE: 128 is a judgement call, not a measurement — nothing has profiled this backend on
|
|
19
|
+
* a device (the tests run better-sqlite3 on a desktop). If a device profile ever shows
|
|
20
|
+
* scans dominated by query round-trips, raise it; if peak memory per scan is the problem,
|
|
21
|
+
* lower it. Conformance tier 3 seeds 306 entries, so any size it still crosses keeps the
|
|
22
|
+
* batch-seam cases meaningful.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ITERATE_BATCH_SIZE = 128;
|
|
8
25
|
/**
|
|
9
26
|
* Type definition for @nativescript-community/sqlite database.
|
|
10
27
|
* We use a minimal interface to avoid hard dependency on the package types.
|
|
@@ -45,13 +62,51 @@ export declare class SQLiteStore implements KVStore {
|
|
|
45
62
|
static create(db: SQLiteDatabase, tableName?: string): SQLiteStore;
|
|
46
63
|
private ensureTable;
|
|
47
64
|
get(key: Uint8Array): Promise<Uint8Array | undefined>;
|
|
65
|
+
/**
|
|
66
|
+
* The shared fallback: `SQLiteDatabase.select` is synchronous and in-process, so a batch
|
|
67
|
+
* is N local statement executions with no round trip to collapse.
|
|
68
|
+
*
|
|
69
|
+
* NOTE: a single `select key, value from … where key in (?, ?, …)` would replace those N
|
|
70
|
+
* statements with one, but it needs a per-batch-size SQL string (or a rebuilt statement
|
|
71
|
+
* each call) and a key→value re-ordering pass, since `in` answers in table order rather
|
|
72
|
+
* than argument order. Not worth it while the reads are local; do it if a SQLite-backed
|
|
73
|
+
* mobile profile ever shows the per-statement overhead.
|
|
74
|
+
*/
|
|
75
|
+
getMany(keys: readonly Uint8Array[]): Promise<(Uint8Array | undefined)[]>;
|
|
48
76
|
put(key: Uint8Array, value: Uint8Array): Promise<void>;
|
|
49
77
|
delete(key: Uint8Array): Promise<void>;
|
|
50
78
|
has(key: Uint8Array): Promise<boolean>;
|
|
51
79
|
iterate(options?: IterateOptions): AsyncIterable<KVEntry>;
|
|
80
|
+
/** Read up to `want` entries for one already-resume-adjusted batch of a range. */
|
|
81
|
+
private fetchBatch;
|
|
82
|
+
/**
|
|
83
|
+
* Build the `where` conditions + bound parameters for a key range.
|
|
84
|
+
*
|
|
85
|
+
* Every bound present is emitted, so a caller `gt` and a resume `gt` would both appear
|
|
86
|
+
* (`and` of two `key > ?` is correct, just redundant). `pagedIterate` merges the resume
|
|
87
|
+
* edge into a single bound, so in practice at most one lower and one upper appear.
|
|
88
|
+
*/
|
|
89
|
+
private buildRangeFilter;
|
|
90
|
+
/**
|
|
91
|
+
* Build one batch read over a key range, ordered by key and capped at `limit` rows.
|
|
92
|
+
*
|
|
93
|
+
* `limit` is the batch size, NOT the caller's `IterateOptions.limit` — `pagedIterate`
|
|
94
|
+
* spends the caller's limit across batches and passes the per-batch want here.
|
|
95
|
+
*/
|
|
52
96
|
private buildIterateQuery;
|
|
53
97
|
batch(): WriteBatch;
|
|
54
98
|
close(): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Count the keys in a range with one `count(*)`.
|
|
101
|
+
*
|
|
102
|
+
* NOTE: `options.limit` is ignored — the count of a range is not affected by how many
|
|
103
|
+
* of it a caller would read. That matches IndexedDB (`IDBObjectStore.count(range)`)
|
|
104
|
+
* and this store's previous behavior; the LevelDB and in-memory backends count by
|
|
105
|
+
* iterating, so they cap at `limit` instead. The contract says nothing either way and
|
|
106
|
+
* the conformance battery never passes `limit` here. If a caller ever depends on the
|
|
107
|
+
* capped reading, settle it in `KVStore.approximateCount`'s contract and make the
|
|
108
|
+
* battery enforce it, rather than changing one backend.
|
|
109
|
+
*/
|
|
55
110
|
approximateCount(options?: IterateOptions): Promise<number>;
|
|
56
111
|
private checkOpen;
|
|
57
112
|
/**
|
package/dist/src/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAgC,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhI;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC;IACnD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,iBAAiB,EAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5D,KAAK,IAAI,IAAI,CAAC;CACf;AAWD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,EAAE,EAAE,cAAc,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,OAAO;IACzC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IAGvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,EAAE,kBAAkB;IAcvC;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,GAAE,MAAa,GAAG,WAAW;IAIxE,OAAO,CAAC,WAAW;IAWb,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAW3D;;;;;;;;;OASG;IACH,OAAO,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,CAAC;IAInE,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtD,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtC,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAMrC,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC;IAoBhE,kFAAkF;YACpE,UAAU;IASxB;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAwBxB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAOzB,KAAK,IAAI,UAAU;IAKb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;;;;;;;;;OAUG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAQjE,OAAO,CAAC,SAAS;IAMjB;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,GAAG,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,UAAU,CAAA;KAAE,CAAC,GAAG,IAAI;CAa1H"}
|
package/dist/src/store.js
CHANGED
|
@@ -4,6 +4,24 @@
|
|
|
4
4
|
* Uses @nativescript-community/sqlite for SQLite bindings on iOS/Android.
|
|
5
5
|
* Keys and values are stored as BLOBs for correct lexicographic ordering.
|
|
6
6
|
*/
|
|
7
|
+
import { defaultGetMany, pagedIterate } from '@quereus/store';
|
|
8
|
+
/**
|
|
9
|
+
* Entries per `select` when {@link SQLiteStore.iterate} pages a range.
|
|
10
|
+
*
|
|
11
|
+
* A SQL `select` hands back a whole result set, so this backend cannot stream: it reads
|
|
12
|
+
* one bounded batch, yields it, and resumes past the last key seen (see `pagedIterate`).
|
|
13
|
+
* The size is the peak-memory / round-trip tradeoff — small enough that a full table
|
|
14
|
+
* scan on a phone holds one page rather than the table, large enough that a big scan is
|
|
15
|
+
* not one query per handful of rows. Exported so the conformance adapter's read meter
|
|
16
|
+
* reports the real read-ahead instead of duplicating the number.
|
|
17
|
+
*
|
|
18
|
+
* NOTE: 128 is a judgement call, not a measurement — nothing has profiled this backend on
|
|
19
|
+
* a device (the tests run better-sqlite3 on a desktop). If a device profile ever shows
|
|
20
|
+
* scans dominated by query round-trips, raise it; if peak memory per scan is the problem,
|
|
21
|
+
* lower it. Conformance tier 3 seeds 306 entries, so any size it still crosses keeps the
|
|
22
|
+
* batch-seam cases meaningful.
|
|
23
|
+
*/
|
|
24
|
+
export const ITERATE_BATCH_SIZE = 128;
|
|
7
25
|
/**
|
|
8
26
|
* SQLite implementation of KVStore for NativeScript.
|
|
9
27
|
*
|
|
@@ -54,6 +72,19 @@ export class SQLiteStore {
|
|
|
54
72
|
}
|
|
55
73
|
return toUint8Array(rows[0].value);
|
|
56
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* The shared fallback: `SQLiteDatabase.select` is synchronous and in-process, so a batch
|
|
77
|
+
* is N local statement executions with no round trip to collapse.
|
|
78
|
+
*
|
|
79
|
+
* NOTE: a single `select key, value from … where key in (?, ?, …)` would replace those N
|
|
80
|
+
* statements with one, but it needs a per-batch-size SQL string (or a rebuilt statement
|
|
81
|
+
* each call) and a key→value re-ordering pass, since `in` answers in table order rather
|
|
82
|
+
* than argument order. Not worth it while the reads are local; do it if a SQLite-backed
|
|
83
|
+
* mobile profile ever shows the per-statement overhead.
|
|
84
|
+
*/
|
|
85
|
+
getMany(keys) {
|
|
86
|
+
return defaultGetMany(this, keys);
|
|
87
|
+
}
|
|
57
88
|
async put(key, value) {
|
|
58
89
|
this.checkOpen();
|
|
59
90
|
this.db.execute(this.sqlPut, [toArrayBuffer(key), toArrayBuffer(value)]);
|
|
@@ -69,16 +100,39 @@ export class SQLiteStore {
|
|
|
69
100
|
}
|
|
70
101
|
async *iterate(options) {
|
|
71
102
|
this.checkOpen();
|
|
72
|
-
|
|
103
|
+
// One `select` per batch, resuming from the last key seen — so peak memory is one
|
|
104
|
+
// batch however many entries the range holds, and a consumer that stops early costs
|
|
105
|
+
// at most one batch beyond what it consumed. `pagedIterate` owns the resume edge.
|
|
106
|
+
//
|
|
107
|
+
// Nothing to release on early termination, so no try/finally: each batch's `select`
|
|
108
|
+
// is self-contained and `SQLiteDatabase.select` returns a realized array — there is
|
|
109
|
+
// no cursor or statement handle left open between batches. If the driver interface
|
|
110
|
+
// ever grows a statement-handle read, this needs a `finally` that closes it.
|
|
111
|
+
//
|
|
112
|
+
// NOTE: `checkOpen` runs once, at the start, as it did when one `select` read the whole
|
|
113
|
+
// range — so a `close()` while a scan is suspended does not stop its remaining batches.
|
|
114
|
+
// Harmless today because `close()` deliberately leaves the (possibly shared) database
|
|
115
|
+
// open. If closing a store ever also closes its database, the next batch would throw a
|
|
116
|
+
// driver error mid-scan; re-check `closed` per batch then.
|
|
117
|
+
yield* pagedIterate(options, (bounds, want) => this.fetchBatch(bounds, want), ITERATE_BATCH_SIZE);
|
|
118
|
+
}
|
|
119
|
+
/** Read up to `want` entries for one already-resume-adjusted batch of a range. */
|
|
120
|
+
async fetchBatch(bounds, want) {
|
|
121
|
+
const { sql, params } = this.buildIterateQuery(bounds, want);
|
|
73
122
|
const rows = this.db.select(sql, params);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
}
|
|
123
|
+
return rows.map((row) => ({
|
|
124
|
+
key: toUint8Array(row.key),
|
|
125
|
+
value: toUint8Array(row.value),
|
|
126
|
+
}));
|
|
80
127
|
}
|
|
81
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Build the `where` conditions + bound parameters for a key range.
|
|
130
|
+
*
|
|
131
|
+
* Every bound present is emitted, so a caller `gt` and a resume `gt` would both appear
|
|
132
|
+
* (`and` of two `key > ?` is correct, just redundant). `pagedIterate` merges the resume
|
|
133
|
+
* edge into a single bound, so in practice at most one lower and one upper appear.
|
|
134
|
+
*/
|
|
135
|
+
buildRangeFilter(options) {
|
|
82
136
|
const conditions = [];
|
|
83
137
|
const params = [];
|
|
84
138
|
if (options?.gte) {
|
|
@@ -97,14 +151,18 @@ export class SQLiteStore {
|
|
|
97
151
|
conditions.push('key < ?');
|
|
98
152
|
params.push(toArrayBuffer(options.lt));
|
|
99
153
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
154
|
+
return { where: conditions.length > 0 ? ` where ${conditions.join(' and ')}` : '', params };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Build one batch read over a key range, ordered by key and capped at `limit` rows.
|
|
158
|
+
*
|
|
159
|
+
* `limit` is the batch size, NOT the caller's `IterateOptions.limit` — `pagedIterate`
|
|
160
|
+
* spends the caller's limit across batches and passes the per-batch want here.
|
|
161
|
+
*/
|
|
162
|
+
buildIterateQuery(options, limit) {
|
|
163
|
+
const { where, params } = this.buildRangeFilter(options);
|
|
164
|
+
const sql = `select key, value from ${this.tableName}${where}`
|
|
165
|
+
+ ` order by key ${options?.reverse ? 'desc' : 'asc'} limit ${limit}`;
|
|
108
166
|
return { sql, params };
|
|
109
167
|
}
|
|
110
168
|
batch() {
|
|
@@ -115,17 +173,21 @@ export class SQLiteStore {
|
|
|
115
173
|
// Mark as closed but don't close the DB - it may be shared
|
|
116
174
|
this.closed = true;
|
|
117
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Count the keys in a range with one `count(*)`.
|
|
178
|
+
*
|
|
179
|
+
* NOTE: `options.limit` is ignored — the count of a range is not affected by how many
|
|
180
|
+
* of it a caller would read. That matches IndexedDB (`IDBObjectStore.count(range)`)
|
|
181
|
+
* and this store's previous behavior; the LevelDB and in-memory backends count by
|
|
182
|
+
* iterating, so they cap at `limit` instead. The contract says nothing either way and
|
|
183
|
+
* the conformance battery never passes `limit` here. If a caller ever depends on the
|
|
184
|
+
* capped reading, settle it in `KVStore.approximateCount`'s contract and make the
|
|
185
|
+
* battery enforce it, rather than changing one backend.
|
|
186
|
+
*/
|
|
118
187
|
async approximateCount(options) {
|
|
119
188
|
this.checkOpen();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const rows = this.db.select(`select count(*) as cnt from ${this.tableName}`, []);
|
|
123
|
-
return rows[0]?.cnt ?? 0;
|
|
124
|
-
}
|
|
125
|
-
// With bounds, we need to count with conditions
|
|
126
|
-
const { sql, params } = this.buildIterateQuery(options);
|
|
127
|
-
const countSql = sql.replace(/^select key, value/, 'select count(*) as cnt').replace(/ order by.*$/, '');
|
|
128
|
-
const rows = this.db.select(countSql, params);
|
|
189
|
+
const { where, params } = this.buildRangeFilter(options);
|
|
190
|
+
const rows = this.db.select(`select count(*) as cnt from ${this.tableName}${where}`, params);
|
|
129
191
|
return rows[0]?.cnt ?? 0;
|
|
130
192
|
}
|
|
131
193
|
checkOpen() {
|
package/dist/src/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quereus/plugin-nativescript-sqlite",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "NativeScript SQLite storage plugin for Quereus - mobile persistent storage",
|
|
6
6
|
"keywords": [
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@nativescript-community/sqlite": "^3.0.0",
|
|
45
|
-
"@quereus/isolation": "^4.
|
|
46
|
-
"@quereus/quereus": "^4.
|
|
47
|
-
"@quereus/store": "^4.
|
|
45
|
+
"@quereus/isolation": "^4.12.0",
|
|
46
|
+
"@quereus/quereus": "^4.12.0",
|
|
47
|
+
"@quereus/store": "^4.12.0"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"quereus": "^0.24.0"
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"test": "cd ../.. && node --import ./packages/quereus-plugin-nativescript-sqlite/register.mjs node_modules/mocha/bin/mocha.js \"packages/quereus-plugin-nativescript-sqlite/test/**/*.spec.ts\" --reporter min --colors"
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
|
-
"@quereus/quereus": "^4.
|
|
91
|
-
"@quereus/store": "^4.
|
|
90
|
+
"@quereus/quereus": "^4.12.0",
|
|
91
|
+
"@quereus/store": "^4.12.0"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@quereus/isolation": "*",
|