dexie / Dexie.js

A Minimalistic Wrapper for IndexedDB
https://dexie.org
Apache License 2.0
11.69k stars 641 forks source link

(Another) mysterious MissingAPI error #1400

Closed dusty-phillips closed 3 years ago

dusty-phillips commented 3 years ago

Just discovered something is still odd with fake-indexeddb and the latest Dexie release candidate.

It seems to be able to add data to the database and retrieve it just fine, but actually there is an error on put and add calls.

Best described with a repro:

import Dexie from "dexie"; // 3.2.0-rc.2
import indexedDB from "fake-indexeddb"; // 3.1.3
import IDBKeyRange from "fake-indexeddb/lib/FDBKeyRange.js";

const db = new Dexie("MyDatabase", {
  indexedDB: indexedDB,
  IDBKeyRange: IDBKeyRange,
});

db.version(1).stores({
  friends: "name,shoeSize",
});

// If we don't catch the error on put...
db.friends.put({ name: "Nicolas", shoeSize: 8 });
// The data is added and we can get it
db.friends.get("Nicolas").then(console.log); // { name: 'Nicolas', shoeSize: 8}

// But if we add a catch clause to the put...
db.friends.put({ name: "Nicolas", shoeSize: 8 }).catch(console.log);
// We get MissingAPIError

I'm guessing that Dexie is trying to access the global indexedDB at some point, rather than the one passed into the options on the constructor. Here's the trace:

DexieError [MissingAPIError]: IndexedDB API missing. Please visit https://tinyurl.com/y2uuvskb
    at cmp (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4035:15)
    at addRange (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4135:18)
    at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4127:29
    at Array.forEach (<anonymous>)
    at RangeSet.addKeys (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4127:14)
    at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4297:44
    at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:1159:23
    at callListener (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:881:19) {
  _e: Error
      at getErrorWithStack (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:286:12)
      at new DexieError (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:385:19)
      at cmp (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4035:15)
      at addRange (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4135:18)
      at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4127:29
      at Array.forEach (<anonymous>)
      at RangeSet.addKeys (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4127:14)
      at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:4297:44
      at file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:1159:23
      at callListener (file:///Users/dustyphillips/Desktop/Code/dexie-wtf/node_modules/dexie/dist/modern/dexie.mjs:881:19),
  inner: null
}
dfahlander commented 3 years ago

Ok, thanks!

dfahlander commented 3 years ago

This is because RangeSet class depends on indexedDB.cmp(). Could put the RangeSet constructor on the db instance and generate one per db instance (as doing with Transaction, Table, Collection etc). I am though very tempted to do a breaking change here and stop accepting the {indexedDB, IDBKeyRange} as options because static methods like Dexie.delete() would anyway need to be configured by setting the static deps in Dexie.dependencies globally.

import indexedDB from "fake-indexeddb"; // 3.1.3
import IDBKeyRange from "fake-indexeddb/lib/FDBKeyRange.js";

// Configure DOM dependencies on static prop
Dexie.dependencies.indexedDB = indexedDB;
Dexie.dependencies.IDBKeyRange = IDBKeyRange;

// ...is needed before calling static method Dexie.delete():
await Dexie.delete("MyDatabase");

Might need to bump version to 4.0 if so. The thing is - is it worth it having a lot of plumping code just to support per-instance IDBFactory? Maybe it is? Thoughts?

dusty-phillips commented 3 years ago

I can only think of one use case for per-instance configuration: parallel unit tests. But I think that can be approximated by having the one global dependency and using different database names for each dexie instance. I’ll try it and get back to you.

From a marketing/social perspective, having an excuse to launch Dexie 4.0 with dexie-cloud 1.0 might help garner some attention. 😉

dusty-phillips commented 3 years ago

Confirmed: copy-pasting your dependency setup code and appending a Math.random to the name solves the parallel test case.

dfahlander commented 3 years ago

Thanks for your thoughts! I'll keep this issue open for some days before taking a final decision. Anyone feel free to weigh in. To sum up: