summaryrefslogtreecommitdiff
path: root/packages/idb-bridge/src/BridgeIDBIndex.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/idb-bridge/src/BridgeIDBIndex.ts')
-rw-r--r--packages/idb-bridge/src/BridgeIDBIndex.ts316
1 files changed, 316 insertions, 0 deletions
diff --git a/packages/idb-bridge/src/BridgeIDBIndex.ts b/packages/idb-bridge/src/BridgeIDBIndex.ts
new file mode 100644
index 000000000..6001bc051
--- /dev/null
+++ b/packages/idb-bridge/src/BridgeIDBIndex.ts
@@ -0,0 +1,316 @@
+/*
+ Copyright 2017 Jeremy Scheff
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+ */
+
+import BridgeIDBCursor from "./BridgeIDBCursor";
+import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import enforceRange from "./util/enforceRange";
+import {
+ ConstraintError,
+ InvalidStateError,
+ TransactionInactiveError,
+} from "./util/errors";
+import { BridgeIDBCursorDirection, Key, KeyPath } from "./util/types";
+import valueToKey from "./util/valueToKey";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import {
+ Schema,
+ Backend,
+ DatabaseTransaction,
+ RecordGetRequest,
+ ResultLevel,
+} from "./backend-interface";
+
+const confirmActiveTransaction = (
+ index: BridgeIDBIndex,
+): BridgeIDBTransaction => {
+ if (index._deleted || index.objectStore._deleted) {
+ throw new InvalidStateError();
+ }
+
+ if (index.objectStore.transaction._state !== "active") {
+ throw new TransactionInactiveError();
+ }
+
+ return index.objectStore.transaction;
+};
+
+// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
+class BridgeIDBIndex {
+ objectStore: BridgeIDBObjectStore;
+
+ get _schema(): Schema {
+ return this.objectStore.transaction.db._schema;
+ }
+
+ get keyPath(): KeyPath {
+ return this._schema.indexes[this._name].keyPath;
+ }
+
+ get multiEntry(): boolean {
+ return this._schema.indexes[this._name].multiEntry;
+ }
+
+ get unique(): boolean {
+ return this._schema.indexes[this._name].unique;
+ }
+
+ get _backend(): Backend {
+ return this.objectStore._backend;
+ }
+
+ _confirmActiveTransaction(): { btx: DatabaseTransaction } {
+ return this.objectStore._confirmActiveTransaction();
+ }
+
+ private _name: string;
+
+ public _deleted: boolean = false;
+
+ constructor(objectStore: BridgeIDBObjectStore, name: string) {
+ this._name = name;
+ this.objectStore = objectStore;
+ }
+
+ get name() {
+ return this._name;
+ }
+
+ // https://w3c.github.io/IndexedDB/#dom-idbindex-name
+ set name(name: any) {
+ const transaction = this.objectStore.transaction;
+
+ if (!transaction.db._runningVersionchangeTransaction) {
+ throw new InvalidStateError();
+ }
+
+ if (transaction._state !== "active") {
+ throw new TransactionInactiveError();
+ }
+
+ const { btx } = this._confirmActiveTransaction();
+
+ const oldName = this._name;
+ const newName = String(name);
+
+ if (newName === oldName) {
+ return;
+ }
+
+ this._backend.renameIndex(btx, oldName, newName);
+
+ if (this.objectStore.indexNames.indexOf(name) >= 0) {
+ throw new ConstraintError();
+ }
+ }
+
+ // tslint:disable-next-line max-line-length
+ // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-openCursor-IDBRequest-any-range-IDBCursorDirection-direction
+ public openCursor(
+ range?: BridgeIDBKeyRange | Key | null | undefined,
+ direction: BridgeIDBCursorDirection = "next",
+ ) {
+ confirmActiveTransaction(this);
+
+ if (range === null) {
+ range = undefined;
+ }
+ if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+ range = BridgeIDBKeyRange.only(valueToKey(range));
+ }
+
+ const request = new BridgeIDBRequest();
+ request.source = this;
+ request.transaction = this.objectStore.transaction;
+
+ const cursor = new BridgeIDBCursorWithValue(
+ this,
+ this.objectStore.name,
+ this._name,
+ range,
+ direction,
+ request,
+ );
+
+ const operation = async () => {
+ return cursor._iterate();
+ };
+
+ return this.objectStore.transaction._execRequestAsync({
+ operation,
+ request,
+ source: this,
+ });
+ }
+
+ // tslint:disable-next-line max-line-length
+ // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-openKeyCursor-IDBRequest-any-range-IDBCursorDirection-direction
+ public openKeyCursor(
+ range?: BridgeIDBKeyRange | Key | null | undefined,
+ direction: BridgeIDBCursorDirection = "next",
+ ) {
+ confirmActiveTransaction(this);
+
+ if (range === null) {
+ range = undefined;
+ }
+ if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
+ range = BridgeIDBKeyRange.only(valueToKey(range));
+ }
+
+ const request = new BridgeIDBRequest();
+ request.source = this;
+ request.transaction = this.objectStore.transaction;
+
+ const cursor = new BridgeIDBCursor(
+ this,
+ this.objectStore.name,
+ this._name,
+ range,
+ direction,
+ request,
+ true,
+ );
+
+ return this.objectStore.transaction._execRequestAsync({
+ operation: cursor._iterate.bind(cursor),
+ request,
+ source: this,
+ });
+ }
+
+ public get(key: BridgeIDBKeyRange | Key) {
+ confirmActiveTransaction(this);
+
+ if (!(key instanceof BridgeIDBKeyRange)) {
+ key = BridgeIDBKeyRange._valueToKeyRange(key);
+ }
+
+ const getReq: RecordGetRequest = {
+ direction: "next",
+ indexName: this._name,
+ limit: 1,
+ range: key,
+ objectStoreName: this.objectStore._name,
+ resultLevel: ResultLevel.Full,
+ };
+
+ const operation = async () => {
+ const { btx } = this._confirmActiveTransaction();
+ const result = await this._backend.getRecords(btx, getReq);
+ if (result.count == 0) {
+ return undefined;
+ }
+ const values = result.values;
+ if (!values) {
+ throw Error("invariant violated");
+ }
+ return values[0];
+ };
+
+ return this.objectStore.transaction._execRequestAsync({
+ operation,
+ source: this,
+ });
+ }
+
+ // http://w3c.github.io/IndexedDB/#dom-idbindex-getall
+ public getAll(query?: BridgeIDBKeyRange | Key, count?: number) {
+ throw Error("not implemented");
+ }
+
+ // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-getKey-IDBRequest-any-key
+ public getKey(key: BridgeIDBKeyRange | Key) {
+ confirmActiveTransaction(this);
+
+ if (!(key instanceof BridgeIDBKeyRange)) {
+ key = BridgeIDBKeyRange._valueToKeyRange(key);
+ }
+
+ const getReq: RecordGetRequest = {
+ direction: "next",
+ indexName: this._name,
+ limit: 1,
+ range: key,
+ objectStoreName: this.objectStore._name,
+ resultLevel: ResultLevel.OnlyKeys,
+ };
+
+ const operation = async () => {
+ const { btx } = this._confirmActiveTransaction();
+ const result = await this._backend.getRecords(btx, getReq);
+ if (result.count == 0) {
+ return undefined;
+ }
+ const primaryKeys = result.primaryKeys;
+ if (!primaryKeys) {
+ throw Error("invariant violated");
+ }
+ return primaryKeys[0];
+ };
+
+ return this.objectStore.transaction._execRequestAsync({
+ operation,
+ source: this,
+ });
+ }
+
+ // http://w3c.github.io/IndexedDB/#dom-idbindex-getallkeys
+ public getAllKeys(query?: BridgeIDBKeyRange | Key, count?: number) {
+ throw Error("not implemented");
+ }
+
+ // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-count-IDBRequest-any-key
+ public count(key: BridgeIDBKeyRange | Key | null | undefined) {
+ confirmActiveTransaction(this);
+
+ if (key === null) {
+ key = undefined;
+ }
+ if (key !== undefined && !(key instanceof BridgeIDBKeyRange)) {
+ key = BridgeIDBKeyRange.only(valueToKey(key));
+ }
+
+ const getReq: RecordGetRequest = {
+ direction: "next",
+ indexName: this._name,
+ limit: 1,
+ range: key,
+ objectStoreName: this.objectStore._name,
+ resultLevel: ResultLevel.OnlyCount,
+ };
+
+ const operation = async () => {
+ const { btx } = this._confirmActiveTransaction();
+ const result = await this._backend.getRecords(btx, getReq);
+ return result.count;
+ };
+
+ return this.objectStore.transaction._execRequestAsync({
+ operation,
+ source: this,
+ });
+
+ }
+
+ public toString() {
+ return "[object IDBIndex]";
+ }
+}
+
+export default BridgeIDBIndex;