fake-indexeddb
Version:
Fake IndexedDB: a pure JS in-memory implementation of the IndexedDB API
405 lines (390 loc) • 15.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _FDBCursor = _interopRequireDefault(require("./FDBCursor.js"));
var _FDBCursorWithValue = _interopRequireDefault(require("./FDBCursorWithValue.js"));
var _FDBIndex = _interopRequireDefault(require("./FDBIndex.js"));
var _FDBKeyRange = _interopRequireDefault(require("./FDBKeyRange.js"));
var _FDBRequest = _interopRequireDefault(require("./FDBRequest.js"));
var _canInjectKey = _interopRequireDefault(require("./lib/canInjectKey.js"));
var _errors = require("./lib/errors.js");
var _extractKey = _interopRequireDefault(require("./lib/extractKey.js"));
var _FakeDOMStringList = _interopRequireDefault(require("./lib/FakeDOMStringList.js"));
var _Index = _interopRequireDefault(require("./lib/Index.js"));
var _validateKeyPath = _interopRequireDefault(require("./lib/validateKeyPath.js"));
var _valueToKey = _interopRequireDefault(require("./lib/valueToKey.js"));
var _valueToKeyRange = _interopRequireDefault(require("./lib/valueToKeyRange.js"));
var _getKeyPath = require("./lib/getKeyPath.js");
var _extractGetAllOptions = _interopRequireDefault(require("./lib/extractGetAllOptions.js"));
var _enforceRange = _interopRequireDefault(require("./lib/enforceRange.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const confirmActiveTransaction = objectStore => {
if (objectStore._rawObjectStore.deleted) {
throw new _errors.InvalidStateError();
}
if (objectStore.transaction._state !== "active") {
throw new _errors.TransactionInactiveError();
}
};
const buildRecordAddPut = (objectStore, value, key) => {
confirmActiveTransaction(objectStore);
if (objectStore.transaction.mode === "readonly") {
throw new _errors.ReadOnlyError();
}
if (objectStore.keyPath !== null) {
if (key !== undefined) {
throw new _errors.DataError();
}
}
const clone = structuredClone(value);
if (objectStore.keyPath !== null) {
const tempKey = (0, _extractKey.default)(objectStore.keyPath, clone);
if (tempKey.type === "found") {
(0, _valueToKey.default)(tempKey.key);
} else {
if (!objectStore._rawObjectStore.keyGenerator) {
throw new _errors.DataError();
} else if (!(0, _canInjectKey.default)(objectStore.keyPath, clone)) {
throw new _errors.DataError();
}
}
}
if (objectStore.keyPath === null && objectStore._rawObjectStore.keyGenerator === null && key === undefined) {
throw new _errors.DataError();
}
if (key !== undefined) {
key = (0, _valueToKey.default)(key);
}
return {
key,
value: clone
};
};
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
class FDBObjectStore {
_indexesCache = new Map();
constructor(transaction, rawObjectStore) {
this._rawObjectStore = rawObjectStore;
this._name = rawObjectStore.name;
this.keyPath = (0, _getKeyPath.getKeyPath)(rawObjectStore.keyPath);
this.autoIncrement = rawObjectStore.autoIncrement;
this.transaction = transaction;
this.indexNames = new _FakeDOMStringList.default(...Array.from(rawObjectStore.rawIndexes.keys()).sort());
}
get name() {
return this._name;
}
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-name
set name(name) {
const transaction = this.transaction;
if (!transaction.db._runningVersionchangeTransaction) {
throw transaction._state === "active" ? new _errors.InvalidStateError() : new _errors.TransactionInactiveError();
}
confirmActiveTransaction(this);
name = String(name);
if (name === this._name) {
return;
}
if (this._rawObjectStore.rawDatabase.rawObjectStores.has(name)) {
throw new _errors.ConstraintError();
}
const oldName = this._name;
const oldObjectStoreNames = [...transaction.db.objectStoreNames];
this._name = name;
this._rawObjectStore.name = name;
this.transaction._objectStoresCache.delete(oldName);
this.transaction._objectStoresCache.set(name, this);
this._rawObjectStore.rawDatabase.rawObjectStores.delete(oldName);
this._rawObjectStore.rawDatabase.rawObjectStores.set(name, this._rawObjectStore);
transaction.db.objectStoreNames = new _FakeDOMStringList.default(...Array.from(this._rawObjectStore.rawDatabase.rawObjectStores.keys()).filter(objectStoreName => {
const objectStore = this._rawObjectStore.rawDatabase.rawObjectStores.get(objectStoreName);
return objectStore && !objectStore.deleted;
}).sort());
const oldScope = new Set(transaction._scope);
const oldTransactionObjectStoreNames = [...transaction.objectStoreNames];
this.transaction._scope.delete(oldName);
transaction._scope.add(name);
transaction.objectStoreNames = new _FakeDOMStringList.default(...Array.from(transaction._scope).sort());
transaction._rollbackLog.push(() => {
this._name = oldName;
this._rawObjectStore.name = oldName;
this.transaction._objectStoresCache.delete(name);
this.transaction._objectStoresCache.set(oldName, this);
this._rawObjectStore.rawDatabase.rawObjectStores.delete(name);
this._rawObjectStore.rawDatabase.rawObjectStores.set(oldName, this._rawObjectStore);
transaction.db.objectStoreNames = new _FakeDOMStringList.default(...oldObjectStoreNames);
transaction._scope = oldScope;
transaction.objectStoreNames = new _FakeDOMStringList.default(...oldTransactionObjectStoreNames);
});
}
put(value, key) {
if (arguments.length === 0) {
throw new TypeError();
}
const record = buildRecordAddPut(this, value, key);
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.storeRecord.bind(this._rawObjectStore, record, false, this.transaction._rollbackLog),
source: this
});
}
add(value, key) {
if (arguments.length === 0) {
throw new TypeError();
}
const record = buildRecordAddPut(this, value, key);
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.storeRecord.bind(this._rawObjectStore, record, true, this.transaction._rollbackLog),
source: this
});
}
delete(key) {
if (arguments.length === 0) {
throw new TypeError();
}
confirmActiveTransaction(this);
if (this.transaction.mode === "readonly") {
throw new _errors.ReadOnlyError();
}
if (!(key instanceof _FDBKeyRange.default)) {
key = (0, _valueToKey.default)(key);
}
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.deleteRecord.bind(this._rawObjectStore, key, this.transaction._rollbackLog),
source: this
});
}
get(key) {
if (arguments.length === 0) {
throw new TypeError();
}
confirmActiveTransaction(this);
if (!(key instanceof _FDBKeyRange.default)) {
key = (0, _valueToKey.default)(key);
}
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.getValue.bind(this._rawObjectStore, key),
source: this
});
}
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getall
getAll(queryOrOptions, count) {
const options = (0, _extractGetAllOptions.default)(queryOrOptions, count, arguments.length);
confirmActiveTransaction(this);
const range = (0, _valueToKeyRange.default)(options.query);
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.getAllValues.bind(this._rawObjectStore, range, options.count, options.direction),
source: this
});
}
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
getKey(key) {
if (arguments.length === 0) {
throw new TypeError();
}
confirmActiveTransaction(this);
if (!(key instanceof _FDBKeyRange.default)) {
key = (0, _valueToKey.default)(key);
}
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.getKey.bind(this._rawObjectStore, key),
source: this
});
}
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
getAllKeys(queryOrOptions, count) {
const options = (0, _extractGetAllOptions.default)(queryOrOptions, count, arguments.length);
confirmActiveTransaction(this);
const range = (0, _valueToKeyRange.default)(options.query);
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.getAllKeys.bind(this._rawObjectStore, range, options.count, options.direction),
source: this
});
}
// https://www.w3.org/TR/IndexedDB/#dom-idbobjectstore-getallrecords
getAllRecords(options) {
let query;
let count;
let direction;
if (options !== undefined) {
if (options.query !== undefined) {
query = options.query;
}
if (options.count !== undefined) {
count = (0, _enforceRange.default)(options.count, "unsigned long");
}
if (options.direction !== undefined) {
direction = options.direction;
}
}
confirmActiveTransaction(this);
const range = (0, _valueToKeyRange.default)(query);
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.getAllRecords.bind(this._rawObjectStore, range, count, direction),
source: this
});
}
clear() {
confirmActiveTransaction(this);
if (this.transaction.mode === "readonly") {
throw new _errors.ReadOnlyError();
}
return this.transaction._execRequestAsync({
operation: this._rawObjectStore.clear.bind(this._rawObjectStore, this.transaction._rollbackLog),
source: this
});
}
openCursor(range, direction) {
confirmActiveTransaction(this);
if (range === null) {
range = undefined;
}
if (range !== undefined && !(range instanceof _FDBKeyRange.default)) {
range = _FDBKeyRange.default.only((0, _valueToKey.default)(range));
}
const request = new _FDBRequest.default();
request.source = this;
request.transaction = this.transaction;
const cursor = new _FDBCursorWithValue.default(this, range, direction, request);
return this.transaction._execRequestAsync({
operation: cursor._iterate.bind(cursor),
request,
source: this
});
}
openKeyCursor(range, direction) {
confirmActiveTransaction(this);
if (range === null) {
range = undefined;
}
if (range !== undefined && !(range instanceof _FDBKeyRange.default)) {
range = _FDBKeyRange.default.only((0, _valueToKey.default)(range));
}
const request = new _FDBRequest.default();
request.source = this;
request.transaction = this.transaction;
const cursor = new _FDBCursor.default(this, range, direction, request, true);
return this.transaction._execRequestAsync({
operation: cursor._iterate.bind(cursor),
request,
source: this
});
}
// tslint:-next-line max-line-length
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-createIndex-IDBIndex-DOMString-name-DOMString-sequence-DOMString--keyPath-IDBIndexParameters-optionalParameters
createIndex(name, keyPath, optionalParameters = {}) {
if (arguments.length < 2) {
throw new TypeError();
}
const multiEntry = optionalParameters.multiEntry !== undefined ? optionalParameters.multiEntry : false;
const unique = optionalParameters.unique !== undefined ? optionalParameters.unique : false;
if (this.transaction.mode !== "versionchange") {
throw new _errors.InvalidStateError();
}
confirmActiveTransaction(this);
if (this.indexNames.contains(name)) {
throw new _errors.ConstraintError();
}
(0, _validateKeyPath.default)(keyPath);
if (Array.isArray(keyPath) && multiEntry) {
throw new _errors.InvalidAccessError();
}
// The index that is requested to be created can contain constraints on the data allowed in the index's
// referenced object store, such as requiring uniqueness of the values referenced by the index's keyPath. If the
// referenced object store already contains data which violates these constraints, this MUST NOT cause the
// implementation of createIndex to throw an exception or affect what it returns. The implementation MUST still
// create and return an IDBIndex object. Instead the implementation must queue up an operation to abort the
// "versionchange" transaction which was used for the createIndex call.
const indexNames = [...this.indexNames];
this.transaction._rollbackLog.push(() => {
const index2 = this._rawObjectStore.rawIndexes.get(name);
if (index2) {
index2.deleted = true;
}
this.indexNames = new _FakeDOMStringList.default(...indexNames);
this._rawObjectStore.rawIndexes.delete(name);
});
const index = new _Index.default(this._rawObjectStore, name, keyPath, multiEntry, unique);
this.indexNames._push(name);
this.indexNames._sort();
this._rawObjectStore.rawIndexes.set(name, index);
index.initialize(this.transaction); // This is async by design
return new _FDBIndex.default(this, index);
}
// https://w3c.github.io/IndexedDB/#dom-idbobjectstore-index
index(name) {
if (arguments.length === 0) {
throw new TypeError();
}
if (this._rawObjectStore.deleted || this.transaction._state === "finished") {
throw new _errors.InvalidStateError();
}
const index = this._indexesCache.get(name);
if (index !== undefined) {
return index;
}
const rawIndex = this._rawObjectStore.rawIndexes.get(name);
if (!this.indexNames.contains(name) || rawIndex === undefined) {
throw new _errors.NotFoundError();
}
const index2 = new _FDBIndex.default(this, rawIndex);
this._indexesCache.set(name, index2);
return index2;
}
deleteIndex(name) {
if (arguments.length === 0) {
throw new TypeError();
}
if (this.transaction.mode !== "versionchange") {
throw new _errors.InvalidStateError();
}
confirmActiveTransaction(this);
const rawIndex = this._rawObjectStore.rawIndexes.get(name);
if (rawIndex === undefined) {
throw new _errors.NotFoundError();
}
this.transaction._rollbackLog.push(() => {
rawIndex.deleted = false;
this._rawObjectStore.rawIndexes.set(name, rawIndex);
this.indexNames._push(name);
this.indexNames._sort();
});
this.indexNames = new _FakeDOMStringList.default(...Array.from(this.indexNames).filter(indexName => {
return indexName !== name;
}));
rawIndex.deleted = true; // Not sure if this is supposed to happen synchronously
this.transaction._execRequestAsync({
operation: () => {
const rawIndex2 = this._rawObjectStore.rawIndexes.get(name);
// Hack in case another index is given this name before this async request is processed. It'd be better
// to have a real unique ID for each index.
if (rawIndex === rawIndex2) {
this._rawObjectStore.rawIndexes.delete(name);
}
},
source: this
});
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-count-IDBRequest-any-key
count(key) {
confirmActiveTransaction(this);
if (key === null) {
key = undefined;
}
if (key !== undefined && !(key instanceof _FDBKeyRange.default)) {
key = _FDBKeyRange.default.only((0, _valueToKey.default)(key));
}
return this.transaction._execRequestAsync({
operation: () => {
return this._rawObjectStore.count(key);
},
source: this
});
}
get [Symbol.toStringTag]() {
return "IDBObjectStore";
}
}
var _default = exports.default = FDBObjectStore;
module.exports = exports.default;