From 9c85f6277bf85606eb4fbbca47f1a1b5404d2a2e Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 23 Feb 2021 20:16:10 +0100 Subject: idb: implement missing methods --- packages/idb-bridge/src/MemoryBackend.ts | 39 ++++ packages/idb-bridge/src/backend-interface.ts | 5 + packages/idb-bridge/src/bridge-idb.ts | 299 ++++++++++++++++++++++++++- 3 files changed, 333 insertions(+), 10 deletions(-) (limited to 'packages/idb-bridge') diff --git a/packages/idb-bridge/src/MemoryBackend.ts b/packages/idb-bridge/src/MemoryBackend.ts index 2317fb163..68f60f756 100644 --- a/packages/idb-bridge/src/MemoryBackend.ts +++ b/packages/idb-bridge/src/MemoryBackend.ts @@ -860,6 +860,45 @@ export class MemoryBackend implements Backend { }); } + async clearObjectStore( + btx: DatabaseTransaction, + objectStoreName: string, + ): Promise { + const myConn = this.requireConnectionFromTransaction(btx); + const db = this.databases[myConn.dbName]; + if (!db) { + throw Error("db not found"); + } + if (db.txLevel < TransactionLevel.Write) { + throw Error("only allowed in write transaction"); + } + if ( + db.txRestrictObjectStores && + !db.txRestrictObjectStores.includes(objectStoreName) + ) { + throw Error( + `Not allowed to access store '${objectStoreName}', transaction is over ${JSON.stringify( + db.txRestrictObjectStores, + )}`, + ); + } + + const schema = myConn.modifiedSchema; + const objectStoreMapEntry = myConn.objectStoreMap[objectStoreName]; + + objectStoreMapEntry.store.modifiedData = new BTree([], compareKeys); + + for (const indexName of Object.keys( + schema.objectStores[objectStoreName].indexes, + )) { + const index = myConn.objectStoreMap[objectStoreName].indexMap[indexName]; + if (!index) { + throw Error("index referenced by object store does not exist"); + } + index.modifiedData = new BTree([], compareKeys); + } + } + async deleteRecord( btx: DatabaseTransaction, objectStoreName: string, diff --git a/packages/idb-bridge/src/backend-interface.ts b/packages/idb-bridge/src/backend-interface.ts index 3d2953847..5ca70c8a4 100644 --- a/packages/idb-bridge/src/backend-interface.ts +++ b/packages/idb-bridge/src/backend-interface.ts @@ -216,4 +216,9 @@ export interface Backend { btx: DatabaseTransaction, storeReq: RecordStoreRequest, ): Promise; + + clearObjectStore( + btx: DatabaseTransaction, + objectStoreName: string, + ): Promise } diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts index 744ad1aef..3c168674d 100644 --- a/packages/idb-bridge/src/bridge-idb.ts +++ b/packages/idb-bridge/src/bridge-idb.ts @@ -1234,11 +1234,48 @@ export class BridgeIDBIndex implements IDBIndex { query?: BridgeIDBKeyRange | IDBValidKey, count?: number, ): IDBRequest { - throw Error("not implemented"); + this._confirmIndexExists(); + this._confirmActiveTransaction(); + if (this._deleted) { + throw new InvalidStateError(); + } + + if (!(query instanceof BridgeIDBKeyRange)) { + query = BridgeIDBKeyRange._valueToKeyRange(query); + } + + if (count === undefined) { + count = -1; + } + + const getReq: RecordGetRequest = { + direction: "next", + indexName: this._name, + limit: count, + range: query, + objectStoreName: this._objectStore._name, + resultLevel: ResultLevel.Full, + }; + + const operation = async () => { + const { btx } = this._confirmStartedBackendTransaction(); + const result = await this._backend.getRecords(btx, getReq); + const values = result.values; + if (!values) { + throw Error("invariant violated"); + } + return values.map((x) => structuredRevive(x)); + }; + + return this._objectStore._transaction._execRequestAsync({ + operation, + source: this, + }); } // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-getKey-IDBRequest-any-key public getKey(key: BridgeIDBKeyRange | IDBValidKey) { + this._confirmIndexExists(); this._confirmActiveTransaction(); if (!(key instanceof BridgeIDBKeyRange)) { @@ -1278,11 +1315,45 @@ export class BridgeIDBIndex implements IDBIndex { query?: BridgeIDBKeyRange | IDBValidKey, count?: number, ): IDBRequest { - throw Error("not implemented"); + this._confirmIndexExists(); + this._confirmActiveTransaction(); + + if (!(query instanceof BridgeIDBKeyRange)) { + query = BridgeIDBKeyRange._valueToKeyRange(query); + } + + if (count === undefined) { + count = -1; + } + + const getReq: RecordGetRequest = { + direction: "next", + indexName: this._name, + limit: count, + range: query, + objectStoreName: this._objectStore._name, + resultLevel: ResultLevel.OnlyKeys, + }; + + const operation = async () => { + const { btx } = this._confirmStartedBackendTransaction(); + const result = await this._backend.getRecords(btx, getReq); + const primaryKeys = result.primaryKeys; + if (!primaryKeys) { + throw Error("invariant violated"); + } + return primaryKeys; + }; + + return this._objectStore._transaction._execRequestAsync({ + operation, + source: this, + }); } // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-count-IDBRequest-any-key public count(key: BridgeIDBKeyRange | IDBValidKey | null | undefined) { + this._confirmIndexExists(); this._confirmActiveTransaction(); if (key === null) { @@ -1718,14 +1789,147 @@ export class BridgeIDBObjectStore implements IDBObjectStore { query?: BridgeIDBKeyRange | IDBValidKey, count?: number, ): IDBRequest { - throw Error("not implemented"); + if (BridgeIDBFactory.enableTracing) { + console.log(`getting from object store ${this._name} key ${query}`); + } + + if (arguments.length === 0) { + throw new TypeError(); + } + + if (!this._transaction._active) { + throw new TransactionInactiveError(); + } + + if (this._deleted) { + throw new InvalidStateError( + "tried to call 'delete' on a deleted object store", + ); + } + + if (count === undefined) { + count = -1; + } + + let keyRange: BridgeIDBKeyRange; + + if (query instanceof BridgeIDBKeyRange) { + keyRange = query; + } else { + try { + keyRange = BridgeIDBKeyRange.only(valueToKey(query)); + } catch (e) { + throw new DataError( + `invalid key (type ${typeof query}) for object store '${this._name}'`, + ); + } + } + + const recordRequest: RecordGetRequest = { + objectStoreName: this._name, + indexName: undefined, + lastIndexPosition: undefined, + lastObjectStorePosition: undefined, + direction: "next", + limit: count, + resultLevel: ResultLevel.Full, + range: keyRange, + }; + + const operation = async () => { + if (BridgeIDBFactory.enableTracing) { + console.log("running getAll operation:", recordRequest); + } + const { btx } = this._confirmStartedBackendTransaction(); + const result = await this._backend.getRecords(btx, recordRequest); + + if (BridgeIDBFactory.enableTracing) { + console.log("get operation result count:", result.count); + } + const values = result.values; + if (!values) { + throw Error("invariant violated"); + } + return values.map((x) => structuredRevive(x)); + }; + + return this._transaction._execRequestAsync({ + operation, + source: this, + }); } // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey public getKey( - key?: BridgeIDBKeyRange | IDBValidKey, + query?: BridgeIDBKeyRange | IDBValidKey, ): IDBRequest { - throw Error("not implemented"); + if (arguments.length === 0) { + throw new TypeError(); + } + + if (!this._transaction._active) { + throw new TransactionInactiveError(); + } + + if (this._deleted) { + throw new InvalidStateError( + "tried to call 'delete' on a deleted object store", + ); + } + + let keyRange: BridgeIDBKeyRange; + + if (query instanceof BridgeIDBKeyRange) { + keyRange = query; + } else { + try { + keyRange = BridgeIDBKeyRange.only(valueToKey(query)); + } catch (e) { + throw new DataError( + `invalid key (type ${typeof query}) for object store '${this._name}'`, + ); + } + } + + const recordRequest: RecordGetRequest = { + objectStoreName: this._name, + indexName: undefined, + lastIndexPosition: undefined, + lastObjectStorePosition: undefined, + direction: "next", + limit: 1, + resultLevel: ResultLevel.OnlyKeys, + range: keyRange, + }; + + const operation = async () => { + if (BridgeIDBFactory.enableTracing) { + console.log("running get operation:", recordRequest); + } + const { btx } = this._confirmStartedBackendTransaction(); + const result = await this._backend.getRecords(btx, recordRequest); + + if (BridgeIDBFactory.enableTracing) { + console.log("get operation result count:", result.count); + } + + if (result.count === 0) { + return undefined; + } + const primaryKeys = result.primaryKeys; + if (!primaryKeys) { + throw Error("invariant violated"); + } + if (primaryKeys.length !== 1) { + throw Error("invariant violated"); + } + return structuredRevive(primaryKeys[0]); + }; + + return this._transaction._execRequestAsync({ + operation, + source: this, + }); } // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys @@ -1733,11 +1937,86 @@ export class BridgeIDBObjectStore implements IDBObjectStore { query?: BridgeIDBKeyRange | IDBValidKey, count?: number, ): IDBRequest { - throw Error("not implemented"); + if (arguments.length === 0) { + throw new TypeError(); + } + + if (!this._transaction._active) { + throw new TransactionInactiveError(); + } + + if (this._deleted) { + throw new InvalidStateError( + "tried to call 'delete' on a deleted object store", + ); + } + + if (count === undefined) { + count = -1; + } + + let keyRange: BridgeIDBKeyRange; + + if (query instanceof BridgeIDBKeyRange) { + keyRange = query; + } else { + try { + keyRange = BridgeIDBKeyRange.only(valueToKey(query)); + } catch (e) { + throw new DataError( + `invalid key (type ${typeof query}) for object store '${this._name}'`, + ); + } + } + + const recordRequest: RecordGetRequest = { + objectStoreName: this._name, + indexName: undefined, + lastIndexPosition: undefined, + lastObjectStorePosition: undefined, + direction: "next", + limit: count, + resultLevel: ResultLevel.OnlyKeys, + range: keyRange, + }; + + const operation = async () => { + const { btx } = this._confirmStartedBackendTransaction(); + const result = await this._backend.getRecords(btx, recordRequest); + + const primaryKeys = result.primaryKeys; + if (!primaryKeys) { + throw Error("invariant violated"); + } + return primaryKeys.map((x) => structuredRevive(x)); + }; + + return this._transaction._execRequestAsync({ + operation, + source: this, + }); } - public clear(): IDBRequest { - throw Error("not implemented"); + public clear(): IDBRequest { + if (!this._transaction._active) { + throw new TransactionInactiveError(); + } + + if (this._deleted) { + throw new InvalidStateError( + "tried to call 'delete' on a deleted object store", + ); + } + + const operation = async () => { + const { btx } = this._confirmStartedBackendTransaction(); + await this._backend.clearObjectStore(btx, this._name); + }; + + return this._transaction._execRequestAsync({ + operation, + source: this, + }); } public openCursor( @@ -2228,12 +2507,12 @@ export class BridgeIDBTransaction if (this._db._upgradeTransaction) { for (const os of this._usedObjectStores) { if (os._justCreated) { - os._deleted = true + os._deleted = true; } } for (const ind of this._usedIndexes) { if (ind._justCreated) { - ind._deleted = true + ind._deleted = true; } } } -- cgit v1.2.3