indexeddbshim 17.4.3 → 17.5.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/dist/IDBFactory.d.ts +1 -3
- package/dist/IDBFactory.d.ts.map +1 -1
- package/dist/indexeddbshim-Key.js +1 -1
- package/dist/indexeddbshim-Key.min.js +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +14 -2010
- package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.js +6 -8
- package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js +2 -2
- package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
- package/dist/indexeddbshim-node.cjs +14 -2010
- package/dist/indexeddbshim-node.cjs.map +1 -1
- package/dist/indexeddbshim-noninvasive.js +6 -8
- package/dist/indexeddbshim-noninvasive.js.map +1 -1
- package/dist/indexeddbshim-noninvasive.min.js +2 -2
- package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
- package/dist/indexeddbshim.js +6 -8
- package/dist/indexeddbshim.js.map +1 -1
- package/dist/indexeddbshim.min.js +2 -2
- package/dist/indexeddbshim.min.js.map +1 -1
- package/dist/nodeWebSQL.d.ts +1 -1
- package/dist/nodeWebSQL.d.ts.map +1 -1
- package/package.json +4 -8
- package/src/IDBFactory.js +5 -7
- package/src/nodeWebSQL.js +5 -5
- package/dist/nodeSQLiteDatabase.d.ts +0 -64
- package/dist/nodeSQLiteDatabase.d.ts.map +0 -1
- package/src/nodeSQLiteDatabase.js +0 -314
package/dist/nodeWebSQL.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export type SQLiteDatabaseInstance = {
|
|
|
15
15
|
*/
|
|
16
16
|
export type SQLiteDatabaseConstructor = new (name: string, opts?: any) => SQLiteDatabaseInstance;
|
|
17
17
|
declare const nodeWebSQL: (...args: unknown[]) => import("websql-configurable/lib/websql/WebSQLDatabase.js").default;
|
|
18
|
-
import SQLiteDatabase from '
|
|
18
|
+
import SQLiteDatabase from 'websql-configurable/lib/sqlite/SQLiteDatabase.js';
|
|
19
19
|
//# sourceMappingURL=nodeWebSQL.d.ts.map
|
package/dist/nodeWebSQL.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nodeWebSQL.d.ts","sourceRoot":"","sources":["../src/nodeWebSQL.js"],"names":[],"mappings":";qCAKa;IACR,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAA,OAAO,cAAc,EAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;CACjD;;;;;;;;;;;wCAYS,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,sBAAsB;AAgCrE,qHAAsK;2BAnD3I,
|
|
1
|
+
{"version":3,"file":"nodeWebSQL.d.ts","sourceRoot":"","sources":["../src/nodeWebSQL.js"],"names":[],"mappings":";qCAKa;IACR,GAAG,EAAE,GAAG,CAAC;IACT,IAAI,EAAE,CAAA,OAAO,cAAc,EAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAA;CACjD;;;;;;;;;;;wCAYS,KAAK,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,sBAAsB;AAgCrE,qHAAsK;2BAnD3I,kDAAkD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "indexeddbshim",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Parashuram <code@nparashuram.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -55,7 +55,6 @@
|
|
|
55
55
|
"fix-permissions": "chmod +x node_modules/npm-run-all/bin/*/index.js",
|
|
56
56
|
"lint": "eslint .",
|
|
57
57
|
"eslint": "npm run lint --",
|
|
58
|
-
"sqlite-rebuild": "cd node_modules/sqlite3 && npm i",
|
|
59
58
|
"w3c-build-only": "node test-support/node-buildjs",
|
|
60
59
|
"w3c-build": "run-s clean-w3c clean-w3c-built w3c-build-only",
|
|
61
60
|
"fake": "run-s clean-fake coverage:fake clean-fake",
|
|
@@ -168,13 +167,12 @@
|
|
|
168
167
|
},
|
|
169
168
|
"dependencies": {
|
|
170
169
|
"@node-static/node-static": "^0.9.1",
|
|
171
|
-
"better-sqlite3": "^13.0.3",
|
|
172
170
|
"canvas": "^3.2.3",
|
|
173
171
|
"eventtargeter": "0.12.0",
|
|
174
172
|
"sync-promise-expanded": "^2.0.0",
|
|
175
173
|
"typeson": "10.2.0",
|
|
176
174
|
"typeson-registry": "14.3.0",
|
|
177
|
-
"websql-configurable": "^
|
|
175
|
+
"websql-configurable": "^4.0.0"
|
|
178
176
|
},
|
|
179
177
|
"devDependencies": {
|
|
180
178
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
@@ -187,7 +185,6 @@
|
|
|
187
185
|
"@rollup/plugin-json": "^6.1.0",
|
|
188
186
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
189
187
|
"@rollup/plugin-terser": "^1.0.0",
|
|
190
|
-
"@types/better-sqlite3": "^9.6.0",
|
|
191
188
|
"@types/node": "^26.4.0",
|
|
192
189
|
"@types/websql": "^0.0.30",
|
|
193
190
|
"@unicode/unicode-17.0.0": "^2.0.2",
|
|
@@ -202,7 +199,7 @@
|
|
|
202
199
|
"coveradge": "^0.8.2",
|
|
203
200
|
"delay-cli": "^3.0.0",
|
|
204
201
|
"eslint": "^10.9.1",
|
|
205
|
-
"eslint-config-ash-nazg": "^
|
|
202
|
+
"eslint-config-ash-nazg": "^43.1.0",
|
|
206
203
|
"globals": "^17.11.0",
|
|
207
204
|
"is-date-object": "1.1.0",
|
|
208
205
|
"isomorphic-fetch": "^3.0.0",
|
|
@@ -215,7 +212,7 @@
|
|
|
215
212
|
"npm-run-all": "^4.1.5",
|
|
216
213
|
"open-cli": "^9.0.0",
|
|
217
214
|
"rimraf": "^6.1.3",
|
|
218
|
-
"rollup": "^4.63.
|
|
215
|
+
"rollup": "^4.63.1",
|
|
219
216
|
"rollup-plugin-filesize": "^10.0.0",
|
|
220
217
|
"rollup-plugin-node-polyfills": "^0.2.1",
|
|
221
218
|
"sinon": "^22.1.0",
|
|
@@ -227,7 +224,6 @@
|
|
|
227
224
|
"allowScripts": {
|
|
228
225
|
"es5-ext@0.10.64": true,
|
|
229
226
|
"fsevents@2.3.3": true,
|
|
230
|
-
"sqlite3@5.1.7": true,
|
|
231
227
|
"canvas@3.2.3": true,
|
|
232
228
|
"unrs-resolver@1.12.2": true,
|
|
233
229
|
"better-sqlite3@13.0.3": true,
|
package/src/IDBFactory.js
CHANGED
|
@@ -220,9 +220,7 @@ function triggerAnyVersionChangeAndBlockedEvents (openConnections, req, oldVersi
|
|
|
220
220
|
/**
|
|
221
221
|
* @typedef {import('websql-configurable/lib/websql/WebSQLDatabase.js').default & {
|
|
222
222
|
* _db: {
|
|
223
|
-
*
|
|
224
|
-
* close: (errBack: (err: Error) => void) => void
|
|
225
|
-
* }
|
|
223
|
+
* close: (errBack: (err: Error) => void) => void
|
|
226
224
|
* }
|
|
227
225
|
* }} DatabaseFull
|
|
228
226
|
*/
|
|
@@ -304,7 +302,7 @@ function closeCachedWebSQLConnections (name, cb) {
|
|
|
304
302
|
return;
|
|
305
303
|
}
|
|
306
304
|
dbs.forEach((db) => {
|
|
307
|
-
const sqliteDB = db._db
|
|
305
|
+
const sqliteDB = db._db;
|
|
308
306
|
if (!sqliteDB || !sqliteDB.close) {
|
|
309
307
|
remaining--;
|
|
310
308
|
if (remaining === 0) {
|
|
@@ -347,9 +345,9 @@ function cleanupDatabaseResources (__openDatabase, name, escapedDatabaseName, da
|
|
|
347
345
|
databaseDeleted();
|
|
348
346
|
return;
|
|
349
347
|
}
|
|
350
|
-
const sqliteDB = latestSQLiteDBCached._db
|
|
348
|
+
const sqliteDB = latestSQLiteDBCached._db;
|
|
351
349
|
if (!sqliteDB || !sqliteDB.close) {
|
|
352
|
-
console.error('The `openDatabase` implementation does not have the expected `._db.
|
|
350
|
+
console.error('The `openDatabase` implementation does not have the expected `._db.close` method for closing the database');
|
|
353
351
|
return;
|
|
354
352
|
}
|
|
355
353
|
sqliteDB.close(
|
|
@@ -1229,7 +1227,7 @@ IDBFactory.prototype.databases = function () {
|
|
|
1229
1227
|
let calledDbCreateError = false;
|
|
1230
1228
|
// Snapshotted *now*, synchronously, at call time -- not read later
|
|
1231
1229
|
// from inside the SQL query's callback below, which runs on a
|
|
1232
|
-
// deferred macrotask (see `
|
|
1230
|
+
// deferred macrotask (see `websql-configurable`'s `SQLiteDatabase#exec`) and so
|
|
1233
1231
|
// could otherwise race against (and lose to) an in-flight upgrade's
|
|
1234
1232
|
// own commit/abort handler clearing its `pendingVersionChanges`
|
|
1235
1233
|
// entry in the meantime -- which would make this method incorrectly
|
package/src/nodeWebSQL.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import customOpenDatabase from 'websql-configurable/custom/index.js';
|
|
2
|
-
import SQLiteDatabase from '
|
|
2
|
+
import SQLiteDatabase from 'websql-configurable/lib/sqlite/SQLiteDatabase.js';
|
|
3
3
|
import CFG from './CFG.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -29,16 +29,16 @@ function wrappedSQLiteDatabase (name) {
|
|
|
29
29
|
// @ts-ignore It's ok; needed under some TS versions
|
|
30
30
|
const db = new SQLiteDatabase(name, {});
|
|
31
31
|
if (CFG.sqlBusyTimeout) {
|
|
32
|
-
db.
|
|
32
|
+
db.configure('busyTimeout', /** @type {number} */ (CFG.sqlBusyTimeout)); // Default is 1000
|
|
33
33
|
}
|
|
34
34
|
if (CFG.sqlTrace) {
|
|
35
|
-
db.
|
|
35
|
+
db.configure('trace', CFG.sqlTrace);
|
|
36
36
|
}
|
|
37
37
|
if (CFG.sqlProfile) {
|
|
38
|
-
db.
|
|
38
|
+
db.configure('profile', CFG.sqlProfile);
|
|
39
39
|
}
|
|
40
40
|
if (CFG.sqlMemoryQuota) {
|
|
41
|
-
db.
|
|
41
|
+
db.configure('memoryQuota', /** @type {number} */ (CFG.sqlMemoryQuota));
|
|
42
42
|
}
|
|
43
43
|
return db;
|
|
44
44
|
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
export default SQLiteDatabase;
|
|
2
|
-
export type SQLProfileCallback = (sql: string, duration: number) => void;
|
|
3
|
-
export type SQLTraceCallback = ((sql: string) => void) | undefined;
|
|
4
|
-
/**
|
|
5
|
-
* @class
|
|
6
|
-
* @param {string} name
|
|
7
|
-
* @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
|
|
8
|
-
* @this {{_db: any, _qFilePath: string}}
|
|
9
|
-
* @returns {void}
|
|
10
|
-
*/
|
|
11
|
-
declare function SQLiteDatabase(this: {
|
|
12
|
-
_db: any;
|
|
13
|
-
_qFilePath: string;
|
|
14
|
-
}, name: string, opts?: {
|
|
15
|
-
busyTimeout?: number;
|
|
16
|
-
trace?: (sql: string) => void;
|
|
17
|
-
profile?: SQLProfileCallback;
|
|
18
|
-
}): void;
|
|
19
|
-
declare class SQLiteDatabase {
|
|
20
|
-
/**
|
|
21
|
-
* @class
|
|
22
|
-
* @param {string} name
|
|
23
|
-
* @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
|
|
24
|
-
* @this {{_db: any, _qFilePath: string}}
|
|
25
|
-
* @returns {void}
|
|
26
|
-
*/
|
|
27
|
-
constructor(this: {
|
|
28
|
-
_db: any;
|
|
29
|
-
_qFilePath: string;
|
|
30
|
-
}, name: string, opts?: {
|
|
31
|
-
busyTimeout?: number;
|
|
32
|
-
trace?: (sql: string) => void;
|
|
33
|
-
profile?: SQLProfileCallback;
|
|
34
|
-
});
|
|
35
|
-
_qFilePath: string;
|
|
36
|
-
_db: any;
|
|
37
|
-
/**
|
|
38
|
-
* @param {{sql: string, args: unknown[]}[]} queries
|
|
39
|
-
* @param {boolean} readOnly
|
|
40
|
-
* @param {(err: Error|null, results?: SQLiteResult[]) => void} callback
|
|
41
|
-
* @returns {void}
|
|
42
|
-
*/
|
|
43
|
-
exec(queries: {
|
|
44
|
-
sql: string;
|
|
45
|
-
args: unknown[];
|
|
46
|
-
}[], readOnly: boolean, callback: (err: Error | null, results?: SQLiteResult[]) => void): void;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
50
|
-
*/
|
|
51
|
-
declare class SQLiteResult {
|
|
52
|
-
/**
|
|
53
|
-
* @param {Error|null|undefined} error
|
|
54
|
-
* @param {number|undefined} [insertId]
|
|
55
|
-
* @param {number} [rowsAffected]
|
|
56
|
-
* @param {object[]} [rows]
|
|
57
|
-
*/
|
|
58
|
-
constructor(error: Error | null | undefined, insertId?: number | undefined, rowsAffected?: number, rows?: object[]);
|
|
59
|
-
error: Error | null | undefined;
|
|
60
|
-
insertId: number | undefined;
|
|
61
|
-
rowsAffected: number | undefined;
|
|
62
|
-
rows: object[] | undefined;
|
|
63
|
-
}
|
|
64
|
-
//# sourceMappingURL=nodeSQLiteDatabase.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"nodeSQLiteDatabase.d.ts","sourceRoot":"","sources":["../src/nodeSQLiteDatabase.js"],"names":[],"mappings":";iCAyBa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI;+BAIvC,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAC,SAAS;AAkC9C;;;;;;GAMG;AACH;SAHgB,GAAG;gBAAc,MAAM;SAF5B,MAAM,SACN;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAAC,GAEjF,IAAI,CA4EhB;;IAjFD;;;;;;OAMG;IACH;aAHgB,GAAG;oBAAc,MAAM;aAF5B,MAAM,SACN;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;KAAC,EA8E7F;IAzEG,mBAAsB;IAkBtB,SAsDE;IAyBN;;;;;OAKG;IACH,cALW;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAA;KAAC,EAAE,YAChC,OAAO,YACP,CAAC,GAAG,EAAE,KAAK,GAAC,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,KAAK,IAAI,GACjD,IAAI,CA2IhB;;AArTD;;GAEG;AACH;IACI;;;;;OAKG;IACH,mBALW,KAAK,GAAC,IAAI,GAAC,SAAS,aACpB,MAAM,GAAC,SAAS,iBAChB,MAAM,SACN,MAAM,EAAE,EAOlB;IAJG,gCAAkB;IAClB,6BAAwB;IACxB,iCAAgC;IAChC,2BAAgB;CAEvB"}
|
|
@@ -1,314 +0,0 @@
|
|
|
1
|
-
import Database from 'better-sqlite3';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
class SQLiteResult {
|
|
7
|
-
/**
|
|
8
|
-
* @param {Error|null|undefined} error
|
|
9
|
-
* @param {number|undefined} [insertId]
|
|
10
|
-
* @param {number} [rowsAffected]
|
|
11
|
-
* @param {object[]} [rows]
|
|
12
|
-
*/
|
|
13
|
-
constructor (error, insertId, rowsAffected, rows) {
|
|
14
|
-
this.error = error;
|
|
15
|
-
this.insertId = insertId;
|
|
16
|
-
this.rowsAffected = rowsAffected;
|
|
17
|
-
this.rows = rows;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const READ_ONLY_ERROR = new Error(
|
|
22
|
-
'could not prepare statement (23 not authorized)'
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {(sql: string, duration: number) => void} SQLProfileCallback
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {((sql: string) => void)|undefined} SQLTraceCallback
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
// Two separate `better-sqlite3` connections (i.e., two separate
|
|
34
|
-
// `SQLiteDatabase` instances) can legitimately point at the same
|
|
35
|
-
// underlying file at once -- e.g. an old, about-to-close IndexedDB
|
|
36
|
-
// connection and a newly-opened one during an upgrade. `PRAGMA
|
|
37
|
-
// busy_timeout` can't safely arbitrate between them here: it blocks the
|
|
38
|
-
// single JS thread synchronously while it retries, but the lock holder's
|
|
39
|
-
// own release is itself scheduled via `setImmediate` below, which can
|
|
40
|
-
// never fire while that retry loop is blocking the same thread -- so
|
|
41
|
-
// instead of waiting, a second connection's write just fails with
|
|
42
|
-
// "database is locked".
|
|
43
|
-
//
|
|
44
|
-
// `fileReaders`/`fileWriter` implement a simple (not starvation-proof --
|
|
45
|
-
// see the release loop below) reader/writer lock per file path instead:
|
|
46
|
-
// any number of `readonly` transactions may hold the file concurrently,
|
|
47
|
-
// matching what SQLite itself already natively supports (multiple
|
|
48
|
-
// readers never need to exclude each other, only a writer needs
|
|
49
|
-
// exclusivity), while a non-`readonly` transaction still waits for
|
|
50
|
-
// exclusive access -- no concurrent readers, no concurrent writer. A
|
|
51
|
-
// simple single-owner mutex (this file's prior implementation) would
|
|
52
|
-
// otherwise serialize even purely-concurrent-reader scenarios that
|
|
53
|
-
// never touch a writer at all, for no reason `better-sqlite3`/SQLite
|
|
54
|
-
// itself requires.
|
|
55
|
-
/** @type {Map<string, Set<{_db: any, _qFilePath: string}>>} */
|
|
56
|
-
const fileReaders = new Map();
|
|
57
|
-
/** @type {Map<string, {_db: any, _qFilePath: string}>} */
|
|
58
|
-
const fileWriter = new Map();
|
|
59
|
-
/** @type {Map<string, {isReader: boolean, resume: () => void}[]>} */
|
|
60
|
-
const fileWaiters = new Map();
|
|
61
|
-
const beginRe = /^\s*BEGIN\b/iu;
|
|
62
|
-
const endRe = /^\s*(END|COMMIT|ROLLBACK)\b/iu;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* @class
|
|
66
|
-
* @param {string} name
|
|
67
|
-
* @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
|
|
68
|
-
* @this {{_db: any, _qFilePath: string}}
|
|
69
|
-
* @returns {void}
|
|
70
|
-
*/
|
|
71
|
-
function SQLiteDatabase (name, opts = {}) {
|
|
72
|
-
this._qFilePath = name;
|
|
73
|
-
/** @type {import('better-sqlite3').Database} */
|
|
74
|
-
const db = new Database(name);
|
|
75
|
-
|
|
76
|
-
/** @type {SQLTraceCallback} */
|
|
77
|
-
// eslint-disable-next-line prefer-destructuring -- TS
|
|
78
|
-
let trace = opts.trace;
|
|
79
|
-
/** @type {SQLProfileCallback|undefined} */
|
|
80
|
-
// eslint-disable-next-line prefer-destructuring -- TS
|
|
81
|
-
let profile = opts.profile;
|
|
82
|
-
|
|
83
|
-
if (opts.busyTimeout) {
|
|
84
|
-
db.pragma('busy_timeout = ' + Number(opts.busyTimeout));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Kept untyped (rather than the better-sqlite3 `Database` type) since that
|
|
88
|
-
// type is internal to `@types/better-sqlite3` and can't be named in this
|
|
89
|
-
// file's emitted declaration.
|
|
90
|
-
this._db = /** @type {any} */ ({
|
|
91
|
-
_db: db,
|
|
92
|
-
/**
|
|
93
|
-
* Compatibility with node-sqlite3's configure API.
|
|
94
|
-
* @param {'busyTimeout'|'trace'|'profile'|'memoryQuota'} option
|
|
95
|
-
* @param {number|((sql: string, duration?: number) => void)} value
|
|
96
|
-
* @returns {void}
|
|
97
|
-
*/
|
|
98
|
-
configure (option, value) {
|
|
99
|
-
if (option === 'busyTimeout') {
|
|
100
|
-
db.pragma('busy_timeout = ' + Number(/** @type {number} */ (value)));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (option === 'trace') {
|
|
104
|
-
trace = /** @type {(sql: string) => void} */ (value);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (option === 'profile') {
|
|
108
|
-
profile = /** @type {SQLProfileCallback} */ (value);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (option === 'memoryQuota') {
|
|
112
|
-
const bytes = Number(value);
|
|
113
|
-
// `better-sqlite3` doesn't have a direct `sqlite3_quota_set` binding,
|
|
114
|
-
// but we can limit the max pages:
|
|
115
|
-
const pageSizeObj = db.pragma('page_size', {simple: true});
|
|
116
|
-
const pageSize = Number(pageSizeObj);
|
|
117
|
-
db.pragma('max_page_count = ' + Math.ceil(bytes / pageSize));
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
/**
|
|
121
|
-
* Compatibility with callback-oriented close semantics.
|
|
122
|
-
* @param {(err?: Error|null) => void} [cb]
|
|
123
|
-
* @returns {void}
|
|
124
|
-
*/
|
|
125
|
-
close (cb) {
|
|
126
|
-
try {
|
|
127
|
-
db.close();
|
|
128
|
-
if (cb) {
|
|
129
|
-
return cb(null);
|
|
130
|
-
}
|
|
131
|
-
} catch (err) {
|
|
132
|
-
if (cb) {
|
|
133
|
-
return cb(/** @type {Error} */ (err));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return undefined;
|
|
137
|
-
},
|
|
138
|
-
getTrace () {
|
|
139
|
-
return trace;
|
|
140
|
-
},
|
|
141
|
-
getProfile () {
|
|
142
|
-
return profile;
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @param {import('better-sqlite3').Database} db
|
|
149
|
-
* @param {string} sql
|
|
150
|
-
* @param {unknown[]} args
|
|
151
|
-
* @returns {object[]}
|
|
152
|
-
*/
|
|
153
|
-
function runSelect (db, sql, args) {
|
|
154
|
-
const stmt = db.prepare(sql);
|
|
155
|
-
return stmt.reader ? /** @type {object[]} */ (stmt.all(...args)) : [];
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* @param {import('better-sqlite3').Database} db
|
|
160
|
-
* @param {string} sql
|
|
161
|
-
* @param {unknown[]} args
|
|
162
|
-
* @returns {import('better-sqlite3').RunResult}
|
|
163
|
-
*/
|
|
164
|
-
function runNonSelect (db, sql, args) {
|
|
165
|
-
const stmt = db.prepare(sql);
|
|
166
|
-
return stmt.run(...args);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* @param {{sql: string, args: unknown[]}[]} queries
|
|
171
|
-
* @param {boolean} readOnly
|
|
172
|
-
* @param {(err: Error|null, results?: SQLiteResult[]) => void} callback
|
|
173
|
-
* @returns {void}
|
|
174
|
-
*/
|
|
175
|
-
SQLiteDatabase.prototype.exec = function exec (queries, readOnly, callback) {
|
|
176
|
-
// `:memory:` (SQLite/`better-sqlite3`'s own sentinel for an isolated,
|
|
177
|
-
// non-shared in-memory database) can never actually collide with a
|
|
178
|
-
// different connection -- each `new Database(':memory:')` call is
|
|
179
|
-
// already fully independent -- so there is nothing to serialize.
|
|
180
|
-
const filePath = this._qFilePath === ':memory:' ? null : this._qFilePath;
|
|
181
|
-
if (filePath && queries[0] && beginRe.test(queries[0].sql)) {
|
|
182
|
-
const activeReaders = fileReaders.get(filePath);
|
|
183
|
-
const hasActiveReaders = Boolean(activeReaders && activeReaders.size);
|
|
184
|
-
const activeWriter = fileWriter.get(filePath);
|
|
185
|
-
const blockedByWriter = Boolean(activeWriter && activeWriter !== this);
|
|
186
|
-
const blocked = blockedByWriter || (!readOnly && hasActiveReaders);
|
|
187
|
-
if (blocked) {
|
|
188
|
-
const waiters = fileWaiters.get(filePath) || [];
|
|
189
|
-
waiters.push({
|
|
190
|
-
isReader: readOnly,
|
|
191
|
-
resume: () => this.exec(queries, readOnly, callback)
|
|
192
|
-
});
|
|
193
|
-
fileWaiters.set(filePath, waiters);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if (readOnly) {
|
|
197
|
-
if (activeReaders) {
|
|
198
|
-
activeReaders.add(this);
|
|
199
|
-
} else {
|
|
200
|
-
fileReaders.set(filePath, new Set([this]));
|
|
201
|
-
}
|
|
202
|
-
} else {
|
|
203
|
-
fileWriter.set(filePath, this);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const db = this._db._db;
|
|
208
|
-
const len = queries.length;
|
|
209
|
-
const results = Array.from({length: len});
|
|
210
|
-
|
|
211
|
-
for (let i = 0; i < len; i++) {
|
|
212
|
-
const query = queries[i];
|
|
213
|
-
const {sql, args} = query;
|
|
214
|
-
const isSelect = (/^\s*SELECT\b/iu).test(sql);
|
|
215
|
-
if (readOnly && !isSelect) {
|
|
216
|
-
results[i] = new SQLiteResult(READ_ONLY_ERROR);
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
const trace = this._db.getTrace();
|
|
220
|
-
const profile = this._db.getProfile();
|
|
221
|
-
// eslint-disable-next-line unicorn/prefer-bigint-literals -- `0n` needs ES2020+ target for tsc
|
|
222
|
-
const start = profile ? process.hrtime.bigint() : BigInt(0);
|
|
223
|
-
try {
|
|
224
|
-
if (trace) {
|
|
225
|
-
trace(sql);
|
|
226
|
-
}
|
|
227
|
-
if (isSelect) {
|
|
228
|
-
const rows = runSelect(db, sql, args);
|
|
229
|
-
results[i] = new SQLiteResult(null, undefined, 0, rows);
|
|
230
|
-
} else {
|
|
231
|
-
const executionResult = runNonSelect(db, sql, args);
|
|
232
|
-
const insertId = Number(executionResult.lastInsertRowid);
|
|
233
|
-
results[i] = new SQLiteResult(null, insertId, executionResult.changes, []);
|
|
234
|
-
}
|
|
235
|
-
} catch (err) {
|
|
236
|
-
results[i] = new SQLiteResult(/** @type {Error} */ (err));
|
|
237
|
-
} finally {
|
|
238
|
-
if (profile) {
|
|
239
|
-
profile(sql, Number(process.hrtime.bigint() - start));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// A real macrotask (not `queueMicrotask`) so this yields properly:
|
|
244
|
-
// code that synchronously issues a new request from within each
|
|
245
|
-
// request's callback (e.g. to keep a transaction alive) would
|
|
246
|
-
// otherwise chain microtask to microtask forever, starving out any
|
|
247
|
-
// `setTimeout`-based code (including IndexedDB's own internal request
|
|
248
|
-
// scheduling) that never gets a turn to run. `setImmediate` (Node's
|
|
249
|
-
// "check" phase) still yields the same way `setTimeout(..., 0)` did,
|
|
250
|
-
// but runs sooner in Node's event loop.
|
|
251
|
-
setImmediate(() => {
|
|
252
|
-
// Release the file lock (if held) and hand it to the next waiting
|
|
253
|
-
// connection, if any, only once this transaction has genuinely
|
|
254
|
-
// finished -- and only here, on its own turn, so a resumed
|
|
255
|
-
// waiter's own `exec` call can't run reentrantly inside this one.
|
|
256
|
-
// Checked across the whole batch, not just `queries[0]`: when a
|
|
257
|
-
// transaction's `BEGIN` and its later `ROLLBACK`/`COMMIT` are both
|
|
258
|
-
// queued synchronously before `websql-configurable`'s async flush
|
|
259
|
-
// gets a turn to run (e.g. an immediate `transaction.abort()`
|
|
260
|
-
// inside `onupgradeneeded`), they arrive coalesced into a single
|
|
261
|
-
// batch/`exec()` call, with the terminal statement anywhere but
|
|
262
|
-
// first -- so only checking `queries[0]` would acquire the lock
|
|
263
|
-
// and never see the matching release, deadlocking every later
|
|
264
|
-
// connection to the same file.
|
|
265
|
-
if (filePath && queries.some((q) => endRe.test(q.sql))) {
|
|
266
|
-
const activeReaders = fileReaders.get(filePath);
|
|
267
|
-
if (activeReaders) {
|
|
268
|
-
activeReaders.delete(this);
|
|
269
|
-
if (!activeReaders.size) {
|
|
270
|
-
fileReaders.delete(filePath);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (fileWriter.get(filePath) === this) {
|
|
274
|
-
fileWriter.delete(filePath);
|
|
275
|
-
}
|
|
276
|
-
// Resume as many waiters as the lock now genuinely allows:
|
|
277
|
-
// any leading run of readers (concurrent reads are always
|
|
278
|
-
// fine), then at most one writer, since a writer needs
|
|
279
|
-
// exclusivity -- stop there rather than looking past it.
|
|
280
|
-
// Not starvation-proof: a steady stream of arriving readers
|
|
281
|
-
// could in principle keep a waiting writer waiting
|
|
282
|
-
// indefinitely, but that's an acceptable tradeoff here over
|
|
283
|
-
// the added complexity of tracking arrival order across
|
|
284
|
-
// reader/writer kinds.
|
|
285
|
-
const waiters = fileWaiters.get(filePath);
|
|
286
|
-
if (waiters) {
|
|
287
|
-
while (waiters.length) {
|
|
288
|
-
if (fileWriter.has(filePath)) {
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
const next = waiters[0];
|
|
292
|
-
const stillHasReaders = fileReaders.get(filePath);
|
|
293
|
-
if (!next.isReader && stillHasReaders && stillHasReaders.size) {
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
waiters.shift();
|
|
297
|
-
next.resume();
|
|
298
|
-
if (!next.isReader) {
|
|
299
|
-
// A writer just took exclusive ownership (inside
|
|
300
|
-
// `resume()`, synchronously, via the acquire
|
|
301
|
-
// logic above) -- nothing else may proceed now.
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (!waiters.length) {
|
|
306
|
-
fileWaiters.delete(filePath);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
callback(null, results);
|
|
311
|
-
});
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
export default SQLiteDatabase;
|