indexeddbshim 17.3.3 → 17.4.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.
Files changed (43) hide show
  1. package/README.md +3 -146
  2. package/dist/CFG.d.ts +1 -0
  3. package/dist/CFG.d.ts.map +1 -1
  4. package/dist/DOMException.d.ts +4 -4
  5. package/dist/DOMException.d.ts.map +1 -1
  6. package/dist/IDBFactory.d.ts.map +1 -1
  7. package/dist/IDBTransaction.d.ts +8 -6
  8. package/dist/IDBTransaction.d.ts.map +1 -1
  9. package/dist/Key.d.ts.map +1 -1
  10. package/dist/Sca.d.ts.map +1 -1
  11. package/dist/indexeddbshim-Key.js +54 -14
  12. package/dist/indexeddbshim-Key.js.map +1 -1
  13. package/dist/indexeddbshim-Key.min.js +2 -2
  14. package/dist/indexeddbshim-Key.min.js.map +1 -1
  15. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +212 -80
  16. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
  17. package/dist/indexeddbshim-UnicodeIdentifiers.js +201 -79
  18. package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
  19. package/dist/indexeddbshim-UnicodeIdentifiers.min.js +3 -3
  20. package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
  21. package/dist/indexeddbshim-node.cjs +212 -80
  22. package/dist/indexeddbshim-node.cjs.map +1 -1
  23. package/dist/indexeddbshim-noninvasive.js +201 -79
  24. package/dist/indexeddbshim-noninvasive.js.map +1 -1
  25. package/dist/indexeddbshim-noninvasive.min.js +3 -3
  26. package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
  27. package/dist/indexeddbshim.js +201 -79
  28. package/dist/indexeddbshim.js.map +1 -1
  29. package/dist/indexeddbshim.min.js +3 -3
  30. package/dist/indexeddbshim.min.js.map +1 -1
  31. package/dist/nodeSQLiteDatabase.d.ts.map +1 -1
  32. package/dist/nodeWebSQL.d.ts.map +1 -1
  33. package/package.json +8 -9
  34. package/src/CFG.js +2 -0
  35. package/src/DOMException.js +21 -21
  36. package/src/IDBFactory.js +88 -39
  37. package/src/IDBObjectStore.js +1 -1
  38. package/src/IDBTransaction.js +7 -1
  39. package/src/Key.js +34 -12
  40. package/src/Sca.js +53 -3
  41. package/src/nodeSQLiteDatabase.js +10 -1
  42. package/src/nodeWebSQL.js +3 -0
  43. package/src/setGlobalVars.js +3 -3
@@ -1 +1 @@
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,CAmEhB;;IAxED;;;;;;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,EAqE7F;IAhEG,mBAAsB;IAkBtB,SA6CE;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;;AA5SD;;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
+ {"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 +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;AA6BrE,qHAAsK;2BAhD3I,yBAAyB"}
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,yBAAyB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indexeddbshim",
3
- "version": "17.3.3",
3
+ "version": "17.4.0",
4
4
  "type": "module",
5
5
  "author": "Parashuram <code@nparashuram.com>",
6
6
  "contributors": [
@@ -47,13 +47,13 @@
47
47
  "clean-mocha": "rimraf D_indexeddbshim_test_database_* D_test.sqlite",
48
48
  "clean-fake": "rimraf D_test0.*",
49
49
  "clean-mock": "rimraf D_test_database*",
50
- "clean-w3c": "rimraf D_^D^B2* D_db* D_test* D_about* D_^I^D^B* D_blank* D_database_name* D_idbtransaction* D_x D_x.sqlite D_y D_y.sqlite D_webworker* D_http*",
50
+ "clean-w3c": "rimraf D_opaque* D_inbox* D_output* D_*_bucket_* D_^D^B2* D_db* D_test* D_about* D_^I^D^B* D_blank* D_database_name* D_idbtransaction* D_x D_x.sqlite D_y D_y.sqlite D_webworker* D_http*",
51
51
  "clean-w3cOld": "rimraf D_db.sqlite* D_test* D_database_name* D_idbtransaction*",
52
52
  "clean-w3c-built": "rimraf test-support/js",
53
53
  "copy": "copyup node_modules/source-map-support/browser-source-map-support.js node_modules/mocha/mocha.css node_modules/mocha/mocha.js node_modules/chai/chai.js node_modules/sinon/pkg/sinon-no-sourcemaps.js test-support",
54
54
  "fix-permissions": "chmod +x node_modules/npm-run-all/bin/*/index.js",
55
55
  "lint": "eslint .",
56
- "eslint": "npm run lint",
56
+ "eslint": "npm run lint --",
57
57
  "sqlite-rebuild": "cd node_modules/sqlite3 && npm i",
58
58
  "w3c-build-only": "node test-support/node-buildjs",
59
59
  "w3c-build": "run-s clean-w3c clean-w3c-built w3c-build-only",
@@ -172,7 +172,7 @@
172
172
  "eventtargeter": "0.12.0",
173
173
  "sync-promise-expanded": "^2.0.0",
174
174
  "typeson": "10.2.0",
175
- "typeson-registry": "14.2.2",
175
+ "typeson-registry": "14.3.0",
176
176
  "websql-configurable": "^3.1.0"
177
177
  },
178
178
  "devDependencies": {
@@ -186,9 +186,9 @@
186
186
  "@rollup/plugin-node-resolve": "^16.0.3",
187
187
  "@rollup/plugin-terser": "^1.0.0",
188
188
  "@types/better-sqlite3": "^9.6.0",
189
- "@types/node": "^26.3.0",
189
+ "@types/node": "^26.4.0",
190
190
  "@types/websql": "^0.0.30",
191
- "@unicode/unicode-17.0.0": "^1.6.17",
191
+ "@unicode/unicode-17.0.0": "^2.0.2",
192
192
  "babel-plugin-add-module-exports": "1.0.4",
193
193
  "bson": "^7.3.2",
194
194
  "builtin-modules": "^5.3.0",
@@ -198,7 +198,6 @@
198
198
  "colors": "1.4.0",
199
199
  "copyfiles": "^2.4.1",
200
200
  "coveradge": "^0.8.2",
201
- "cyclonejs": "1.1.4",
202
201
  "delay-cli": "^3.0.0",
203
202
  "eslint": "^10.9.1",
204
203
  "eslint-config-ash-nazg": "^42.6.0",
@@ -207,14 +206,14 @@
207
206
  "isomorphic-fetch": "^3.0.0",
208
207
  "jsdom": "^30.0.1",
209
208
  "license-badger": "^0.23.0",
210
- "local-xmlhttprequest": "3.0.0",
209
+ "local-xmlhttprequest": "4.0.0",
211
210
  "mocha": "^11.8.0",
212
211
  "mocha-badge-generator": "^0.11.0",
213
212
  "mocha-multi-reporters": "^1.5.1",
214
213
  "npm-run-all": "^4.1.5",
215
214
  "open-cli": "^9.0.0",
216
215
  "rimraf": "^6.1.3",
217
- "rollup": "^4.62.5",
216
+ "rollup": "^4.63.0",
218
217
  "rollup-plugin-filesize": "^10.0.0",
219
218
  "rollup-plugin-node-polyfills": "^0.2.1",
220
219
  "sinon": "^22.1.0",
package/src/CFG.js CHANGED
@@ -43,6 +43,7 @@
43
43
  * sqlBusyTimeout: number,
44
44
  * sqlTrace: () => void,
45
45
  * sqlProfile: () => void,
46
+ * sqlMemoryQuota: number,
46
47
  * escapeNULForSQLiteStatements: boolean,
47
48
  * createIndexes: boolean
48
49
  * }} ConfigValues
@@ -220,6 +221,7 @@ const CFG = /** @type {ConfigValues} */ ({});
220
221
  // Defaults to true except in Node builds where we can preserve literal NUL with better-sqlite3
221
222
  'escapeNULForSQLiteStatements',
222
223
 
224
+ 'sqlMemoryQuota',
223
225
  'createIndexes'
224
226
  ]).forEach((prop) => {
225
227
  /** @type {(val: any) => void} */
@@ -330,15 +330,14 @@ function findError (args) {
330
330
  * optional here and, in practice, always `undefined` today (the `case 4`/
331
331
  * `case 7` branches below are effectively unreachable pending upstream
332
332
  * support for surfacing a real error code).
333
- * @param {Error & {code?: number}} webSQLErr
333
+ * @param {Error & {code?: number|string}} webSQLErr
334
334
  * @returns {(DOMException|Error) & {
335
- * sqlError: Error & {code?: number}
335
+ * sqlError: Error & {code?: number|string}
336
336
  * }|QuotaExceededError}
337
337
  */
338
338
  function webSQLErrback (webSQLErr) {
339
339
  let name, message, useQuotaExceededError = false;
340
- switch (webSQLErr.code) {
341
- case 4: { // SQLError.QUOTA_ERR
340
+ if (webSQLErr.code === 4 || webSQLErr.code === 'SQLITE_FULL') { // SQLError.QUOTA_ERR or better-sqlite3 SQLITE_FULL
342
341
  name = 'QuotaExceededError';
343
342
  message = 'The operation failed because there was not enough ' +
344
343
  'remaining storage space, or the storage quota was reached ' +
@@ -346,22 +345,23 @@ function webSQLErrback (webSQLErr) {
346
345
  if (typeof QuotaExceededError !== 'undefined') {
347
346
  useQuotaExceededError = true;
348
347
  }
349
- break;
350
- }
351
- /*
352
- // Should a WebSQL timeout treat as IndexedDB `TransactionInactiveError` or `UnknownError`?
353
- case 7: { // SQLError.TIMEOUT_ERR
354
- // All transaction errors abort later, so no need to mark inactive
355
- name = 'TransactionInactiveError';
356
- message = 'A request was placed against a transaction which is currently not active, or which is finished (Internal SQL Timeout).';
357
- break;
358
- }
359
- */
360
- default: {
361
- name = 'UnknownError';
362
- message = 'The operation failed for reasons unrelated to the database itself and not covered by any other errors.';
363
- break;
364
- }
348
+ } else {
349
+ switch (webSQLErr.code) {
350
+ /*
351
+ // Should a WebSQL timeout treat as IndexedDB `TransactionInactiveError` or `UnknownError`?
352
+ case 7: { // SQLError.TIMEOUT_ERR
353
+ // All transaction errors abort later, so no need to mark inactive
354
+ name = 'TransactionInactiveError';
355
+ message = 'A request was placed against a transaction which is currently not active, or which is finished (Internal SQL Timeout).';
356
+ break;
357
+ }
358
+ */
359
+ default: {
360
+ name = 'UnknownError';
361
+ message = 'The operation failed for reasons unrelated to the database itself and not covered by any other errors.';
362
+ break;
363
+ }
364
+ }
365
365
  }
366
366
  message += ' (' + webSQLErr.message + ')--(' + webSQLErr.code + ')';
367
367
 
@@ -372,7 +372,7 @@ function webSQLErrback (webSQLErr) {
372
372
  const err =
373
373
  /**
374
374
  * @type {(Error | DOMException) & {
375
- * sqlError: Error & {code?: number}
375
+ * sqlError: Error & {code?: number|string}
376
376
  * }}
377
377
  */
378
378
  (createDOMException(name, message));
package/src/IDBFactory.js CHANGED
@@ -557,6 +557,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
557
557
 
558
558
  const req = IDBOpenDBRequest.__createInstance();
559
559
  let calledDbCreateError = false;
560
+ let isRevertingSysdb = false;
560
561
 
561
562
  if (CFG.autoName && name === '') {
562
563
  // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Necessary?
@@ -585,7 +586,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
585
586
  * @returns {boolean}
586
587
  */
587
588
  function dbCreateError (tx, err) {
588
- if (calledDbCreateError) {
589
+ if (calledDbCreateError || isRevertingSysdb) {
589
590
  return false;
590
591
  }
591
592
  // Defensive cleanup: `pendingVersionChanges.set(name, ...)` (see
@@ -647,46 +648,63 @@ IDBFactory.prototype.open = function (name /* , version */) {
647
648
  */
648
649
  let sysdbFinishedCb = function (systx, err, cb) {
649
650
  if (err) {
650
- try {
651
- systx.executeSql('ROLLBACK', [], cb, cb);
652
- } catch (err) {
653
- // Browser may fail with expired transaction above so
654
- // no choice but to manually revert
655
- sysdb.transaction(function (systx) {
656
- /**
657
- *
658
- * @param {string} msg
659
- * @throws {Error}
660
- * @returns {never}
661
- */
662
- function reportError (msg) {
663
- throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
651
+ /**
652
+ * @param {any} [errorToShow]
653
+ * @returns {void}
654
+ */
655
+ const manualRevert = function (errorToShow) {
656
+ /**
657
+ * @param {string} [msg]
658
+ * @throws {Error}
659
+ * @returns {never}
660
+ */
661
+ function reportError (msg) {
662
+ throw new Error('Unable to roll back upgrade transaction!' + (msg || ''));
663
+ }
664
+
665
+ sysdb.transaction(
666
+ function (systx) {
667
+ // Attempt to revert
668
+ if (oldVersion === 0) {
669
+ systx.executeSql(
670
+ 'DELETE FROM dbVersions WHERE "name" = ?',
671
+ [sqlSafeName]
672
+ );
673
+ } else {
674
+ systx.executeSql(
675
+ 'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
676
+ [oldVersion, sqlSafeName]
677
+ );
678
+ }
679
+ },
680
+ function (sqlErr) {
681
+ isRevertingSysdb = false;
682
+ cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
683
+ },
684
+ function () {
685
+ isRevertingSysdb = false;
686
+ cb(errorToShow || reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
664
687
  }
688
+ );
689
+ };
665
690
 
666
- // Attempt to revert
667
- if (oldVersion === 0) {
668
- systx.executeSql(
669
- 'DELETE FROM dbVersions WHERE "name" = ?',
670
- [sqlSafeName],
671
- function () {
672
- // @ts-expect-error Force to work
673
- cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
674
- },
675
- // @ts-expect-error Force to work
676
- reportError
677
- );
678
- } else {
679
- systx.executeSql(
680
- 'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
681
- [
682
- oldVersion, sqlSafeName
683
- ],
684
- cb,
685
- // @ts-expect-error Force to work
686
- reportError
687
- );
691
+ try {
692
+ systx.executeSql(
693
+ 'ROLLBACK',
694
+ [],
695
+ function () {
696
+ cb(); // eslint-disable-line promise/no-callback-in-promise -- Convenient
697
+ },
698
+ function (tx, sqlErr) {
699
+ // Browser/Node may fail with expired transaction, so manually revert
700
+ manualRevert(sqlErr);
701
+ return false;
688
702
  }
689
- });
703
+ );
704
+ } catch (e) {
705
+ // Browser may fail with expired transaction above so
706
+ // no choice but to manually revert
707
+ manualRevert(e);
690
708
  }
691
709
  return;
692
710
  }
@@ -790,6 +808,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
790
808
 
791
809
  // eslint-disable-next-line camelcase -- Clear API
792
810
  req.transaction.on__preabort = function () {
811
+ isRevertingSysdb = true;
793
812
  pendingVersionChanges.delete(name);
794
813
  connection.__upgradeTransaction = null;
795
814
  // We ensure any cache is deleted before any request error events fire and try to reopen
@@ -808,6 +827,7 @@ IDBFactory.prototype.open = function (name /* , version */) {
808
827
  req.__done = false;
809
828
 
810
829
  connection.close();
830
+ isRevertingSysdb = true;
811
831
  setTimeout(() => {
812
832
  const err = createDOMException('AbortError', 'The upgrade transaction was aborted.');
813
833
  sysdbFinishedCb(systx, err, function (reportError) {
@@ -876,7 +896,36 @@ IDBFactory.prototype.open = function (name /* , version */) {
876
896
  }
877
897
  sysdbFinishedCb = function (systx, err, cb) {
878
898
  if (err) {
879
- rollback(err, cb);
899
+ rollback(err,
900
+ /**
901
+ * @param {Error} [reportError]
902
+ * @returns {void}
903
+ */
904
+ function (reportError) {
905
+ sysdb.transaction(
906
+ function (systx) {
907
+ if (oldVersion === 0) {
908
+ systx.executeSql(
909
+ 'DELETE FROM dbVersions WHERE "name" = ?',
910
+ [sqlSafeName]
911
+ );
912
+ } else {
913
+ systx.executeSql(
914
+ 'UPDATE dbVersions SET "version" = ? WHERE "name" = ?',
915
+ [oldVersion, sqlSafeName]
916
+ );
917
+ }
918
+ },
919
+ function (sqlErr) {
920
+ isRevertingSysdb = false;
921
+ cb(sqlErr); // eslint-disable-line promise/no-callback-in-promise -- Convenient
922
+ },
923
+ function () {
924
+ isRevertingSysdb = false;
925
+ cb(reportError); // eslint-disable-line promise/no-callback-in-promise -- Convenient
926
+ }
927
+ );
928
+ });
880
929
  } else {
881
930
  commit(cb);
882
931
  }
@@ -322,7 +322,7 @@ IDBObjectStore.__createObjectStore = function (db, store) {
322
322
  encodedKeyPath,
323
323
  // For why converting here, see comment and following
324
324
  // discussion at:
325
- // https://github.com/axemclion/IndexedDBShim/issues/313#issuecomment-590086778
325
+ // https://github.com/indexeddbshim/IndexedDBShim/issues/313#issuecomment-590086778
326
326
  Number(store.autoIncrement),
327
327
  '{}',
328
328
  1
@@ -8,6 +8,7 @@ import IDBObjectStore from './IDBObjectStore.js';
8
8
  import CFG from './CFG.js';
9
9
 
10
10
  let uniqueID = 0;
11
+ const activeTransactions = new Set();
11
12
  const listeners = ['onabort', 'oncomplete', 'onerror'];
12
13
  const readonlyProperties = ['objectStoreNames', 'mode', 'durability', 'db', 'error'];
13
14
 
@@ -137,6 +138,7 @@ IDBTransaction.__createInstance = function (db, storeNames, mode, durability = '
137
138
  me.__mode = mode;
138
139
  me.__durability = durability;
139
140
  me.__db = db;
141
+ activeTransactions.add(me);
140
142
  me.__error = null;
141
143
  // @ts-expect-error Part of `ShimEventTarget`
142
144
  me.__setOptions({
@@ -641,6 +643,7 @@ IDBTransaction.prototype.__executeRequests = function () {
641
643
  me.__errored = true;
642
644
  throw e;
643
645
  } finally {
646
+ activeTransactions.delete(me);
644
647
  me.__storeHandles = {};
645
648
  }
646
649
  }
@@ -855,6 +858,7 @@ IDBTransaction.prototype.__abortTransaction = function (err) {
855
858
  });
856
859
  }
857
860
  me.__active = false; // Setting here and in requestsFinished for https://github.com/w3c/IndexedDB/issues/87
861
+ activeTransactions.delete(me);
858
862
 
859
863
  if (err !== null) {
860
864
  me.__error = err;
@@ -1064,7 +1068,7 @@ IDBTransaction.__assertNotFinishedObjectStoreMethod = function (tx) {
1064
1068
  * @returns {void}
1065
1069
  */
1066
1070
  IDBTransaction.__assertActive = function (tx) {
1067
- if (!tx || !tx.__active || tx.__committed) {
1071
+ if (!tx || !tx.__active || !tx.__handlerActive || tx.__committed) {
1068
1072
  throw createDOMException('TransactionInactiveError', 'A request was placed against a transaction which is currently not active, or which is finished');
1069
1073
  }
1070
1074
  };
@@ -1095,4 +1099,6 @@ Object.defineProperty(IDBTransaction, 'prototype', {
1095
1099
  });
1096
1100
  /* eslint-enable unicorn/no-top-level-side-effects -- Would be good */
1097
1101
 
1102
+ IDBTransaction.activeTransactions = activeTransactions;
1103
+
1098
1104
  export default IDBTransaction;
package/src/Key.js CHANGED
@@ -1,3 +1,22 @@
1
+ /**
2
+ * @param {unknown[]} arr
3
+ * @param {number} index
4
+ * @param {unknown} val
5
+ * @returns {void}
6
+ */
7
+ const setArrayValue = (arr, index, val) => {
8
+ if (Reflect.has(Array.prototype, index)) {
9
+ Object.defineProperty(arr, index, {value: val, enumerable: true, writable: true, configurable: true});
10
+ } else {
11
+ arr[index] = val;
12
+ }
13
+ };
14
+ /**
15
+ * @param {unknown[]} arr
16
+ * @param {unknown} val
17
+ * @returns {void}
18
+ */
19
+ const safePush = (arr, val) => setArrayValue(arr, arr.length, val);
1
20
  import {createDOMException} from './DOMException.js';
2
21
  import * as util from './util.js';
3
22
  import cmp from './cmp.js';
@@ -281,12 +300,13 @@ const types = {
281
300
  * @returns {string}
282
301
  */
283
302
  encode (key) {
303
+ /** @type {(string|null)[]} */
284
304
  const encoded = [];
285
305
  for (const [i, item] of key.entries()) {
286
306
  const encodedItem = encode(item, true); // encode the array item
287
- encoded[i] = encodedItem;
307
+ setArrayValue(encoded, i, encodedItem);
288
308
  }
289
- encoded.push(keyTypeToEncodedChar.invalid + '-'); // append an extra item, so empty arrays sort correctly
309
+ safePush(encoded, keyTypeToEncodedChar.invalid + '-'); // append an extra item, so empty arrays sort correctly
290
310
  let encodedKey = JSON.stringify(encoded);
291
311
  if (CFG.escapeNULForSQLiteStatements === false) {
292
312
  encodedKey = encodedKey.replaceAll(String.raw`\u0000`, '\0');
@@ -307,7 +327,7 @@ const types = {
307
327
  for (let i = 0; i < decoded.length; i++) {
308
328
  const item = decoded[i];
309
329
  const decodedItem = decode(item, true); // decode the item
310
- decoded[i] = decodedItem;
330
+ setArrayValue(decoded, i, decodedItem);
311
331
  }
312
332
  return decoded;
313
333
  }
@@ -577,7 +597,7 @@ function convertValueToKeyValueDecoded (input, seen, multiEntry, fullKeys) {
577
597
  } case 'array': { // May throw (from binary)
578
598
  const arr = /** @type {Array<any>} */ (input);
579
599
  const len = arr.length;
580
- seen.push(input);
600
+ safePush(seen, input);
581
601
 
582
602
  /** @type {(KeyValueObject|Value)[]} */
583
603
  const keys = [];
@@ -598,7 +618,7 @@ function convertValueToKeyValueDecoded (input, seen, multiEntry, fullKeys) {
598
618
  (!fullKeys && keys.every((k) => cmp(k, key.value) !== 0)) ||
599
619
  (fullKeys && keys.every((k) => cmp(k, key) !== 0))
600
620
  ) {
601
- keys.push(fullKeys ? key : key.value);
621
+ safePush(keys, fullKeys ? key : key.value);
602
622
  }
603
623
  } catch (err) {
604
624
  if (!multiEntry) {
@@ -732,7 +752,7 @@ function evaluateKeyPathOnValueToDecodedValue (value, keyPath, multiEntry, fullK
732
752
  if (key.failure) {
733
753
  return true;
734
754
  }
735
- result.push(key.value);
755
+ safePush(result, key.value);
736
756
  return false;
737
757
  })
738
758
  ? {failure: true}
@@ -793,11 +813,11 @@ function injectKeyIntoValueUsingKeyPath (value, key, keyPath) {
793
813
  identifiers.forEach((identifier) => {
794
814
  const hop = Object.hasOwn(value, identifier);
795
815
  if (!hop) {
796
- value[identifier] = {};
816
+ Object.defineProperty(value, identifier, {value: {}, enumerable: true, writable: true, configurable: true});
797
817
  }
798
818
  value = value[identifier];
799
819
  });
800
- value[/** @type {string} */ (last)] = key; // key is already a `keyValue` in our processing so no need to convert
820
+ Object.defineProperty(value, /** @type {string} */ (last), {value: key, enumerable: true, writable: true, configurable: true}); // key is already a `keyValue` in our processing so no need to convert
801
821
  }
802
822
 
803
823
  /**
@@ -883,6 +903,7 @@ function isMultiEntryMatch (encodedEntry, encodedKey) {
883
903
  * @returns {Key[]}
884
904
  */
885
905
  function findMultiEntryMatches (keyEntry, range) {
906
+ /** @type {unknown[]} */
886
907
  const matches = [];
887
908
 
888
909
  if (Array.isArray(keyEntry)) {
@@ -896,18 +917,18 @@ function findMultiEntryMatches (keyEntry, range) {
896
917
  } else {
897
918
  const nested = findMultiEntryMatches(key, range);
898
919
  if (nested.length > 0) {
899
- matches.push(key);
920
+ safePush(matches, key);
900
921
  }
901
922
  continue;
902
923
  }
903
924
  }
904
925
 
905
926
  if (util.isNullish(range) || isKeyInRange(key, range, true)) {
906
- matches.push(key);
927
+ safePush(matches, key);
907
928
  }
908
929
  }
909
930
  } else if (util.isNullish(range) || isKeyInRange(keyEntry, range, true)) {
910
- matches.push(keyEntry);
931
+ safePush(matches, keyEntry);
911
932
  }
912
933
  return matches;
913
934
  }
@@ -924,12 +945,13 @@ function convertKeyToValue (key) {
924
945
  case 'number': case 'string': {
925
946
  return value;
926
947
  } case 'array': {
948
+ /** @type {ValueType[]} */
927
949
  const array = [];
928
950
  const len = value.length;
929
951
  let index = 0;
930
952
  while (index < len) {
931
953
  const entry = convertKeyToValue(value[index]);
932
- array[index] = entry;
954
+ setArrayValue(array, index, entry);
933
955
  index++;
934
956
  }
935
957
  return array;
package/src/Sca.js CHANGED
@@ -1,12 +1,62 @@
1
1
  import {
2
- Typeson, hasConstructorOf, structuredCloningThrowing
2
+ Typeson, hasConstructorOf, structuredCloningForStorage
3
3
  } from 'typeson-registry';
4
4
 
5
5
  import {createDOMException, ShimDOMException} from './DOMException.js';
6
6
 
7
7
  // See: https://stackoverflow.com/questions/42170826/categories-for-rejection-by-the-structured-cloning-algorithm
8
8
 
9
- let typeson = new Typeson().register(structuredCloningThrowing);
9
+ // Although typeson-registry already has a FileList type in its structured cloning presets,
10
+ // we need to override it so it works with our tests
11
+
12
+ const specSet = structuredCloningForStorage
13
+ .flatMap((preset) => (Array.isArray(preset) ? preset : [preset]))
14
+ .find((preset) => preset && !Array.isArray(preset) && 'filelist' in preset);
15
+
16
+ const origFileList = specSet && !Array.isArray(specSet) && 'filelist' in specSet
17
+ ? specSet.filelist
18
+ : undefined;
19
+
20
+ const origTest = origFileList && typeof origFileList === 'object' && 'test' in origFileList && typeof origFileList.test === 'function'
21
+ ? origFileList.test
22
+ : undefined;
23
+
24
+ const origRevive = origFileList && typeof origFileList === 'object' && 'revive' in origFileList && typeof origFileList.revive === 'function'
25
+ ? origFileList.revive
26
+ : undefined;
27
+
28
+ const customFileList = origFileList
29
+ ? {
30
+ ...origFileList,
31
+ /**
32
+ * @param {unknown} x
33
+ * @param {import('typeson').StateObject} state
34
+ * @returns {boolean}
35
+ */
36
+ test (x, state) {
37
+ if (typeof FileList !== 'undefined') {
38
+ return x instanceof FileList;
39
+ }
40
+ return typeof origTest === 'function' ? origTest(x, state) : false;
41
+ },
42
+ /**
43
+ * @param {unknown} x
44
+ * @param {import('typeson').StateObject} state
45
+ * @returns {unknown}
46
+ */
47
+ revive (x, state) {
48
+ if (typeof FileList !== 'undefined') {
49
+ return Reflect.construct(FileList, [x]);
50
+ }
51
+ return typeof origRevive === 'function' ? origRevive(x, state) : undefined;
52
+ }
53
+ }
54
+ : undefined;
55
+
56
+ let typeson = new Typeson().register([
57
+ structuredCloningForStorage,
58
+ customFileList ? {filelist: customFileList} : {}
59
+ ]);
10
60
 
11
61
  /**
12
62
  * @param {(preset: import('typeson-registry').Preset) =>
@@ -15,7 +65,7 @@ let typeson = new Typeson().register(structuredCloningThrowing);
15
65
  */
16
66
  function register (func) {
17
67
  // eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- Should be one-time cache
18
- typeson = new Typeson().register(func(structuredCloningThrowing));
68
+ typeson = new Typeson().register(func(structuredCloningForStorage));
19
69
  }
20
70
 
21
71
  /**
@@ -91,7 +91,7 @@ function SQLiteDatabase (name, opts = {}) {
91
91
  _db: db,
92
92
  /**
93
93
  * Compatibility with node-sqlite3's configure API.
94
- * @param {'busyTimeout'|'trace'|'profile'} option
94
+ * @param {'busyTimeout'|'trace'|'profile'|'memoryQuota'} option
95
95
  * @param {number|((sql: string, duration?: number) => void)} value
96
96
  * @returns {void}
97
97
  */
@@ -106,6 +106,15 @@ function SQLiteDatabase (name, opts = {}) {
106
106
  }
107
107
  if (option === 'profile') {
108
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));
109
118
  }
110
119
  },
111
120
  /**
package/src/nodeWebSQL.js CHANGED
@@ -37,6 +37,9 @@ function wrappedSQLiteDatabase (name) {
37
37
  if (CFG.sqlProfile) {
38
38
  db._db.configure('profile', CFG.sqlProfile);
39
39
  }
40
+ if (CFG.sqlMemoryQuota) {
41
+ db._db.configure('memoryQuota', /** @type {number} */ (CFG.sqlMemoryQuota));
42
+ }
40
43
  return db;
41
44
  }
42
45