indexeddbshim 17.0.0 → 17.2.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 (58) hide show
  1. package/README.md +62 -0
  2. package/badges/licenses-badge-dev.svg +1 -1
  3. package/badges/licenses-badge.svg +1 -1
  4. package/dist/CFG.d.ts +1 -0
  5. package/dist/CFG.d.ts.map +1 -1
  6. package/dist/DOMException.d.ts.map +1 -1
  7. package/dist/IDBCursor.d.ts +25 -18
  8. package/dist/IDBCursor.d.ts.map +1 -1
  9. package/dist/IDBFactory.d.ts +19 -19
  10. package/dist/IDBFactory.d.ts.map +1 -1
  11. package/dist/IDBIndex.d.ts.map +1 -1
  12. package/dist/IDBKeyRange.d.ts +5 -5
  13. package/dist/IDBKeyRange.d.ts.map +1 -1
  14. package/dist/IDBTransaction.d.ts +24 -3
  15. package/dist/IDBTransaction.d.ts.map +1 -1
  16. package/dist/Key.d.ts +41 -41
  17. package/dist/Key.d.ts.map +1 -1
  18. package/dist/indexeddbshim-Key.js +88 -69
  19. package/dist/indexeddbshim-Key.js.map +1 -1
  20. package/dist/indexeddbshim-Key.min.js +2 -2
  21. package/dist/indexeddbshim-Key.min.js.map +1 -1
  22. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs +1682 -1056
  23. package/dist/indexeddbshim-UnicodeIdentifiers-node.cjs.map +1 -1
  24. package/dist/indexeddbshim-UnicodeIdentifiers.js +886 -663
  25. package/dist/indexeddbshim-UnicodeIdentifiers.js.map +1 -1
  26. package/dist/indexeddbshim-UnicodeIdentifiers.min.js +4 -4
  27. package/dist/indexeddbshim-UnicodeIdentifiers.min.js.map +1 -1
  28. package/dist/indexeddbshim-node.cjs +1682 -1056
  29. package/dist/indexeddbshim-node.cjs.map +1 -1
  30. package/dist/indexeddbshim-noninvasive.js +886 -663
  31. package/dist/indexeddbshim-noninvasive.js.map +1 -1
  32. package/dist/indexeddbshim-noninvasive.min.js +4 -4
  33. package/dist/indexeddbshim-noninvasive.min.js.map +1 -1
  34. package/dist/indexeddbshim.js +890 -660
  35. package/dist/indexeddbshim.js.map +1 -1
  36. package/dist/indexeddbshim.min.js +4 -4
  37. package/dist/indexeddbshim.min.js.map +1 -1
  38. package/dist/node-UnicodeIdentifiers.d.ts.map +1 -1
  39. package/dist/node.d.ts.map +1 -1
  40. package/dist/nodeSQLiteDatabase.d.ts +65 -0
  41. package/dist/nodeSQLiteDatabase.d.ts.map +1 -0
  42. package/dist/nodeWebSQL.d.ts.map +1 -1
  43. package/dist/util.d.ts +5 -0
  44. package/dist/util.d.ts.map +1 -1
  45. package/package.json +22 -18
  46. package/src/CFG.js +3 -0
  47. package/src/DOMException.js +10 -10
  48. package/src/IDBCursor.js +152 -102
  49. package/src/IDBFactory.js +30 -26
  50. package/src/IDBIndex.js +1 -2
  51. package/src/IDBKeyRange.js +5 -5
  52. package/src/IDBTransaction.js +35 -5
  53. package/src/Key.js +63 -55
  54. package/src/node-UnicodeIdentifiers.js +5 -1
  55. package/src/node.js +5 -1
  56. package/src/nodeSQLiteDatabase.js +174 -0
  57. package/src/nodeWebSQL.js +1 -3
  58. package/src/util.js +62 -3
@@ -1 +1 @@
1
- {"version":3,"file":"node-UnicodeIdentifiers.d.ts","sourceRoot":"","sources":["../src/node-UnicodeIdentifiers.js"],"names":[],"mappings":";AASA;;;;GAIG;AACH,sCAJW,OAAO,oBAAoB,EAAE,aAAa,wBAExC,OAAO,oBAAoB,EAAE,aAAa,GAAC,MAAM,CAW7D"}
1
+ {"version":3,"file":"node-UnicodeIdentifiers.d.ts","sourceRoot":"","sources":["../src/node-UnicodeIdentifiers.js"],"names":[],"mappings":";AASA;;;;GAIG;AACH,sCAJW,OAAO,oBAAoB,EAAE,aAAa,wBAExC,OAAO,oBAAoB,EAAE,aAAa,GAAC,MAAM,CAe7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.js"],"names":[],"mappings":";AAQA;;;;GAIG;AACH,sCAJW,OAAO,oBAAoB,EAAE,aAAa,wBAExC,OAAO,oBAAoB,EAAE,aAAa,GAAC,MAAM,CAI7D"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.js"],"names":[],"mappings":";AAQA;;;;GAIG;AACH,sCAJW,OAAO,oBAAoB,EAAE,aAAa,wBAExC,OAAO,oBAAoB,EAAE,aAAa,GAAC,MAAM,CAQ7D"}
@@ -0,0 +1,65 @@
1
+ export default SQLiteDatabase;
2
+ export type SQLProfileCallback = (sql: string, duration: number) => void;
3
+ export type SQLTraceCallback = ((sql: string) => void) | undefined;
4
+ /**
5
+ * @typedef {(sql: string, duration: number) => void} SQLProfileCallback
6
+ */
7
+ /**
8
+ * @typedef {((sql: string) => void)|undefined} SQLTraceCallback
9
+ */
10
+ /**
11
+ * @param {string} name
12
+ * @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
13
+ * @returns {void}
14
+ */
15
+ declare function SQLiteDatabase(name: string, opts?: {
16
+ busyTimeout?: number;
17
+ trace?: (sql: string) => void;
18
+ profile?: SQLProfileCallback;
19
+ }): void;
20
+ declare class SQLiteDatabase {
21
+ /**
22
+ * @typedef {(sql: string, duration: number) => void} SQLProfileCallback
23
+ */
24
+ /**
25
+ * @typedef {((sql: string) => void)|undefined} SQLTraceCallback
26
+ */
27
+ /**
28
+ * @param {string} name
29
+ * @param {{busyTimeout?: number, trace?: (sql: string) => void, profile?: SQLProfileCallback}} [opts]
30
+ * @returns {void}
31
+ */
32
+ constructor(name: string, opts?: {
33
+ busyTimeout?: number;
34
+ trace?: (sql: string) => void;
35
+ profile?: SQLProfileCallback;
36
+ });
37
+ _db: any;
38
+ /**
39
+ * @param {{sql: string, args: unknown[]}[]} queries
40
+ * @param {boolean} readOnly
41
+ * @param {(err: Error|null, results?: SQLiteResult[]) => void} callback
42
+ * @returns {void}
43
+ */
44
+ exec(queries: {
45
+ sql: string;
46
+ args: unknown[];
47
+ }[], readOnly: boolean, callback: (err: Error | null, results?: SQLiteResult[]) => void): void;
48
+ }
49
+ /**
50
+ *
51
+ */
52
+ declare class SQLiteResult {
53
+ /**
54
+ * @param {Error|null|undefined} error
55
+ * @param {number|undefined} [insertId]
56
+ * @param {number} [rowsAffected]
57
+ * @param {object[]} [rows]
58
+ */
59
+ constructor(error: Error | null | undefined, insertId?: number | undefined, rowsAffected?: number, rows?: object[]);
60
+ error: Error | null | undefined;
61
+ insertId: number | undefined;
62
+ rowsAffected: number | undefined;
63
+ rows: object[] | undefined;
64
+ }
65
+ //# sourceMappingURL=nodeSQLiteDatabase.d.ts.map
@@ -0,0 +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;AAL9C;;GAEG;AAEH;;GAEG;AAEH;;;;GAIG;AACH,sCAJW,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,GACjF,IAAI,CAkEhB;;IA7ED;;OAEG;IAEH;;OAEG;IAEH;;;;OAIG;IACH,kBAJW,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,EAmE7F;IA9CG,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,CA0ChB;;AAzKD;;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":";AAwBA,iHAAiE"}
1
+ {"version":3,"file":"nodeWebSQL.d.ts","sourceRoot":"","sources":["../src/nodeWebSQL.js"],"names":[],"mappings":";AAsBA,iHAAiE"}
package/dist/util.d.ts CHANGED
@@ -172,4 +172,9 @@ export function convertToSequenceDOMString(val: AnyValue): string | string[];
172
172
  * @returns {v is null|undefined}
173
173
  */
174
174
  export function isNullish(v: AnyValue): v is null | undefined;
175
+ /**
176
+ * @param {() => void} fn
177
+ * @returns {void}
178
+ */
179
+ export function runContinuationSafely(fn: () => void): void;
175
180
  //# sourceMappingURL=util.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.js"],"names":[],"mappings":"sBAKa,MAAM;;uBAofN,GAAG;AA1chB;;;;GAIG;AACH,2CAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;GAGG;AACH,4CAHW,MAAM,GACJ,MAAM,CAUlB;AAuBD;;;;GAIG;AACH,qDAJW,MAAM,GAEJ,MAAM,CAoClB;AAmBD;;;;GAIG;AACH,uDAHW,MAAM,GACJ,MAAM,CAkClB;AAED;;;GAGG;AACH,6CAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;GAGG;AACH,6CAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;GAGG;AACH,sDAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,mCAHW,MAAM,GACJ,MAAM,CAKlB;AAzID;;;GAGG;AACH,8BAHW,MAAM,GACJ,MAAM,CAIlB;AAqID;;GAEG;AAGH;;;;;GAKG;AACH,gCAJW,QAAQ,QACR,QAAQ,GACN,OAAO,CAInB;AAED;;;;GAIG;AACH,2BAHW,QAAQ,GACN,GAAG,IAAI,MAAM,CAIzB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAInB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,8BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,8BAHW,QAAQ,GACN,OAAO,CAOnB;AAED;;;;GAIG;AACH,gCAHW,QAAQ,GACN,OAAO,CAOnB;AAED;;;;;GAKG;AACH,0CAJW,MAAM,SACN,MAAM,EAAE,GACN,IAAI,CAkBhB;AAED;;;;;GAKG;AACH,kDAJW,MAAM,SACN,MAAM,EAAE,GACN,IAAI,CAchB;AAED;;;;;;;GAOG;AACH,8CANW,MAAM,GAAG;IACnB,CAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,aACO,MAAM,EAAE,GACN,IAAI,CA2BhB;AAED;;;;;;;;GAQG;AACH,8CAPW,MAAM,SACN,MAAM,GAAC,MAAM,EAAE,WACf,IAAI,GAAC;IACf,CAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,GACS,IAAI,CA8BhB;AAgCD;;;;GAIG;AACH,wCAHW,MAAM,GAAC,MAAM,EAAE,GACb,OAAO,CAWnB;AAED;;;;;GAKG;AACH,qCALW,MAAM,QACN,oBAAoB,GAAC,eAAe,GAElC,MAAM,CAyBlB;AAED;;GAEG;AAEH;;;;GAIG;AACH,sCAJW,QAAQ,gBACR,OAAO,GACL,MAAM,CAIlB;AAWD;;;;GAIG;AACH,gDAHW,QAAQ,GACN,MAAM,GAAC,MAAM,EAAE,CAW3B;AAED;;;GAGG;AACH,6BAHW,QAAQ,GACN,CAAC,IAAI,IAAI,GAAC,SAAS,CAI/B"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.js"],"names":[],"mappings":"sBAKa,MAAM;;uBA6fN,GAAG;AAndhB;;;;GAIG;AACH,2CAHW,MAAM,GACJ,MAAM,CASlB;AAED;;;GAGG;AACH,4CAHW,MAAM,GACJ,MAAM,CAclB;AAuBD;;;;GAIG;AACH,qDAJW,MAAM,GAEJ,MAAM,CAoClB;AAmBD;;;;GAIG;AACH,uDAHW,MAAM,GACJ,MAAM,CAkClB;AAED;;;GAGG;AACH,6CAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;GAGG;AACH,6CAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;GAGG;AACH,sDAHW,MAAM,GACJ,MAAM,CAIlB;AAED;;;;GAIG;AACH,mCAHW,MAAM,GACJ,MAAM,CAKlB;AAzID;;;GAGG;AACH,8BAHW,MAAM,GACJ,MAAM,CAIlB;AAqID;;GAEG;AAGH;;;;;GAKG;AACH,gCAJW,QAAQ,QACR,QAAQ,GACN,OAAO,CAInB;AAED;;;;GAIG;AACH,2BAHW,QAAQ,GACN,GAAG,IAAI,MAAM,CAIzB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAInB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,8BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;GAIG;AACH,8BAHW,QAAQ,GACN,OAAO,CAOnB;AAED;;;;GAIG;AACH,gCAHW,QAAQ,GACN,OAAO,CAOnB;AAED;;;;;GAKG;AACH,0CAJW,MAAM,SACN,MAAM,EAAE,GACN,IAAI,CAkBhB;AAED;;;;;GAKG;AACH,kDAJW,MAAM,SACN,MAAM,EAAE,GACN,IAAI,CAchB;AAED;;;;;;;GAOG;AACH,8CANW,MAAM,GAAG;IACnB,CAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,aACO,MAAM,EAAE,GACN,IAAI,CA2BhB;AAED;;;;;;;;GAQG;AACH,8CAPW,MAAM,SACN,MAAM,GAAC,MAAM,EAAE,WACf,IAAI,GAAC;IACf,CAAK,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB,GACS,IAAI,CA8BhB;AAgCD;;;;GAIG;AACH,wCAHW,MAAM,GAAC,MAAM,EAAE,GACb,OAAO,CAWnB;AAED;;;;;GAKG;AACH,qCALW,MAAM,QACN,oBAAoB,GAAC,eAAe,GAElC,MAAM,CAyBlB;AAED;;GAEG;AAEH;;;;GAIG;AACH,sCAJW,QAAQ,gBACR,OAAO,GACL,MAAM,CAIlB;AAWD;;;;GAIG;AACH,gDAHW,QAAQ,GACN,MAAM,GAAC,MAAM,EAAE,CAW3B;AAED;;;GAGG;AACH,6BAHW,QAAQ,GACN,CAAC,IAAI,IAAI,GAAC,SAAS,CAI/B;AA6BD;;;GAGG;AACH,0CAHW,MAAM,IAAI,GACR,IAAI,CAmBhB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "indexeddbshim",
3
- "version": "17.0.0",
3
+ "version": "17.2.0",
4
4
  "type": "module",
5
5
  "author": "Parashuram <code@nparashuram.com>",
6
6
  "contributors": [
@@ -156,17 +156,18 @@
156
156
  "node": "^22.18.0 || >= 24.11.0"
157
157
  },
158
158
  "dependencies": {
159
+ "@node-static/node-static": "^0.9.0",
160
+ "better-sqlite3": "^13.0.3",
159
161
  "canvas": "^3.2.3",
160
162
  "eventtargeter": "0.9.0",
161
- "sync-promise-expanded": "^1.0.0",
162
- "typeson": "10.0.0",
163
- "typeson-registry": "13.0.0",
163
+ "sync-promise-expanded": "^2.0.0",
164
+ "typeson": "10.2.0",
165
+ "typeson-registry": "13.1.0",
164
166
  "websql-configurable": "^3.0.3"
165
167
  },
166
168
  "devDependencies": {
167
169
  "@babel/core": "^8.0.1",
168
170
  "@babel/preset-env": "^8.0.2",
169
- "@brettz9/node-static": "^0.1.1",
170
171
  "@es-joy/json-file-reporter": "^1.5.0",
171
172
  "@rollup/plugin-babel": "^7.1.0",
172
173
  "@rollup/plugin-commonjs": "^29.0.3",
@@ -174,36 +175,37 @@
174
175
  "@rollup/plugin-json": "^6.1.0",
175
176
  "@rollup/plugin-node-resolve": "^16.0.3",
176
177
  "@rollup/plugin-terser": "^1.0.0",
177
- "@types/node": "^26.1.2",
178
+ "@types/better-sqlite3": "^9.6.0",
179
+ "@types/node": "^26.2.0",
178
180
  "@types/websql": "^0.0.30",
179
181
  "@unicode/unicode-17.0.0": "^1.6.17",
180
182
  "babel-plugin-add-module-exports": "1.0.4",
181
- "bson": "^7.3.1",
183
+ "bson": "^7.3.2",
182
184
  "builtin-modules": "^5.3.0",
183
185
  "c8": "^12.0.0",
184
186
  "chai": "6.2.2",
185
187
  "chokidar-cli": "^3.0.0",
186
188
  "colors": "1.4.0",
187
189
  "copyfiles": "^2.4.1",
188
- "core-js-bundle": "^3.49.0",
190
+ "core-js-bundle": "^3.50.0",
189
191
  "coveradge": "^0.8.2",
190
192
  "cyclonejs": "1.1.4",
191
193
  "delay-cli": "^3.0.0",
192
- "eslint": "^10.8.0",
193
- "eslint-config-ash-nazg": "^41.0.0",
194
- "globals": "^17.8.0",
194
+ "eslint": "^10.8.1",
195
+ "eslint-config-ash-nazg": "^42.5.0",
196
+ "globals": "^17.11.0",
195
197
  "is-date-object": "1.1.0",
196
198
  "isomorphic-fetch": "^3.0.0",
197
- "jsdom": "^30.0.0",
198
- "license-badger": "^0.22.1",
199
+ "jsdom": "^30.0.1",
200
+ "license-badger": "^0.23.0",
199
201
  "local-xmlhttprequest": "2.0.0",
200
- "mocha": "^11.7.6",
202
+ "mocha": "^11.8.0",
201
203
  "mocha-badge-generator": "^0.11.0",
202
204
  "mocha-multi-reporters": "^1.5.1",
203
205
  "npm-run-all": "^4.1.5",
204
206
  "open-cli": "^9.0.0",
205
207
  "rimraf": "^6.1.3",
206
- "rollup": "^4.62.3",
208
+ "rollup": "^4.62.4",
207
209
  "rollup-plugin-filesize": "^10.0.0",
208
210
  "rollup-plugin-node-polyfills": "^0.2.1",
209
211
  "sinon": "^22.1.0",
@@ -211,14 +213,16 @@
211
213
  "sourcemap-transformer": "git+https://github.com/brettz9/sourcemap-transformer.git",
212
214
  "typescript": "^6.0.3",
213
215
  "w3c-blob": "0.0.1",
214
- "ws": "^8.21.1"
216
+ "ws": "^8.21.3"
215
217
  },
216
218
  "allowScripts": {
217
219
  "es5-ext@0.10.64": true,
218
220
  "fsevents@2.3.3": true,
219
221
  "sqlite3@5.1.7": true,
220
222
  "canvas@3.2.3": true,
221
- "core-js-bundle@3.49.0": true,
222
- "unrs-resolver@1.12.2": true
223
+ "unrs-resolver@1.12.2": true,
224
+ "better-sqlite3@13.0.3": true,
225
+ "core-js-bundle@3.50.0": true,
226
+ "is-hidden-file@1.1.2": true
223
227
  }
224
228
  }
package/src/CFG.js CHANGED
@@ -42,6 +42,7 @@
42
42
  * sqlBusyTimeout: number,
43
43
  * sqlTrace: () => void,
44
44
  * sqlProfile: () => void,
45
+ * escapeNULForSQLiteStatements: boolean,
45
46
  * createIndexes: boolean
46
47
  * }} ConfigValues
47
48
  */
@@ -215,6 +216,8 @@ const CFG = /** @type {ConfigValues} */ ({});
215
216
  'sqlBusyTimeout', // Defaults to 1000
216
217
  'sqlTrace', // Callback not used by default
217
218
  'sqlProfile', // Callback not used by default
219
+ // Defaults to true except in Node builds where we can preserve literal NUL with better-sqlite3
220
+ 'escapeNULForSQLiteStatements',
218
221
 
219
222
  'createIndexes'
220
223
  ]).forEach((prop) => {
@@ -368,22 +368,22 @@ try {
368
368
  const createDOMException = useNativeDOMException
369
369
  // eslint-disable-next-line @stylistic/operator-linebreak -- Need JSDoc
370
370
  ? /**
371
- * @param {string} name
372
- * @param {string} message
373
- * @param {ErrorLike} [error]
374
- * @returns {DOMException}
375
- */
371
+ * @param {string} name
372
+ * @param {string} message
373
+ * @param {ErrorLike} [error]
374
+ * @returns {DOMException}
375
+ */
376
376
  function (name, message, error) {
377
377
  logError(name, message, error);
378
378
  return createNativeDOMException(name, message);
379
379
  }
380
380
  // eslint-disable-next-line @stylistic/operator-linebreak -- Need JSDoc
381
381
  : /**
382
- * @param {string} name
383
- * @param {string} message
384
- * @param {ErrorLike} [error]
385
- * @returns {Error}
386
- */
382
+ * @param {string} name
383
+ * @param {string} message
384
+ * @param {ErrorLike} [error]
385
+ * @returns {Error}
386
+ */
387
387
  function (name, message, error) {
388
388
  logError(name, message, error);
389
389
  return createNonNativeDOMException(name, message);
package/src/IDBCursor.js CHANGED
@@ -46,6 +46,9 @@ import CFG from './CFG.js';
46
46
  * __unique: boolean,
47
47
  * __sqlDirection: "DESC"|"ASC",
48
48
  * __matchedKeys: {[key: string]: true},
49
+ * __continuationKey: import('./Key.js').Key|undefined,
50
+ * __continuationPrimaryKey: import('./Key.js').Key|undefined,
51
+ * __multiEntryExhausted: boolean,
49
52
  * __invalidateCache: () => void
50
53
  * }} IDBCursorFull
51
54
  */
@@ -117,6 +120,9 @@ IDBCursor.__super = function IDBCursor (query, direction, store, source, keyColu
117
120
  this.__valueDecoder = this.__keyOnly ? Key : Sca;
118
121
  this.__count = count;
119
122
  this.__prefetchedIndex = -1;
123
+ this.__continuationKey = undefined;
124
+ this.__continuationPrimaryKey = undefined;
125
+ this.__multiEntryExhausted = false;
120
126
  this.__multiEntryIndex = this.__indexSource
121
127
  ? 'multiEntry' in source && source.multiEntry
122
128
  : false;
@@ -270,77 +276,107 @@ const leftBracketRegex = /\[/gu;
270
276
  * @param {SQLTransaction} tx
271
277
  * @param {KeySuccess} success
272
278
  * @param {FindError} error
273
- * @param {Integer|undefined} recordsToLoad
279
+ * @param {Integer} [recordsToLoad]
274
280
  * @this {IDBCursorFull}
275
281
  * @returns {void}
276
282
  */
277
- IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, error, recordsToLoad) {
283
+ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, error, recordsToLoad = 1) {
278
284
  const me = this;
279
285
 
280
- if (me.__prefetchedData && me.__prefetchedData.length === me.__prefetchedIndex) {
281
- if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); }
286
+ if (me.__multiEntryExhausted) {
287
+ if (CFG.DEBUG) { console.log('[multiEntry] Reached end of multiEntry cursor (already exhausted)'); }
282
288
  success(undefined, undefined, undefined);
283
289
  return;
284
290
  }
285
291
 
286
292
  const quotedKeyColumnName = util.sqlQuote(me.__keyColumnName);
287
- const sql = ['SELECT * FROM', util.escapeStoreNameForSQL(me.__store.__currentName)];
288
- /** @type {string[]} */
289
- const sqlValues = [];
290
- sql.push('WHERE', quotedKeyColumnName, 'NOT NULL');
291
- if (me.__range && (me.__range.lower !== undefined && Array.isArray(me.__range.upper))) {
292
- if (me.__range.upper.indexOf(me.__range.lower) === 0) {
293
- sql.push('AND', quotedKeyColumnName, "LIKE ? ESCAPE '^'");
294
- sqlValues.push(
295
- '%' + util.sqlLIKEEscape(
296
- /** @type {string} */ (me.__range.__lowerCached).slice(0, -1)
297
- ) + '%'
298
- );
299
- }
300
- }
293
+ const quotedKey = util.sqlQuote('key');
301
294
 
302
295
  // Determine the ORDER BY direction based on the cursor.
303
296
  const direction = me.__sqlDirection;
304
297
  const op = direction === 'ASC' ? '>' : '<';
305
- const quotedKey = util.sqlQuote('key');
306
298
 
307
- if (primaryKey !== undefined) {
308
- sql.push('AND', quotedKey, op + '= ?');
309
- // Key.convertValueToKey(primaryKey); // Already checked by `continuePrimaryKey`
310
- sqlValues.push(/** @type {string} */ (Key.encode(primaryKey)));
311
- }
312
- if (key !== undefined) {
313
- sql.push('AND', quotedKeyColumnName, op + '= ?');
314
- // Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey`
315
- sqlValues.push(/** @type {string} */ (Key.encode(key)));
316
- } else if (me.__key !== undefined) {
317
- sql.push('AND', quotedKeyColumnName, op + ' ?');
318
- // Key.convertValueToKey(me.__key); // Already checked when entered
319
- sqlValues.push(/** @type {string} */ (Key.encode(me.__key)));
320
- }
321
-
322
- if (!me.__count) {
323
- // 1. Sort by key
324
- sql.push('ORDER BY', quotedKeyColumnName, direction);
299
+ /**
300
+ * Runs (and, if a batch of underlying rows produces no matching
301
+ * multi-entry values, repeatedly re-runs) the query for the next batch
302
+ * of underlying rows, advancing `me.__continuationKey`/
303
+ * `me.__continuationPrimaryKey` (tracking the last *physical* row
304
+ * scanned) each time, until either some matches are found or the
305
+ * underlying table is exhausted.
306
+ * @returns {void}
307
+ */
308
+ function runQuery () {
309
+ const sql = ['SELECT * FROM', util.escapeStoreNameForSQL(me.__store.__currentName)];
310
+ /** @type {string[]} */
311
+ const sqlValues = [];
312
+ sql.push('WHERE', quotedKeyColumnName, 'NOT NULL');
313
+ if (me.__range && (me.__range.lower !== undefined && Array.isArray(me.__range.upper))) {
314
+ if (me.__range.upper.indexOf(me.__range.lower) === 0) {
315
+ sql.push('AND', quotedKeyColumnName, "LIKE ? ESCAPE '^'");
316
+ sqlValues.push(
317
+ '%' + util.sqlLIKEEscape(
318
+ /** @type {string} */ (me.__range.__lowerCached).slice(0, -1)
319
+ ) + '%'
320
+ );
321
+ }
322
+ }
325
323
 
326
- // 2. Sort by primaryKey (if defined and not unique)
327
- if (!me.__unique && me.__keyColumnName !== 'key') { // Avoid adding 'key' twice
328
- sql.push(',', util.sqlQuote('key'), direction);
324
+ if (primaryKey !== undefined) {
325
+ sql.push('AND', quotedKey, op + '= ?');
326
+ // Key.convertValueToKey(primaryKey); // Already checked by `continuePrimaryKey`
327
+ sqlValues.push(/** @type {string} */ (Key.encode(primaryKey)));
328
+ }
329
+ if (key !== undefined) {
330
+ sql.push('AND', quotedKeyColumnName, op + '= ?');
331
+ // Key.convertValueToKey(key); // Already checked by `continue` or `continuePrimaryKey`
332
+ sqlValues.push(/** @type {string} */ (Key.encode(key)));
333
+ } else if (me.__continuationKey !== undefined) {
334
+ // Resume from the last underlying (physical) row we scanned, not
335
+ // from the last *matching* multi-entry value (which lives in a
336
+ // different key-space than the raw index column and cannot be
337
+ // reliably compared against it, e.g. for array index keys).
338
+ sql.push(
339
+ 'AND (', quotedKeyColumnName, op, '?',
340
+ 'OR (', quotedKeyColumnName, '= ?', 'AND', quotedKey, op, '?))'
341
+ );
342
+ const encodedContinuationKey = /** @type {string} */ (
343
+ Key.encode(me.__continuationKey, true)
344
+ );
345
+ sqlValues.push(
346
+ encodedContinuationKey,
347
+ encodedContinuationKey,
348
+ /** @type {string} */ (Key.encode(me.__continuationPrimaryKey))
349
+ );
329
350
  }
330
351
 
331
- // 3. Sort by position (if defined)
352
+ if (!me.__count) {
353
+ // 1. Sort by key
354
+ sql.push('ORDER BY', quotedKeyColumnName, direction);
332
355
 
333
- if (!me.__unique && me.__indexSource) {
334
- // 4. Sort by object store position (if defined and not unique)
335
- sql.push(',', util.sqlQuote(me.__valueColumnName), direction);
356
+ // 2. Sort by primaryKey (if defined and not unique)
357
+ if (!me.__unique && me.__keyColumnName !== 'key') { // Avoid adding 'key' twice
358
+ sql.push(',', util.sqlQuote('key'), direction);
359
+ }
360
+
361
+ // 3. Sort by position (if defined)
362
+
363
+ if (!me.__unique && me.__indexSource) {
364
+ // 4. Sort by object store position (if defined and not unique)
365
+ sql.push(',', util.sqlQuote(me.__valueColumnName), direction);
366
+ }
367
+ sql.push('LIMIT', String(recordsToLoad));
336
368
  }
337
- sql.push('LIMIT', String(recordsToLoad));
338
- }
339
- const sqlStr = sql.join(' ');
340
- if (CFG.DEBUG) { console.log(sqlStr, sqlValues); }
369
+ const sqlStr = sql.join(' ');
370
+ if (CFG.DEBUG) { console.log('[multiEntry] query', sqlStr, sqlValues); }
371
+
372
+ tx.executeSql(sqlStr, sqlValues, function (tx, data) {
373
+ if (data.rows.length === 0) {
374
+ me.__multiEntryExhausted = true;
375
+ if (CFG.DEBUG) { console.log('[multiEntry] Reached end of multiEntry cursor (no more rows)'); }
376
+ success(undefined, undefined, undefined);
377
+ return;
378
+ }
341
379
 
342
- tx.executeSql(sqlStr, sqlValues, function (tx, data) {
343
- if (data.rows.length > 0) {
344
380
  if (me.__count) { // Avoid caching and other processing below
345
381
  let ct = 0;
346
382
  for (let i = 0; i < data.rows.length; i++) {
@@ -352,6 +388,17 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
352
388
  success(undefined, ct, undefined);
353
389
  return;
354
390
  }
391
+
392
+ // Track how far we've physically scanned, regardless of whether
393
+ // this batch produced any matches, so the next batch (if any)
394
+ // resumes after this one instead of re-scanning or stopping early.
395
+ const lastRawRow = data.rows.item(data.rows.length - 1);
396
+ me.__continuationKey = Key.decode(lastRawRow[me.__keyColumnName], true);
397
+ me.__continuationPrimaryKey = Key.decode(lastRawRow.key);
398
+ if (data.rows.length < recordsToLoad) {
399
+ me.__multiEntryExhausted = true;
400
+ }
401
+
355
402
  const rows = [];
356
403
  for (let i = 0; i < data.rows.length; i++) {
357
404
  const rowItem = data.rows.item(i);
@@ -373,6 +420,18 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
373
420
  rows.push(clone);
374
421
  }
375
422
  }
423
+
424
+ if (rows.length === 0) {
425
+ if (me.__multiEntryExhausted) {
426
+ if (CFG.DEBUG) { console.log('[multiEntry] Reached end of multiEntry cursor (last batch had no matches)'); }
427
+ success(undefined, undefined, undefined);
428
+ return;
429
+ }
430
+ if (CFG.DEBUG) { console.log('[multiEntry] batch had no matches; fetching next batch'); }
431
+ runQuery();
432
+ return;
433
+ }
434
+
376
435
  const reverse = me.direction.indexOf('prev') === 0;
377
436
  rows.sort(function (a, b) {
378
437
  if (a.matchingKey.replaceAll(leftBracketRegex, 'z') < b.matchingKey.replaceAll(leftBracketRegex, 'z')) {
@@ -390,37 +449,28 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
390
449
  return 0;
391
450
  });
392
451
 
393
- if (rows.length > 1) {
394
- me.__prefetchedIndex = 0;
395
- me.__prefetchedData = {
396
- data: rows,
397
- length: rows.length,
398
- /**
399
- * @param {Integer} index
400
- * @returns {RowItemNonNull}
401
- */
402
- item (index) {
403
- return this.data[index];
404
- }
405
- };
406
- if (CFG.DEBUG) { console.log('Preloaded ' + me.__prefetchedData.length + ' records for multiEntry cursor'); }
407
- me.__decode(rows[0], success);
408
- } else if (rows.length === 1) {
409
- if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); }
410
- me.__decode(rows[0], success);
411
- } else {
412
- if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); }
413
- success(undefined, undefined, undefined);
414
- }
415
- } else {
416
- if (CFG.DEBUG) { console.log('Reached end of multiEntry cursor'); }
417
- success(undefined, undefined, undefined);
418
- }
419
- }, function (tx, err) {
420
- if (CFG.DEBUG) { console.log('Could not execute Cursor.continue', sqlStr, sqlValues); }
421
- error(err);
422
- return false;
423
- });
452
+ me.__prefetchedIndex = 0;
453
+ me.__prefetchedData = {
454
+ data: rows,
455
+ length: rows.length,
456
+ /**
457
+ * @param {Integer} index
458
+ * @returns {RowItemNonNull}
459
+ */
460
+ item (index) {
461
+ return this.data[index];
462
+ }
463
+ };
464
+ if (CFG.DEBUG) { console.log('[multiEntry] Preloaded ' + me.__prefetchedData.length + ' records for multiEntry cursor'); }
465
+ me.__decode(rows[0], success);
466
+ }, function (tx, err) {
467
+ if (CFG.DEBUG) { console.log('[multiEntry] Could not execute Cursor.continue', sqlStr, sqlValues); }
468
+ error(err);
469
+ return false;
470
+ });
471
+ }
472
+
473
+ runQuery();
424
474
  };
425
475
 
426
476
  /**
@@ -432,19 +482,19 @@ IDBCursor.prototype.__findMultiEntry = function (key, primaryKey, tx, success, e
432
482
  */
433
483
 
434
484
  /**
435
- * @callback SuccessArg
436
- * @param {StructuredCloneValue} value
437
- * @param {import('./IDBRequest.js').IDBRequestFull} req
438
- * @returns {void}
439
- */
485
+ * @callback SuccessArg
486
+ * @param {StructuredCloneValue} value
487
+ * @param {import('./IDBRequest.js').IDBRequestFull} req
488
+ * @returns {void}
489
+ */
440
490
 
441
491
  /**
442
- * @callback SuccessCallback
443
- * @param {IndexedDBKey} key
444
- * @param {StructuredCloneValue} value
445
- * @param {IndexedDBKey} primaryKey
446
- * @returns {void}
447
- */
492
+ * @callback SuccessCallback
493
+ * @param {IndexedDBKey} key
494
+ * @param {StructuredCloneValue} value
495
+ * @param {IndexedDBKey} primaryKey
496
+ * @returns {void}
497
+ */
448
498
 
449
499
  /**
450
500
  * Creates an "onsuccess" callback.
@@ -472,11 +522,11 @@ IDBCursor.prototype.__onsuccess = function (success) {
472
522
 
473
523
  /**
474
524
  * @typedef {{
475
- * matchingKey: string,
476
- * key: string,
477
- * [k: string]: string
478
- * }} RowItemNonNull
479
- */
525
+ * matchingKey: string,
526
+ * key: string,
527
+ * [k: string]: string
528
+ * }} RowItemNonNull
529
+ */
480
530
 
481
531
  /**
482
532
  *
@@ -540,6 +590,7 @@ IDBCursor.prototype.__sourceOrEffectiveObjStoreDeleted = function () {
540
590
  IDBCursor.prototype.__invalidateCache = function () {
541
591
  // @ts-expect-error Why is this not being found?
542
592
  this.__prefetchedData = null;
593
+ this.__multiEntryExhausted = false;
543
594
  };
544
595
 
545
596
  /**
@@ -599,9 +650,8 @@ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState)
599
650
  me.__advanceCount--;
600
651
  me.__key = k;
601
652
  me.__continue(undefined, true);
602
- /** @type {() => void} */ (
603
- executeNextRequest
604
- )(); // We don't call success yet but do need to advance the transaction queue
653
+ // We don't call success yet but do need to advance the transaction queue
654
+ util.runContinuationSafely(/** @type {() => void} */ (executeNextRequest));
605
655
  return;
606
656
  }
607
657
  me.__advanceCount = undefined;
@@ -627,12 +677,12 @@ IDBCursor.prototype.__continueFinish = function (key, primaryKey, advanceState)
627
677
  return;
628
678
  }
629
679
  // @ts-expect-error Todo: Our bug to fix
630
- cursorContinue(tx, args, success, error);
680
+ util.runContinuationSafely(() => cursorContinue(tx, args, success, error));
631
681
  }
632
682
  if (me.__unique && !me.__multiEntryIndex &&
633
683
  encKey === Key.encode(me.key, me.__multiEntryIndex)) {
634
684
  // @ts-expect-error Todo: Our bug to fix
635
- cursorContinue(tx, args, success, error);
685
+ util.runContinuationSafely(() => cursorContinue(tx, args, success, error));
636
686
  return;
637
687
  }
638
688
  checkKey();