fake-indexeddb
Version:
Fake IndexedDB: a pure JS in-memory implementation of the IndexedDB API
154 lines (148 loc) • 6.41 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _FDBTransaction = _interopRequireDefault(require("./FDBTransaction.js"));
var _errors = require("./lib/errors.js");
var _FakeDOMStringList = _interopRequireDefault(require("./lib/FakeDOMStringList.js"));
var _FakeEventTarget = _interopRequireDefault(require("./lib/FakeEventTarget.js"));
var _ObjectStore = _interopRequireDefault(require("./lib/ObjectStore.js"));
var _validateKeyPath = _interopRequireDefault(require("./lib/validateKeyPath.js"));
var _closeConnection = _interopRequireDefault(require("./lib/closeConnection.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const confirmActiveVersionchangeTransaction = database => {
if (!database._runningVersionchangeTransaction) {
throw new _errors.InvalidStateError();
}
// Find the latest versionchange transaction
const transactions = database._rawDatabase.transactions.filter(tx => {
return tx.mode === "versionchange";
});
const transaction = transactions[transactions.length - 1];
if (!transaction || transaction._state === "finished") {
throw new _errors.InvalidStateError();
}
if (transaction._state !== "active") {
throw new _errors.TransactionInactiveError();
}
return transaction;
};
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
class FDBDatabase extends _FakeEventTarget.default {
_closePending = false;
_closed = false;
_runningVersionchangeTransaction = false;
constructor(rawDatabase) {
super();
this._rawDatabase = rawDatabase;
this._rawDatabase.connections.push(this);
this.name = rawDatabase.name;
this.version = rawDatabase.version;
this.objectStoreNames = new _FakeDOMStringList.default(...Array.from(rawDatabase.rawObjectStores.keys()).sort());
}
// http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
createObjectStore(name, options = {}) {
if (name === undefined) {
throw new TypeError();
}
const transaction = confirmActiveVersionchangeTransaction(this);
const keyPath = options !== null && options.keyPath !== undefined ? options.keyPath : null;
const autoIncrement = options !== null && options.autoIncrement !== undefined ? options.autoIncrement : false;
if (keyPath !== null) {
(0, _validateKeyPath.default)(keyPath);
}
if (this._rawDatabase.rawObjectStores.has(name)) {
throw new _errors.ConstraintError();
}
if (autoIncrement && (keyPath === "" || Array.isArray(keyPath))) {
throw new _errors.InvalidAccessError();
}
const objectStoreNames = [...this.objectStoreNames];
transaction._rollbackLog.push(() => {
const objectStore = this._rawDatabase.rawObjectStores.get(name);
if (objectStore) {
objectStore.deleted = true;
}
this.objectStoreNames = new _FakeDOMStringList.default(...objectStoreNames);
transaction._scope.delete(name);
this._rawDatabase.rawObjectStores.delete(name);
});
const rawObjectStore = new _ObjectStore.default(this._rawDatabase, name, keyPath, autoIncrement);
this.objectStoreNames._push(name);
this.objectStoreNames._sort();
transaction._scope.add(name);
this._rawDatabase.rawObjectStores.set(name, rawObjectStore);
transaction.objectStoreNames = new _FakeDOMStringList.default(...this.objectStoreNames);
return transaction.objectStore(name);
}
deleteObjectStore(name) {
if (name === undefined) {
throw new TypeError();
}
const transaction = confirmActiveVersionchangeTransaction(this);
const store = this._rawDatabase.rawObjectStores.get(name);
if (store === undefined) {
throw new _errors.NotFoundError();
}
this.objectStoreNames = new _FakeDOMStringList.default(...Array.from(this.objectStoreNames).filter(objectStoreName => {
return objectStoreName !== name;
}));
transaction.objectStoreNames = new _FakeDOMStringList.default(...this.objectStoreNames);
transaction._rollbackLog.push(() => {
store.deleted = false;
this._rawDatabase.rawObjectStores.set(name, store);
this.objectStoreNames._push(name);
this.objectStoreNames._sort();
});
store.deleted = true;
this._rawDatabase.rawObjectStores.delete(name);
transaction._objectStoresCache.delete(name);
}
transaction(storeNames, mode, options) {
mode = mode !== undefined ? mode : "readonly";
if (mode !== "readonly" && mode !== "readwrite" && mode !== "versionchange") {
throw new TypeError("Invalid mode: " + mode);
}
const hasActiveVersionchange = this._rawDatabase.transactions.some(transaction => {
return transaction._state === "active" && transaction.mode === "versionchange" && transaction.db === this;
});
if (hasActiveVersionchange) {
throw new _errors.InvalidStateError();
}
if (this._closePending) {
throw new _errors.InvalidStateError();
}
if (!Array.isArray(storeNames)) {
storeNames = [storeNames];
}
if (storeNames.length === 0 && mode !== "versionchange") {
throw new _errors.InvalidAccessError();
}
for (const storeName of storeNames) {
if (!this.objectStoreNames.contains(storeName)) {
throw new _errors.NotFoundError("No objectStore named " + storeName + " in this database");
}
}
// the actual algo is more complex but this passes the IDB tests: https://webidl.spec.whatwg.org/#es-dictionary
const durability = options?.durability ?? "default";
// invalid enums throw a TypeError: https://webidl.spec.whatwg.org/#es-enumeration
if (durability !== "default" && durability !== "strict" && durability !== "relaxed") {
throw new TypeError(
// based on Firefox's error message
`'${durability}' (value of 'durability' member of IDBTransactionOptions) ` + `is not a valid value for enumeration IDBTransactionDurability`);
}
const tx = new _FDBTransaction.default(storeNames, mode, durability, this);
this._rawDatabase.transactions.push(tx);
this._rawDatabase.processTransactions(); // See if can start right away (async)
return tx;
}
close() {
(0, _closeConnection.default)(this);
}
get [Symbol.toStringTag]() {
return "IDBDatabase";
}
}
var _default = exports.default = FDBDatabase;
module.exports = exports.default;