idb-bridge: remove cyclic dependencies, rip out api extractor
This commit is contained in:
parent
883637d3f2
commit
4452984a24
@ -1,131 +0,0 @@
|
||||
/**
|
||||
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
|
||||
*/
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
|
||||
/**
|
||||
* (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
|
||||
* analyzes the symbols exported by this module.
|
||||
*
|
||||
* The file extension must be ".d.ts" and not ".ts".
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
*/
|
||||
"mainEntryPointFilePath": "<projectFolder>/lib/index.d.ts",
|
||||
|
||||
/**
|
||||
* A list of NPM package names whose exports should be treated as part of this package.
|
||||
*
|
||||
* For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
|
||||
* and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part
|
||||
* of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
|
||||
* imports library2. To avoid this, we can specify:
|
||||
*
|
||||
* "bundledPackages": [ "library2" ],
|
||||
*
|
||||
* This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
|
||||
* local files for library1.
|
||||
*/
|
||||
"bundledPackages": [],
|
||||
|
||||
/**
|
||||
* Configures how the API report file (*.api.md) will be generated.
|
||||
*/
|
||||
"apiReport": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate an API report.
|
||||
*/
|
||||
"enabled": false
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the doc model file (*.api.json) will be generated.
|
||||
*/
|
||||
"docModel": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate a doc model file.
|
||||
*/
|
||||
"enabled": false
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the .d.ts rollup file will be generated.
|
||||
*/
|
||||
"dtsRollup": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate the .d.ts rollup file.
|
||||
*/
|
||||
"enabled": true
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the tsdoc-metadata.json file will be generated.
|
||||
*/
|
||||
"tsdocMetadata": {
|
||||
/**
|
||||
* Whether to generate the tsdoc-metadata.json file.
|
||||
*
|
||||
* DEFAULT VALUE: true
|
||||
*/
|
||||
"enabled": false
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies what type of newlines API Extractor should use when writing output files. By default, the output files
|
||||
* will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead.
|
||||
* To use the OS's default newline kind, specify "os".
|
||||
*
|
||||
* DEFAULT VALUE: "crlf"
|
||||
*/
|
||||
"newlineKind": "lf",
|
||||
|
||||
/**
|
||||
* Configures how API Extractor reports error and warning messages produced during analysis.
|
||||
*
|
||||
* There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages.
|
||||
*/
|
||||
"messages": {
|
||||
/**
|
||||
* Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
|
||||
* the input .d.ts files.
|
||||
*
|
||||
* TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551"
|
||||
*
|
||||
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
|
||||
*/
|
||||
"compilerMessageReporting": {
|
||||
/**
|
||||
* Configures the default routing for messages that don't match an explicit rule in this table.
|
||||
*/
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures handling of messages reported by API Extractor during its analysis.
|
||||
*
|
||||
* API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag"
|
||||
*
|
||||
* DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
|
||||
*/
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures handling of messages reported by the TSDoc parser when analyzing code comments.
|
||||
*/
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,19 +4,19 @@
|
||||
"description": "IndexedDB implementation that uses SQLite3 as storage",
|
||||
"main": "./dist/idb-bridge.js",
|
||||
"module": "./lib/index.js",
|
||||
"types": "./dist/idb-bridge.d.ts",
|
||||
"types": "./lib/index.d.ts",
|
||||
"author": "Florian Dold",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"test": "tsc && ava",
|
||||
"prepare": "tsc && rollup -c && api-extractor run",
|
||||
"compile": "tsc && rollup -c && api-extractor run",
|
||||
"prepare": "tsc && rollup -c",
|
||||
"compile": "tsc && rollup -c",
|
||||
"clean": "rimraf dist lib tsconfig.tsbuildinfo",
|
||||
"pretty": "prettier --write src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "^7.13.0",
|
||||
"@types/node": "^14.14.22",
|
||||
"ava": "^3.15.0",
|
||||
"esm": "^3.2.25",
|
||||
"prettier": "^2.2.1",
|
||||
@ -25,7 +25,6 @@
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^14.14.22",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"ava": {
|
||||
@ -33,4 +32,4 @@
|
||||
"esm"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,364 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2017 Jeremy Scheff
|
||||
Copyright 2019 Florian Dold
|
||||
|
||||
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 { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
import { BridgeIDBObjectStore } from "./BridgeIDBObjectStore";
|
||||
import { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import compareKeys from "./util/cmp";
|
||||
import {
|
||||
DataError,
|
||||
InvalidStateError,
|
||||
ReadOnlyError,
|
||||
TransactionInactiveError,
|
||||
} from "./util/errors";
|
||||
import structuredClone from "./util/structuredClone";
|
||||
import {
|
||||
CursorRange,
|
||||
CursorSource,
|
||||
Key,
|
||||
Value,
|
||||
BridgeIDBCursorDirection,
|
||||
} from "./util/types";
|
||||
import valueToKey from "./util/valueToKey";
|
||||
import {
|
||||
RecordGetRequest,
|
||||
ResultLevel,
|
||||
Backend,
|
||||
RecordStoreRequest,
|
||||
StoreLevel,
|
||||
} from "./backend-interface";
|
||||
import { BridgeIDBFactory } from "./BridgeIDBFactory";
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#cursor
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class BridgeIDBCursor {
|
||||
_request: BridgeIDBRequest | undefined;
|
||||
|
||||
private _gotValue: boolean = false;
|
||||
private _range: CursorRange;
|
||||
private _indexPosition = undefined; // Key of previously returned record
|
||||
private _objectStorePosition = undefined;
|
||||
private _keyOnly: boolean;
|
||||
|
||||
private _source: CursorSource;
|
||||
private _direction: BridgeIDBCursorDirection;
|
||||
private _key = undefined;
|
||||
private _primaryKey: Key | undefined = undefined;
|
||||
private _indexName: string | undefined;
|
||||
private _objectStoreName: string;
|
||||
|
||||
protected _value: Value = undefined;
|
||||
|
||||
constructor(
|
||||
source: CursorSource,
|
||||
objectStoreName: string,
|
||||
indexName: string | undefined,
|
||||
range: CursorRange,
|
||||
direction: BridgeIDBCursorDirection,
|
||||
request: BridgeIDBRequest,
|
||||
keyOnly: boolean,
|
||||
) {
|
||||
this._indexName = indexName;
|
||||
this._objectStoreName = objectStoreName;
|
||||
this._range = range;
|
||||
this._source = source;
|
||||
this._direction = direction;
|
||||
this._request = request;
|
||||
this._keyOnly = keyOnly;
|
||||
}
|
||||
|
||||
get _effectiveObjectStore(): BridgeIDBObjectStore {
|
||||
if (this.source instanceof BridgeIDBObjectStore) {
|
||||
return this.source;
|
||||
}
|
||||
return this.source.objectStore;
|
||||
}
|
||||
|
||||
get _backend(): Backend {
|
||||
return this._source._backend;
|
||||
}
|
||||
|
||||
// Read only properties
|
||||
get source() {
|
||||
return this._source;
|
||||
}
|
||||
set source(val) {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
get direction() {
|
||||
return this._direction;
|
||||
}
|
||||
set direction(val) {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
get key() {
|
||||
return this._key;
|
||||
}
|
||||
set key(val) {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
get primaryKey() {
|
||||
return this._primaryKey;
|
||||
}
|
||||
|
||||
set primaryKey(val) {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
protected get _isValueCursor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://w3c.github.io/IndexedDB/#iterate-a-cursor
|
||||
*/
|
||||
async _iterate(key?: Key, primaryKey?: Key): Promise<any> {
|
||||
BridgeIDBFactory.enableTracing &&
|
||||
console.log(
|
||||
`iterating cursor os=${this._objectStoreName},idx=${this._indexName}`,
|
||||
);
|
||||
BridgeIDBFactory.enableTracing &&
|
||||
console.log("cursor type ", this.toString());
|
||||
const recordGetRequest: RecordGetRequest = {
|
||||
direction: this.direction,
|
||||
indexName: this._indexName,
|
||||
lastIndexPosition: this._indexPosition,
|
||||
lastObjectStorePosition: this._objectStorePosition,
|
||||
limit: 1,
|
||||
range: this._range,
|
||||
objectStoreName: this._objectStoreName,
|
||||
advanceIndexKey: key,
|
||||
advancePrimaryKey: primaryKey,
|
||||
resultLevel: this._keyOnly ? ResultLevel.OnlyKeys : ResultLevel.Full,
|
||||
};
|
||||
|
||||
const { btx } = this.source._confirmActiveTransaction();
|
||||
|
||||
let response = await this._backend.getRecords(btx, recordGetRequest);
|
||||
|
||||
if (response.count === 0) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("cursor is returning empty result");
|
||||
}
|
||||
this._gotValue = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.count !== 1) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("request is:", JSON.stringify(recordGetRequest));
|
||||
console.log("get response is:", JSON.stringify(response));
|
||||
}
|
||||
|
||||
if (this._indexName !== undefined) {
|
||||
this._key = response.indexKeys![0];
|
||||
} else {
|
||||
this._key = response.primaryKeys![0];
|
||||
}
|
||||
|
||||
this._primaryKey = response.primaryKeys![0];
|
||||
|
||||
if (!this._keyOnly) {
|
||||
this._value = response.values![0];
|
||||
}
|
||||
|
||||
this._gotValue = true;
|
||||
this._objectStorePosition = structuredClone(response.primaryKeys![0]);
|
||||
if (response.indexKeys !== undefined && response.indexKeys.length > 0) {
|
||||
this._indexPosition = structuredClone(response.indexKeys[0]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-update-IDBRequest-any-value
|
||||
public update(value: Value) {
|
||||
if (value === undefined) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
if (transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
|
||||
if (this._effectiveObjectStore._deleted) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (
|
||||
!(this.source instanceof BridgeIDBObjectStore) &&
|
||||
this.source._deleted
|
||||
) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!this._gotValue || !this._isValueCursor) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const storeReq: RecordStoreRequest = {
|
||||
key: this._primaryKey,
|
||||
value: value,
|
||||
objectStoreName: this._objectStoreName,
|
||||
storeLevel: StoreLevel.UpdateExisting,
|
||||
};
|
||||
|
||||
const operation = async () => {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("updating at cursor");
|
||||
}
|
||||
const { btx } = this.source._confirmActiveTransaction();
|
||||
await this._backend.storeRecord(btx, storeReq);
|
||||
};
|
||||
return transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-advance-void-unsigned-long-count
|
||||
*/
|
||||
public advance(count: number) {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-continue-void-any-key
|
||||
*/
|
||||
public continue(key?: Key) {
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
if (this._effectiveObjectStore._deleted) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
if (
|
||||
!(this.source instanceof BridgeIDBObjectStore) &&
|
||||
this.source._deleted
|
||||
) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!this._gotValue) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (key !== undefined) {
|
||||
key = valueToKey(key);
|
||||
let lastKey =
|
||||
this._indexName === undefined
|
||||
? this._objectStorePosition
|
||||
: this._indexPosition;
|
||||
|
||||
const cmpResult = compareKeys(key, lastKey);
|
||||
|
||||
if (
|
||||
(cmpResult <= 0 &&
|
||||
(this.direction === "next" || this.direction === "nextunique")) ||
|
||||
(cmpResult >= 0 &&
|
||||
(this.direction === "prev" || this.direction === "prevunique"))
|
||||
) {
|
||||
throw new DataError();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._request) {
|
||||
this._request.readyState = "pending";
|
||||
}
|
||||
|
||||
const operation = async () => {
|
||||
return this._iterate(key);
|
||||
};
|
||||
|
||||
transaction._execRequestAsync({
|
||||
operation,
|
||||
request: this._request,
|
||||
source: this.source,
|
||||
});
|
||||
|
||||
this._gotValue = false;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#dom-idbcursor-continueprimarykey
|
||||
public continuePrimaryKey(key: Key, primaryKey: Key) {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
public delete() {
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
if (transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
|
||||
if (this._effectiveObjectStore._deleted) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
if (
|
||||
!(this.source instanceof BridgeIDBObjectStore) &&
|
||||
this.source._deleted
|
||||
) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!this._gotValue || !this._isValueCursor) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const operation = async () => {
|
||||
const { btx } = this.source._confirmActiveTransaction();
|
||||
this._backend.deleteRecord(
|
||||
btx,
|
||||
this._objectStoreName,
|
||||
BridgeIDBKeyRange._valueToKeyRange(this._primaryKey),
|
||||
);
|
||||
};
|
||||
|
||||
return transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBCursor]";
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
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 {
|
||||
CursorRange,
|
||||
CursorSource,
|
||||
BridgeIDBCursorDirection,
|
||||
Value,
|
||||
} from "./util/types";
|
||||
|
||||
export class BridgeIDBCursorWithValue extends BridgeIDBCursor {
|
||||
get value(): Value {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
protected get _isValueCursor(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
constructor(
|
||||
source: CursorSource,
|
||||
objectStoreName: string,
|
||||
indexName: string | undefined,
|
||||
range: CursorRange,
|
||||
direction: BridgeIDBCursorDirection,
|
||||
request?: any,
|
||||
) {
|
||||
super(source, objectStoreName, indexName, range, direction, request, false);
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBCursorWithValue]";
|
||||
}
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
/*
|
||||
* 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 { BridgeIDBTransaction } from "./BridgeIDBTransaction";
|
||||
import {
|
||||
ConstraintError,
|
||||
InvalidAccessError,
|
||||
InvalidStateError,
|
||||
NotFoundError,
|
||||
TransactionInactiveError,
|
||||
} from "./util/errors";
|
||||
import fakeDOMStringList from "./util/fakeDOMStringList";
|
||||
import FakeEventTarget from "./util/FakeEventTarget";
|
||||
import { FakeDOMStringList, KeyPath, TransactionMode } from "./util/types";
|
||||
import validateKeyPath from "./util/validateKeyPath";
|
||||
import queueTask from "./util/queueTask";
|
||||
import {
|
||||
Backend,
|
||||
DatabaseConnection,
|
||||
Schema,
|
||||
DatabaseTransaction,
|
||||
} from "./backend-interface";
|
||||
import { BridgeIDBObjectStore } from "./BridgeIDBObjectStore";
|
||||
|
||||
/**
|
||||
* Ensure that an active version change transaction is currently running.
|
||||
*/
|
||||
const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
|
||||
if (!database._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
// Find the latest versionchange transaction
|
||||
const transactions = database._transactions.filter(
|
||||
(tx: BridgeIDBTransaction) => {
|
||||
return tx.mode === "versionchange";
|
||||
},
|
||||
);
|
||||
const transaction = transactions[transactions.length - 1];
|
||||
|
||||
if (!transaction || transaction._state === "finished") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
return transaction;
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
|
||||
/** @public */
|
||||
export class BridgeIDBDatabase extends FakeEventTarget {
|
||||
_closePending = false;
|
||||
_closed = false;
|
||||
_runningVersionchangeTransaction = false;
|
||||
_transactions: Array<BridgeIDBTransaction> = [];
|
||||
|
||||
_backendConnection: DatabaseConnection;
|
||||
_backend: Backend;
|
||||
|
||||
_schema: Schema;
|
||||
|
||||
get name(): string {
|
||||
return this._schema.databaseName;
|
||||
}
|
||||
|
||||
get version(): number {
|
||||
return this._schema.databaseVersion;
|
||||
}
|
||||
|
||||
get objectStoreNames(): FakeDOMStringList {
|
||||
return fakeDOMStringList(Object.keys(this._schema.objectStores)).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-closing-steps
|
||||
*/
|
||||
_closeConnection() {
|
||||
this._closePending = true;
|
||||
|
||||
const transactionsComplete = this._transactions.every(
|
||||
(transaction: BridgeIDBTransaction) => {
|
||||
return transaction._state === "finished";
|
||||
},
|
||||
);
|
||||
|
||||
if (transactionsComplete) {
|
||||
this._closed = true;
|
||||
this._backend.close(this._backendConnection);
|
||||
} else {
|
||||
queueTask(() => {
|
||||
this._closeConnection();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
constructor(backend: Backend, backendConnection: DatabaseConnection) {
|
||||
super();
|
||||
|
||||
this._schema = backend.getSchema(backendConnection);
|
||||
|
||||
this._backend = backend;
|
||||
this._backendConnection = backendConnection;
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
|
||||
public createObjectStore(
|
||||
name: string,
|
||||
options: { autoIncrement?: boolean; keyPath?: KeyPath } | null = {},
|
||||
): BridgeIDBObjectStore {
|
||||
if (name === undefined) {
|
||||
throw new TypeError();
|
||||
}
|
||||
const transaction = confirmActiveVersionchangeTransaction(this);
|
||||
const backendTx = transaction._backendTransaction;
|
||||
if (!backendTx) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
|
||||
const keyPath =
|
||||
options !== null && options.keyPath !== undefined
|
||||
? options.keyPath
|
||||
: null;
|
||||
const autoIncrement =
|
||||
options !== null && options.autoIncrement !== undefined
|
||||
? options.autoIncrement
|
||||
: false;
|
||||
|
||||
if (keyPath !== null) {
|
||||
validateKeyPath(keyPath);
|
||||
}
|
||||
|
||||
if (Object.keys(this._schema.objectStores).includes(name)) {
|
||||
throw new ConstraintError();
|
||||
}
|
||||
|
||||
if (autoIncrement && (keyPath === "" || Array.isArray(keyPath))) {
|
||||
throw new InvalidAccessError();
|
||||
}
|
||||
|
||||
transaction._backend.createObjectStore(
|
||||
backendTx,
|
||||
name,
|
||||
keyPath,
|
||||
autoIncrement,
|
||||
);
|
||||
|
||||
this._schema = this._backend.getSchema(this._backendConnection);
|
||||
|
||||
return transaction.objectStore(name);
|
||||
}
|
||||
|
||||
public deleteObjectStore(name: string): void {
|
||||
if (name === undefined) {
|
||||
throw new TypeError();
|
||||
}
|
||||
const transaction = confirmActiveVersionchangeTransaction(this);
|
||||
transaction._objectStoresCache.delete(name);
|
||||
}
|
||||
|
||||
public _internalTransaction(
|
||||
storeNames: string | string[],
|
||||
mode?: TransactionMode,
|
||||
backendTransaction?: DatabaseTransaction,
|
||||
): BridgeIDBTransaction {
|
||||
mode = mode !== undefined ? mode : "readonly";
|
||||
if (
|
||||
mode !== "readonly" &&
|
||||
mode !== "readwrite" &&
|
||||
mode !== "versionchange"
|
||||
) {
|
||||
throw new TypeError("Invalid mode: " + mode);
|
||||
}
|
||||
|
||||
const hasActiveVersionchange = this._transactions.some(
|
||||
(transaction: BridgeIDBTransaction) => {
|
||||
return (
|
||||
transaction._state === "active" &&
|
||||
transaction.mode === "versionchange" &&
|
||||
transaction.db === this
|
||||
);
|
||||
},
|
||||
);
|
||||
if (hasActiveVersionchange) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (this._closePending) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!Array.isArray(storeNames)) {
|
||||
storeNames = [storeNames];
|
||||
}
|
||||
if (storeNames.length === 0 && mode !== "versionchange") {
|
||||
throw new InvalidAccessError();
|
||||
}
|
||||
for (const storeName of storeNames) {
|
||||
if (this.objectStoreNames.indexOf(storeName) < 0) {
|
||||
throw new NotFoundError(
|
||||
"No objectStore named " + storeName + " in this database",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const tx = new BridgeIDBTransaction(
|
||||
storeNames,
|
||||
mode,
|
||||
this,
|
||||
backendTransaction,
|
||||
);
|
||||
this._transactions.push(tx);
|
||||
queueTask(() => tx._start());
|
||||
return tx;
|
||||
}
|
||||
|
||||
public transaction(
|
||||
storeNames: string | string[],
|
||||
mode?: TransactionMode,
|
||||
): BridgeIDBTransaction {
|
||||
if (mode === "versionchange") {
|
||||
throw new TypeError("Invalid mode: " + mode);
|
||||
}
|
||||
return this._internalTransaction(storeNames, mode);
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._closeConnection();
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBDatabase]";
|
||||
}
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Jeremy Scheff
|
||||
* Copyright 2019 Florian Dold
|
||||
*
|
||||
* 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 { BridgeIDBDatabase } from "./BridgeIDBDatabase";
|
||||
import { BridgeIDBOpenDBRequest } from "./BridgeIDBOpenDBRequest";
|
||||
import { BridgeIDBVersionChangeEvent } from "./BridgeIDBVersionChangeEvent";
|
||||
import compareKeys from "./util/cmp";
|
||||
import enforceRange from "./util/enforceRange";
|
||||
import { AbortError, VersionError } from "./util/errors";
|
||||
import FakeEvent from "./util/FakeEvent";
|
||||
import { Backend, DatabaseConnection } from "./backend-interface";
|
||||
import queueTask from "./util/queueTask";
|
||||
|
||||
/** @public */
|
||||
export type DatabaseList = Array<{ name: string; version: number }>;
|
||||
|
||||
/** @public */
|
||||
export class BridgeIDBFactory {
|
||||
public cmp = compareKeys;
|
||||
private backend: Backend;
|
||||
private connections: BridgeIDBDatabase[] = [];
|
||||
static enableTracing: boolean = false;
|
||||
|
||||
public constructor(backend: Backend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-deleteDatabase-IDBOpenDBRequest-DOMString-name
|
||||
public deleteDatabase(name: string): BridgeIDBOpenDBRequest {
|
||||
const request = new BridgeIDBOpenDBRequest();
|
||||
request.source = null;
|
||||
|
||||
queueTask(async () => {
|
||||
const databases = await this.backend.getDatabases();
|
||||
const dbInfo = databases.find((x) => x.name == name);
|
||||
if (!dbInfo) {
|
||||
// Database already doesn't exist, success!
|
||||
const event = new BridgeIDBVersionChangeEvent("success", {
|
||||
newVersion: null,
|
||||
oldVersion: 0,
|
||||
});
|
||||
request.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
const oldVersion = dbInfo.version;
|
||||
|
||||
try {
|
||||
const dbconn = await this.backend.connectDatabase(name);
|
||||
const backendTransaction = await this.backend.enterVersionChange(
|
||||
dbconn,
|
||||
0,
|
||||
);
|
||||
await this.backend.deleteDatabase(backendTransaction, name);
|
||||
await this.backend.commit(backendTransaction);
|
||||
await this.backend.close(dbconn);
|
||||
|
||||
request.result = undefined;
|
||||
request.readyState = "done";
|
||||
|
||||
const event2 = new BridgeIDBVersionChangeEvent("success", {
|
||||
newVersion: null,
|
||||
oldVersion,
|
||||
});
|
||||
request.dispatchEvent(event2);
|
||||
} catch (err) {
|
||||
request.error = new Error();
|
||||
request.error.name = err.name;
|
||||
request.readyState = "done";
|
||||
|
||||
const event = new FakeEvent("error", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.eventPath = [];
|
||||
request.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line max-line-length
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-open-IDBOpenDBRequest-DOMString-name-unsigned-long-long-version
|
||||
public open(name: string, version?: number) {
|
||||
if (arguments.length > 1 && version !== undefined) {
|
||||
// Based on spec, not sure why "MAX_SAFE_INTEGER" instead of "unsigned long long", but it's needed to pass
|
||||
// tests
|
||||
version = enforceRange(version, "MAX_SAFE_INTEGER");
|
||||
}
|
||||
if (version === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
const request = new BridgeIDBOpenDBRequest();
|
||||
|
||||
queueTask(async () => {
|
||||
let dbconn: DatabaseConnection;
|
||||
try {
|
||||
dbconn = await this.backend.connectDatabase(name);
|
||||
} catch (err) {
|
||||
request._finishWithError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const schema = this.backend.getSchema(dbconn);
|
||||
const existingVersion = schema.databaseVersion;
|
||||
|
||||
if (version === undefined) {
|
||||
version = existingVersion !== 0 ? existingVersion : 1;
|
||||
}
|
||||
|
||||
const requestedVersion = version;
|
||||
|
||||
BridgeIDBFactory.enableTracing &&
|
||||
console.log(
|
||||
`TRACE: existing version ${existingVersion}, requested version ${requestedVersion}`,
|
||||
);
|
||||
|
||||
if (existingVersion > requestedVersion) {
|
||||
request._finishWithError(new VersionError());
|
||||
return;
|
||||
}
|
||||
|
||||
const db = new BridgeIDBDatabase(this.backend, dbconn);
|
||||
|
||||
if (existingVersion == requestedVersion) {
|
||||
request.result = db;
|
||||
request.readyState = "done";
|
||||
|
||||
const event2 = new FakeEvent("success", {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
});
|
||||
event2.eventPath = [request];
|
||||
request.dispatchEvent(event2);
|
||||
}
|
||||
|
||||
if (existingVersion < requestedVersion) {
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
|
||||
|
||||
for (const otherConn of this.connections) {
|
||||
const event = new BridgeIDBVersionChangeEvent("versionchange", {
|
||||
newVersion: version,
|
||||
oldVersion: existingVersion,
|
||||
});
|
||||
otherConn.dispatchEvent(event);
|
||||
}
|
||||
|
||||
if (this._anyOpen()) {
|
||||
const event = new BridgeIDBVersionChangeEvent("blocked", {
|
||||
newVersion: version,
|
||||
oldVersion: existingVersion,
|
||||
});
|
||||
request.dispatchEvent(event);
|
||||
}
|
||||
|
||||
const backendTransaction = await this.backend.enterVersionChange(
|
||||
dbconn,
|
||||
requestedVersion,
|
||||
);
|
||||
db._runningVersionchangeTransaction = true;
|
||||
|
||||
const transaction = db._internalTransaction(
|
||||
[],
|
||||
"versionchange",
|
||||
backendTransaction,
|
||||
);
|
||||
const event = new BridgeIDBVersionChangeEvent("upgradeneeded", {
|
||||
newVersion: version,
|
||||
oldVersion: existingVersion,
|
||||
});
|
||||
|
||||
request.result = db;
|
||||
request.readyState = "done";
|
||||
request.transaction = transaction;
|
||||
request.dispatchEvent(event);
|
||||
|
||||
await transaction._waitDone();
|
||||
|
||||
// We don't explicitly exit the versionchange transaction,
|
||||
// since this is already done by the BridgeIDBTransaction.
|
||||
db._runningVersionchangeTransaction = false;
|
||||
|
||||
const event2 = new FakeEvent("success", {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
});
|
||||
event2.eventPath = [request];
|
||||
|
||||
request.dispatchEvent(event2);
|
||||
}
|
||||
|
||||
this.connections.push(db);
|
||||
return db;
|
||||
});
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#dom-idbfactory-databases
|
||||
public databases(): Promise<DatabaseList> {
|
||||
return this.backend.getDatabases();
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return "[object IDBFactory]";
|
||||
}
|
||||
|
||||
private _anyOpen(): boolean {
|
||||
return this.connections.some((c) => !c._closed && !c._closePending);
|
||||
}
|
||||
}
|
@ -1,319 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 Jeremy Scheff
|
||||
Copyright 2019 Florian Dold
|
||||
|
||||
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 {
|
||||
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
|
||||
/** @public */
|
||||
export class BridgeIDBIndex {
|
||||
objectStore: BridgeIDBObjectStore;
|
||||
|
||||
get _schema(): Schema {
|
||||
return this.objectStore.transaction.db._schema;
|
||||
}
|
||||
|
||||
get keyPath(): KeyPath {
|
||||
return this._schema.objectStores[this.objectStore.name].indexes[this._name]
|
||||
.keyPath;
|
||||
}
|
||||
|
||||
get multiEntry(): boolean {
|
||||
return this._schema.objectStores[this.objectStore.name].indexes[this._name]
|
||||
.multiEntry;
|
||||
}
|
||||
|
||||
get unique(): boolean {
|
||||
return this._schema.objectStores[this.objectStore.name].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, this.objectStore.name, 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;
|
@ -1,131 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 Florian Dold
|
||||
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 compareKeys from "./util/cmp";
|
||||
import { DataError } from "./util/errors";
|
||||
import { Key } from "./util/types";
|
||||
import valueToKey from "./util/valueToKey";
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#range-concept
|
||||
/** @public */
|
||||
export class BridgeIDBKeyRange {
|
||||
public static only(value: Key) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
value = valueToKey(value);
|
||||
return new BridgeIDBKeyRange(value, value, false, false);
|
||||
}
|
||||
|
||||
static lowerBound(lower: Key, open: boolean = false) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
lower = valueToKey(lower);
|
||||
return new BridgeIDBKeyRange(lower, undefined, open, true);
|
||||
}
|
||||
|
||||
static upperBound(upper: Key, open: boolean = false) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
upper = valueToKey(upper);
|
||||
return new BridgeIDBKeyRange(undefined, upper, true, open);
|
||||
}
|
||||
|
||||
static bound(
|
||||
lower: Key,
|
||||
upper: Key,
|
||||
lowerOpen: boolean = false,
|
||||
upperOpen: boolean = false,
|
||||
) {
|
||||
if (arguments.length < 2) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
const cmpResult = compareKeys(lower, upper);
|
||||
if (cmpResult === 1 || (cmpResult === 0 && (lowerOpen || upperOpen))) {
|
||||
throw new DataError();
|
||||
}
|
||||
|
||||
lower = valueToKey(lower);
|
||||
upper = valueToKey(upper);
|
||||
return new BridgeIDBKeyRange(lower, upper, lowerOpen, upperOpen);
|
||||
}
|
||||
|
||||
readonly lower: Key | undefined;
|
||||
readonly upper: Key | undefined;
|
||||
readonly lowerOpen: boolean;
|
||||
readonly upperOpen: boolean;
|
||||
|
||||
constructor(
|
||||
lower: Key | undefined,
|
||||
upper: Key | undefined,
|
||||
lowerOpen: boolean,
|
||||
upperOpen: boolean,
|
||||
) {
|
||||
this.lower = lower;
|
||||
this.upper = upper;
|
||||
this.lowerOpen = lowerOpen;
|
||||
this.upperOpen = upperOpen;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#dom-idbkeyrange-includes
|
||||
includes(key: Key) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
key = valueToKey(key);
|
||||
|
||||
if (this.lower !== undefined) {
|
||||
const cmpResult = compareKeys(this.lower, key);
|
||||
|
||||
if (cmpResult === 1 || (cmpResult === 0 && this.lowerOpen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.upper !== undefined) {
|
||||
const cmpResult = compareKeys(this.upper, key);
|
||||
|
||||
if (cmpResult === -1 || (cmpResult === 0 && this.upperOpen)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[object IDBKeyRange]";
|
||||
}
|
||||
|
||||
static _valueToKeyRange(value: any, nullDisallowedFlag: boolean = false) {
|
||||
if (value instanceof BridgeIDBKeyRange) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
if (nullDisallowedFlag) {
|
||||
throw new DataError();
|
||||
}
|
||||
return new BridgeIDBKeyRange(undefined, undefined, false, false);
|
||||
}
|
||||
|
||||
const key = valueToKey(value);
|
||||
|
||||
return BridgeIDBKeyRange.only(key);
|
||||
}
|
||||
}
|
@ -1,467 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 Florian Dold
|
||||
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 { BridgeIDBIndex } from "./BridgeIDBIndex";
|
||||
import { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
import { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import { BridgeIDBTransaction } from "./BridgeIDBTransaction";
|
||||
|
||||
import {
|
||||
ConstraintError,
|
||||
InvalidAccessError,
|
||||
InvalidStateError,
|
||||
ReadOnlyError,
|
||||
} from "./util/errors";
|
||||
import fakeDOMStringList from "./util/fakeDOMStringList";
|
||||
import {
|
||||
FakeDOMStringList,
|
||||
BridgeIDBCursorDirection,
|
||||
Key,
|
||||
KeyPath,
|
||||
Value,
|
||||
} from "./util/types";
|
||||
import validateKeyPath from "./util/validateKeyPath";
|
||||
import valueToKey from "./util/valueToKey";
|
||||
import {
|
||||
DatabaseTransaction,
|
||||
RecordGetRequest,
|
||||
ResultLevel,
|
||||
StoreLevel,
|
||||
Schema,
|
||||
Backend,
|
||||
DatabaseConnection,
|
||||
} from "./backend-interface";
|
||||
import { BridgeIDBFactory } from "./BridgeIDBFactory";
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
|
||||
/** @public */
|
||||
export class BridgeIDBObjectStore {
|
||||
_indexesCache: Map<string, BridgeIDBIndex> = new Map();
|
||||
|
||||
transaction: BridgeIDBTransaction;
|
||||
|
||||
get autoIncrement(): boolean {
|
||||
return this._schema.objectStores[this._name].autoIncrement;
|
||||
}
|
||||
|
||||
get indexNames(): FakeDOMStringList {
|
||||
return fakeDOMStringList(
|
||||
Object.keys(this._schema.objectStores[this._name].indexes),
|
||||
).sort();
|
||||
}
|
||||
|
||||
get keyPath(): KeyPath | null {
|
||||
return this._schema.objectStores[this._name].keyPath;
|
||||
}
|
||||
|
||||
_name: string;
|
||||
|
||||
get _schema(): Schema {
|
||||
return this.transaction.db._schema;
|
||||
}
|
||||
|
||||
_deleted: boolean = false;
|
||||
|
||||
constructor(transaction: BridgeIDBTransaction, name: string) {
|
||||
this._name = name;
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get _backend(): Backend {
|
||||
return this.transaction.db._backend;
|
||||
}
|
||||
|
||||
get _backendConnection(): DatabaseConnection {
|
||||
return this.transaction.db._backendConnection;
|
||||
}
|
||||
|
||||
_confirmActiveTransaction(): { btx: DatabaseTransaction } {
|
||||
const btx = this.transaction._backendTransaction;
|
||||
if (!btx) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
return { btx };
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-name
|
||||
set name(newName: any) {
|
||||
const transaction = this.transaction;
|
||||
|
||||
if (!transaction.db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
let { btx } = this._confirmActiveTransaction();
|
||||
|
||||
newName = String(newName);
|
||||
|
||||
const oldName = this._name;
|
||||
|
||||
if (newName === oldName) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._backend.renameObjectStore(btx, oldName, newName);
|
||||
this.transaction.db._schema = this._backend.getSchema(
|
||||
this._backendConnection,
|
||||
);
|
||||
}
|
||||
|
||||
public _store(value: Value, key: Key | undefined, overwrite: boolean) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log(`TRACE: IDBObjectStore._store`);
|
||||
}
|
||||
if (this.transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
const operation = async () => {
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
const result = await this._backend.storeRecord(btx, {
|
||||
objectStoreName: this._name,
|
||||
key: key,
|
||||
value: value,
|
||||
storeLevel: overwrite
|
||||
? StoreLevel.AllowOverwrite
|
||||
: StoreLevel.NoOverwrite,
|
||||
});
|
||||
return result.key;
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({ operation, source: this });
|
||||
}
|
||||
|
||||
public put(value: Value, key?: Key) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
return this._store(value, key, true);
|
||||
}
|
||||
|
||||
public add(value: Value, key?: Key) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
return this._store(value, key, false);
|
||||
}
|
||||
|
||||
public delete(key: Key | BridgeIDBKeyRange) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
|
||||
let keyRange: BridgeIDBKeyRange;
|
||||
|
||||
if (key instanceof BridgeIDBKeyRange) {
|
||||
keyRange = key;
|
||||
} else {
|
||||
keyRange = BridgeIDBKeyRange.only(valueToKey(key));
|
||||
}
|
||||
|
||||
const operation = async () => {
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
return this._backend.deleteRecord(btx, this._name, keyRange);
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
public get(key?: BridgeIDBKeyRange | Key) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log(`getting from object store ${this._name} key ${key}`);
|
||||
}
|
||||
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
let keyRange: BridgeIDBKeyRange;
|
||||
|
||||
if (key instanceof BridgeIDBKeyRange) {
|
||||
keyRange = key;
|
||||
} else {
|
||||
try {
|
||||
keyRange = BridgeIDBKeyRange.only(valueToKey(key));
|
||||
} catch (e) {
|
||||
throw Error(
|
||||
`invalid key (type ${typeof key}) for object store ${this._name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const recordRequest: RecordGetRequest = {
|
||||
objectStoreName: this._name,
|
||||
indexName: undefined,
|
||||
lastIndexPosition: undefined,
|
||||
lastObjectStorePosition: undefined,
|
||||
direction: "next",
|
||||
limit: 1,
|
||||
resultLevel: ResultLevel.Full,
|
||||
range: keyRange,
|
||||
};
|
||||
|
||||
const operation = async () => {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("running get operation:", recordRequest);
|
||||
}
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
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 values = result.values;
|
||||
if (!values) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
return values[0];
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getall
|
||||
public getAll(query?: BridgeIDBKeyRange | Key, count?: number) {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
|
||||
public getKey(key?: BridgeIDBKeyRange | Key) {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
|
||||
public getAllKeys(query?: BridgeIDBKeyRange | Key, count?: number) {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
public clear() {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
public openCursor(
|
||||
range?: BridgeIDBKeyRange | Key,
|
||||
direction: BridgeIDBCursorDirection = "next",
|
||||
) {
|
||||
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.transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursorWithValue(
|
||||
this,
|
||||
this._name,
|
||||
undefined,
|
||||
range,
|
||||
direction,
|
||||
request,
|
||||
);
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
operation: () => cursor._iterate(),
|
||||
request,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
public openKeyCursor(
|
||||
range?: BridgeIDBKeyRange | Key,
|
||||
direction?: BridgeIDBCursorDirection,
|
||||
) {
|
||||
if (range === null) {
|
||||
range = undefined;
|
||||
}
|
||||
if (range !== undefined && !(range instanceof BridgeIDBKeyRange)) {
|
||||
range = BridgeIDBKeyRange.only(valueToKey(range));
|
||||
}
|
||||
|
||||
if (!direction) {
|
||||
direction = "next";
|
||||
}
|
||||
|
||||
const request = new BridgeIDBRequest();
|
||||
request.source = this;
|
||||
request.transaction = this.transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursor(
|
||||
this,
|
||||
this._name,
|
||||
undefined,
|
||||
range,
|
||||
direction,
|
||||
request,
|
||||
true,
|
||||
);
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
operation: cursor._iterate.bind(cursor),
|
||||
request,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-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
|
||||
public createIndex(
|
||||
indexName: string,
|
||||
keyPath: KeyPath,
|
||||
optionalParameters: { multiEntry?: boolean; unique?: boolean } = {},
|
||||
) {
|
||||
if (arguments.length < 2) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (!this.transaction.db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
|
||||
const multiEntry =
|
||||
optionalParameters.multiEntry !== undefined
|
||||
? optionalParameters.multiEntry
|
||||
: false;
|
||||
const unique =
|
||||
optionalParameters.unique !== undefined
|
||||
? optionalParameters.unique
|
||||
: false;
|
||||
|
||||
if (this.transaction.mode !== "versionchange") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (this.indexNames.indexOf(indexName) >= 0) {
|
||||
throw new ConstraintError();
|
||||
}
|
||||
|
||||
validateKeyPath(keyPath);
|
||||
|
||||
if (Array.isArray(keyPath) && multiEntry) {
|
||||
throw new InvalidAccessError();
|
||||
}
|
||||
|
||||
this._backend.createIndex(
|
||||
btx,
|
||||
indexName,
|
||||
this._name,
|
||||
keyPath,
|
||||
multiEntry,
|
||||
unique,
|
||||
);
|
||||
|
||||
return new BridgeIDBIndex(this, indexName);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#dom-idbobjectstore-index
|
||||
public index(name: string) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction._state === "finished") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const index = this._indexesCache.get(name);
|
||||
if (index !== undefined) {
|
||||
return index;
|
||||
}
|
||||
|
||||
return new BridgeIDBIndex(this, name);
|
||||
}
|
||||
|
||||
public deleteIndex(indexName: string) {
|
||||
if (arguments.length === 0) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction.mode !== "versionchange") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!this.transaction.db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
|
||||
const index = this._indexesCache.get(indexName);
|
||||
if (index !== undefined) {
|
||||
index._deleted = true;
|
||||
}
|
||||
|
||||
this._backend.deleteIndex(btx, this._name, indexName);
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBObjectStore-count-IDBRequest-any-key
|
||||
public count(key?: Key | BridgeIDBKeyRange) {
|
||||
if (key === null) {
|
||||
key = undefined;
|
||||
}
|
||||
if (key !== undefined && !(key instanceof BridgeIDBKeyRange)) {
|
||||
key = BridgeIDBKeyRange.only(valueToKey(key));
|
||||
}
|
||||
|
||||
const recordGetRequest: RecordGetRequest = {
|
||||
direction: "next",
|
||||
indexName: undefined,
|
||||
lastIndexPosition: undefined,
|
||||
limit: -1,
|
||||
objectStoreName: this._name,
|
||||
lastObjectStorePosition: undefined,
|
||||
range: key,
|
||||
resultLevel: ResultLevel.OnlyCount,
|
||||
};
|
||||
|
||||
const operation = async () => {
|
||||
const { btx } = this._confirmActiveTransaction();
|
||||
const result = await this._backend.getRecords(btx, recordGetRequest);
|
||||
return result.count;
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({ operation, source: this });
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBObjectStore]";
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 Florian Dold
|
||||
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 { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import { EventListener } from "./idbtypes";
|
||||
|
||||
/** @public */
|
||||
export class BridgeIDBOpenDBRequest extends BridgeIDBRequest {
|
||||
public onupgradeneeded: EventListener | null = null;
|
||||
public onblocked: EventListener | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// https://www.w3.org/TR/IndexedDB/#open-requests
|
||||
this.source = null;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBOpenDBRequest]";
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Jeremy Scheff
|
||||
* Copyright 2019 Florian Dold
|
||||
*
|
||||
* 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 as BridgeFIBCursor } from "./BridgeIDBCursor";
|
||||
import { BridgeIDBIndex } from "./BridgeIDBIndex";
|
||||
import { BridgeIDBObjectStore } from "./BridgeIDBObjectStore";
|
||||
import { BridgeIDBTransaction } from "./BridgeIDBTransaction";
|
||||
import { InvalidStateError } from "./util/errors";
|
||||
import FakeEventTarget from "./util/FakeEventTarget";
|
||||
import FakeEvent from "./util/FakeEvent";
|
||||
import { EventListener } from "./idbtypes";
|
||||
|
||||
/** @public */
|
||||
export class BridgeIDBRequest extends FakeEventTarget {
|
||||
_result: any = null;
|
||||
_error: Error | null | undefined = null;
|
||||
source: BridgeFIBCursor | BridgeIDBIndex | BridgeIDBObjectStore | null = null;
|
||||
transaction: BridgeIDBTransaction | null = null;
|
||||
readyState: "done" | "pending" = "pending";
|
||||
onsuccess: EventListener | null = null;
|
||||
onerror: EventListener | null = null;
|
||||
|
||||
get error() {
|
||||
if (this.readyState === "pending") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
return this._error;
|
||||
}
|
||||
|
||||
set error(value: any) {
|
||||
this._error = value;
|
||||
}
|
||||
|
||||
get result() {
|
||||
if (this.readyState === "pending") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
return this._result;
|
||||
}
|
||||
|
||||
set result(value: any) {
|
||||
this._result = value;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return "[object IDBRequest]";
|
||||
}
|
||||
|
||||
_finishWithError(err: Error) {
|
||||
this.result = undefined;
|
||||
this.readyState = "done";
|
||||
|
||||
this.error = new Error(err.message);
|
||||
this.error.name = err.name;
|
||||
|
||||
const event = new FakeEvent("error", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.eventPath = [];
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
_finishWithResult(result: any) {
|
||||
this.result = result;
|
||||
this.readyState = "done";
|
||||
|
||||
const event = new FakeEvent("success");
|
||||
event.eventPath = [];
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
import { BridgeIDBDatabase } from "./BridgeIDBDatabase";
|
||||
import { BridgeIDBObjectStore } from "./BridgeIDBObjectStore";
|
||||
import { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import {
|
||||
AbortError,
|
||||
InvalidStateError,
|
||||
NotFoundError,
|
||||
TransactionInactiveError,
|
||||
} from "./util/errors";
|
||||
import fakeDOMStringList from "./util/fakeDOMStringList";
|
||||
import FakeEvent from "./util/FakeEvent";
|
||||
import FakeEventTarget from "./util/FakeEventTarget";
|
||||
import { FakeDOMStringList, RequestObj, TransactionMode } from "./util/types";
|
||||
import queueTask from "./util/queueTask";
|
||||
import openPromise from "./util/openPromise";
|
||||
import { DatabaseTransaction, Backend } from "./backend-interface";
|
||||
import { BridgeIDBFactory } from "./BridgeIDBFactory";
|
||||
import { EventListener } from "./idbtypes";
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
|
||||
/** @public */
|
||||
export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
public _state: "active" | "inactive" | "committing" | "finished" = "active";
|
||||
public _started = false;
|
||||
public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
|
||||
|
||||
public _backendTransaction?: DatabaseTransaction;
|
||||
|
||||
public objectStoreNames: FakeDOMStringList;
|
||||
public mode: TransactionMode;
|
||||
public db: BridgeIDBDatabase;
|
||||
public error: Error | null = null;
|
||||
public onabort: EventListener | null = null;
|
||||
public oncomplete: EventListener | null = null;
|
||||
public onerror: EventListener | null = null;
|
||||
|
||||
private _waitPromise: Promise<void>;
|
||||
private _resolveWait: () => void;
|
||||
|
||||
public _scope: Set<string>;
|
||||
private _requests: Array<{
|
||||
operation: () => void;
|
||||
request: BridgeIDBRequest;
|
||||
}> = [];
|
||||
|
||||
get _backend(): Backend {
|
||||
return this.db._backend;
|
||||
}
|
||||
|
||||
constructor(
|
||||
storeNames: string[],
|
||||
mode: TransactionMode,
|
||||
db: BridgeIDBDatabase,
|
||||
backendTransaction?: DatabaseTransaction,
|
||||
) {
|
||||
super();
|
||||
|
||||
const myOpenPromise = openPromise<void>();
|
||||
this._waitPromise = myOpenPromise.promise;
|
||||
this._resolveWait = myOpenPromise.resolve;
|
||||
|
||||
this._scope = new Set(storeNames);
|
||||
this._backendTransaction = backendTransaction;
|
||||
this.mode = mode;
|
||||
this.db = db;
|
||||
this.objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
|
||||
|
||||
this.db._transactions.push(this);
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
|
||||
async _abort(errName: string | null) {
|
||||
this._state = "finished";
|
||||
|
||||
if (errName !== null) {
|
||||
const e = new Error();
|
||||
e.name = errName;
|
||||
this.error = e;
|
||||
}
|
||||
|
||||
// Should this directly remove from _requests?
|
||||
for (const { request } of this._requests) {
|
||||
if (request.readyState !== "done") {
|
||||
request.readyState = "done"; // This will cancel execution of this request's operation
|
||||
if (request.source) {
|
||||
request.result = undefined;
|
||||
request.error = new AbortError();
|
||||
|
||||
const event = new FakeEvent("error", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.eventPath = [this.db, this];
|
||||
request.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only roll back if we actually executed the scheduled operations.
|
||||
const maybeBtx = this._backendTransaction;
|
||||
if (maybeBtx) {
|
||||
await this._backend.rollback(maybeBtx);
|
||||
}
|
||||
|
||||
queueTask(() => {
|
||||
const event = new FakeEvent("abort", {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
});
|
||||
event.eventPath = [this.db];
|
||||
this.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
public abort() {
|
||||
if (this._state === "committing" || this._state === "finished") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
this._state = "active";
|
||||
|
||||
this._abort(null);
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
|
||||
public objectStore(name: string) {
|
||||
if (this._state !== "active") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
const objectStore = this._objectStoresCache.get(name);
|
||||
if (objectStore !== undefined) {
|
||||
return objectStore;
|
||||
}
|
||||
|
||||
return new BridgeIDBObjectStore(this, name);
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-asynchronously-executing-a-request
|
||||
public _execRequestAsync(obj: RequestObj) {
|
||||
const source = obj.source;
|
||||
const operation = obj.operation;
|
||||
let request = obj.hasOwnProperty("request") ? obj.request : null;
|
||||
|
||||
if (this._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
// Request should only be passed for cursors
|
||||
if (!request) {
|
||||
if (!source) {
|
||||
// Special requests like indexes that just need to run some code
|
||||
request = new BridgeIDBRequest();
|
||||
} else {
|
||||
request = new BridgeIDBRequest();
|
||||
request.source = source;
|
||||
request.transaction = (source as any).transaction;
|
||||
}
|
||||
}
|
||||
|
||||
this._requests.push({
|
||||
operation,
|
||||
request,
|
||||
});
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually execute the scheduled work for this transaction.
|
||||
*/
|
||||
public async _start() {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log(
|
||||
`TRACE: IDBTransaction._start, ${this._requests.length} queued`,
|
||||
);
|
||||
}
|
||||
this._started = true;
|
||||
|
||||
if (!this._backendTransaction) {
|
||||
this._backendTransaction = await this._backend.beginTransaction(
|
||||
this.db._backendConnection,
|
||||
Array.from(this._scope),
|
||||
this.mode,
|
||||
);
|
||||
}
|
||||
|
||||
// Remove from request queue - cursor ones will be added back if necessary by cursor.continue and such
|
||||
let operation;
|
||||
let request;
|
||||
while (this._requests.length > 0) {
|
||||
const r = this._requests.shift();
|
||||
|
||||
// This should only be false if transaction was aborted
|
||||
if (r && r.request.readyState !== "done") {
|
||||
request = r.request;
|
||||
operation = r.operation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (request && operation) {
|
||||
if (!request.source) {
|
||||
// Special requests like indexes that just need to run some code, with error handling already built into
|
||||
// operation
|
||||
await operation();
|
||||
} else {
|
||||
let event;
|
||||
try {
|
||||
BridgeIDBFactory.enableTracing &&
|
||||
console.log("TRACE: running operation in transaction");
|
||||
const result = await operation();
|
||||
BridgeIDBFactory.enableTracing &&
|
||||
console.log(
|
||||
"TRACE: operation in transaction finished with success",
|
||||
);
|
||||
request.readyState = "done";
|
||||
request.result = result;
|
||||
request.error = undefined;
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-a-success-event
|
||||
if (this._state === "inactive") {
|
||||
this._state = "active";
|
||||
}
|
||||
event = new FakeEvent("success", {
|
||||
bubbles: false,
|
||||
cancelable: false,
|
||||
});
|
||||
|
||||
try {
|
||||
event.eventPath = [request, this, this.db];
|
||||
request.dispatchEvent(event);
|
||||
} catch (err) {
|
||||
if (this._state !== "committing") {
|
||||
this._abort("AbortError");
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
} catch (err) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("TRACING: error during operation: ", err);
|
||||
}
|
||||
request.readyState = "done";
|
||||
request.result = undefined;
|
||||
request.error = err;
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event
|
||||
if (this._state === "inactive") {
|
||||
this._state = "active";
|
||||
}
|
||||
event = new FakeEvent("error", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
try {
|
||||
event.eventPath = [this.db, this];
|
||||
request.dispatchEvent(event);
|
||||
} catch (err) {
|
||||
if (this._state !== "committing") {
|
||||
this._abort("AbortError");
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
if (!event.canceled) {
|
||||
this._abort(err.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On to the next one
|
||||
if (this._requests.length > 0) {
|
||||
this._start();
|
||||
} else {
|
||||
// Give it another chance for new handlers to be set before finishing
|
||||
queueTask(() => this._start());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._state !== "finished" && this._state !== "committing") {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("finishing transaction");
|
||||
}
|
||||
|
||||
this._state = "committing";
|
||||
|
||||
await this._backend.commit(this._backendTransaction);
|
||||
|
||||
this._state = "finished";
|
||||
|
||||
if (!this.error) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("dispatching 'complete' event on transaction");
|
||||
}
|
||||
const event = new FakeEvent("complete");
|
||||
event.eventPath = [this, this.db];
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
const idx = this.db._transactions.indexOf(this);
|
||||
if (idx < 0) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
this.db._transactions.splice(idx, 1);
|
||||
|
||||
this._resolveWait();
|
||||
}
|
||||
}
|
||||
|
||||
public commit() {
|
||||
if (this._state !== "active") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
this._state = "committing";
|
||||
// We now just wait for auto-commit ...
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBRequest]";
|
||||
}
|
||||
|
||||
_waitDone(): Promise<void> {
|
||||
return this._waitPromise;
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 Florian Dold
|
||||
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 FakeEvent from "./util/FakeEvent";
|
||||
|
||||
export class BridgeIDBVersionChangeEvent extends FakeEvent {
|
||||
public newVersion: number | null;
|
||||
public oldVersion: number;
|
||||
|
||||
constructor(
|
||||
type: "blocked" | "success" | "upgradeneeded" | "versionchange",
|
||||
parameters: { newVersion?: number | null; oldVersion?: number } = {},
|
||||
) {
|
||||
super(type);
|
||||
|
||||
this.newVersion =
|
||||
parameters.newVersion !== undefined ? parameters.newVersion : null;
|
||||
this.oldVersion =
|
||||
parameters.oldVersion !== undefined ? parameters.oldVersion : 0;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "[object IDBVersionChangeEvent]";
|
||||
}
|
||||
}
|
@ -15,13 +15,15 @@
|
||||
*/
|
||||
|
||||
import test from "ava";
|
||||
import {
|
||||
BridgeIDBCursorWithValue,
|
||||
BridgeIDBDatabase,
|
||||
BridgeIDBFactory,
|
||||
BridgeIDBKeyRange,
|
||||
BridgeIDBRequest,
|
||||
BridgeIDBTransaction,
|
||||
} from "./bridge-idb";
|
||||
import MemoryBackend from "./MemoryBackend";
|
||||
import { BridgeIDBFactory } from "./BridgeIDBFactory";
|
||||
import { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import { BridgeIDBDatabase } from "./BridgeIDBDatabase";
|
||||
import { BridgeIDBTransaction } from "./BridgeIDBTransaction";
|
||||
import { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
import { BridgeIDBCursorWithValue } from "./BridgeIDBCursorWithValue";
|
||||
|
||||
function promiseFromRequest(request: BridgeIDBRequest): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -36,11 +36,14 @@ import {
|
||||
} from "./util/errors";
|
||||
import BTree, { ISortedMapF } from "./tree/b+tree";
|
||||
import compareKeys from "./util/cmp";
|
||||
import { Key, Value, KeyPath, TransactionMode } from "./util/types";
|
||||
import { StoreKeyResult, makeStoreKeyValue } from "./util/makeStoreKeyValue";
|
||||
import getIndexKeys from "./util/getIndexKeys";
|
||||
import openPromise from "./util/openPromise";
|
||||
import { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
import { IDBKeyPath, IDBKeyRange, IDBTransactionMode, IDBValidKey } from "./idbtypes";
|
||||
import { BridgeIDBKeyRange } from "./bridge-idb";
|
||||
|
||||
type Key = IDBValidKey;
|
||||
type Value = unknown;
|
||||
|
||||
enum TransactionLevel {
|
||||
Disconnected = 0,
|
||||
@ -476,7 +479,7 @@ export class MemoryBackend implements Backend {
|
||||
async beginTransaction(
|
||||
conn: DatabaseConnection,
|
||||
objectStores: string[],
|
||||
mode: TransactionMode,
|
||||
mode: IDBTransactionMode,
|
||||
): Promise<DatabaseTransaction> {
|
||||
if (this.enableTracing) {
|
||||
console.log(`TRACING: beginTransaction`);
|
||||
@ -773,6 +776,9 @@ export class MemoryBackend implements Backend {
|
||||
if (!schema) {
|
||||
throw Error("no schema for versionchange tx");
|
||||
}
|
||||
if (Array.isArray(keyPath)) {
|
||||
throw Error("array key path not supported for object stores");
|
||||
}
|
||||
schema.objectStores[name] = {
|
||||
autoIncrement,
|
||||
keyPath,
|
||||
@ -785,7 +791,7 @@ export class MemoryBackend implements Backend {
|
||||
btx: DatabaseTransaction,
|
||||
indexName: string,
|
||||
objectStoreName: string,
|
||||
keyPath: KeyPath,
|
||||
keyPath: IDBKeyPath,
|
||||
multiEntry: boolean,
|
||||
unique: boolean,
|
||||
): void {
|
||||
@ -843,7 +849,7 @@ export class MemoryBackend implements Backend {
|
||||
async deleteRecord(
|
||||
btx: DatabaseTransaction,
|
||||
objectStoreName: string,
|
||||
range: BridgeIDBKeyRange,
|
||||
range: IDBKeyRange,
|
||||
): Promise<void> {
|
||||
if (this.enableTracing) {
|
||||
console.log(`TRACING: deleteRecord from store ${objectStoreName}`);
|
||||
@ -900,6 +906,10 @@ export class MemoryBackend implements Backend {
|
||||
}
|
||||
}
|
||||
|
||||
if (currKey === undefined) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
|
||||
// make sure that currKey is either undefined or pointing to an
|
||||
// existing object.
|
||||
let firstValue = modifiedData.get(currKey);
|
||||
@ -1112,6 +1122,10 @@ export class MemoryBackend implements Backend {
|
||||
indexPos = forward ? indexData.minKey() : indexData.maxKey();
|
||||
}
|
||||
|
||||
if (indexPos === undefined) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
|
||||
let indexEntry: IndexRecord | undefined;
|
||||
indexEntry = indexData.get(indexPos);
|
||||
if (!indexEntry) {
|
||||
@ -1191,13 +1205,13 @@ export class MemoryBackend implements Backend {
|
||||
primkeySubPos < 0 ||
|
||||
primkeySubPos >= indexEntry.primaryKeys.length
|
||||
) {
|
||||
const res = forward
|
||||
const res: any = forward
|
||||
? indexData.nextHigherPair(indexPos)
|
||||
: indexData.nextLowerPair(indexPos);
|
||||
if (res) {
|
||||
indexPos = res[1].indexKey;
|
||||
indexEntry = res[1];
|
||||
primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
|
||||
primkeySubPos = forward ? 0 : indexEntry!.primaryKeys.length - 1;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
|
@ -14,26 +14,25 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { BridgeIDBDatabaseInfo, BridgeIDBKeyRange } from "./bridge-idb";
|
||||
import {
|
||||
TransactionMode,
|
||||
Value,
|
||||
BridgeIDBCursorDirection,
|
||||
Key,
|
||||
KeyPath,
|
||||
BridgeIDBDatabaseInfo,
|
||||
} from "./util/types";
|
||||
import { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
IDBCursorDirection,
|
||||
IDBKeyPath,
|
||||
IDBTransactionMode,
|
||||
IDBValidKey,
|
||||
} from "./idbtypes";
|
||||
|
||||
|
||||
/** @public */
|
||||
export interface ObjectStoreProperties {
|
||||
keyPath: KeyPath | null;
|
||||
keyPath: IDBKeyPath | null;
|
||||
autoIncrement: boolean;
|
||||
indexes: { [nameame: string]: IndexProperties };
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface IndexProperties {
|
||||
keyPath: KeyPath;
|
||||
keyPath: IDBKeyPath;
|
||||
multiEntry: boolean;
|
||||
unique: boolean;
|
||||
}
|
||||
@ -71,7 +70,7 @@ export enum StoreLevel {
|
||||
|
||||
/** @public */
|
||||
export interface RecordGetRequest {
|
||||
direction: BridgeIDBCursorDirection;
|
||||
direction: IDBCursorDirection;
|
||||
objectStoreName: string;
|
||||
indexName: string | undefined;
|
||||
/**
|
||||
@ -79,7 +78,7 @@ export interface RecordGetRequest {
|
||||
* If indexName is defined, the range refers to the index keys.
|
||||
* Otherwise it refers to the object store keys.
|
||||
*/
|
||||
range: BridgeIDBKeyRange | undefined;
|
||||
range: BridgeIDBKeyRange | undefined | null;
|
||||
/**
|
||||
* Last cursor position in terms of the index key.
|
||||
* Can only be specified if indexName is defined and
|
||||
@ -87,23 +86,23 @@ export interface RecordGetRequest {
|
||||
*
|
||||
* Must either be undefined or within range.
|
||||
*/
|
||||
lastIndexPosition?: Key;
|
||||
lastIndexPosition?: IDBValidKey;
|
||||
/**
|
||||
* Last position in terms of the object store key.
|
||||
*/
|
||||
lastObjectStorePosition?: Key;
|
||||
lastObjectStorePosition?: IDBValidKey;
|
||||
/**
|
||||
* If specified, the index key of the results must be
|
||||
* greater or equal to advanceIndexKey.
|
||||
*
|
||||
* Only applicable if indexName is specified.
|
||||
*/
|
||||
advanceIndexKey?: Key;
|
||||
advanceIndexKey?: IDBValidKey;
|
||||
/**
|
||||
* If specified, the primary key of the results must be greater
|
||||
* or equal to advancePrimaryKey.
|
||||
*/
|
||||
advancePrimaryKey?: Key;
|
||||
advancePrimaryKey?: IDBValidKey;
|
||||
/**
|
||||
* Maximum number of resuts to return.
|
||||
* If -1, return all available results
|
||||
@ -114,17 +113,17 @@ export interface RecordGetRequest {
|
||||
|
||||
/** @public */
|
||||
export interface RecordGetResponse {
|
||||
values: Value[] | undefined;
|
||||
indexKeys: Key[] | undefined;
|
||||
primaryKeys: Key[] | undefined;
|
||||
values: any[] | undefined;
|
||||
indexKeys: IDBValidKey[] | undefined;
|
||||
primaryKeys: IDBValidKey[] | undefined;
|
||||
count: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface RecordStoreRequest {
|
||||
objectStoreName: string;
|
||||
value: Value;
|
||||
key: Key | undefined;
|
||||
value: any;
|
||||
key: IDBValidKey | undefined;
|
||||
storeLevel: StoreLevel;
|
||||
}
|
||||
|
||||
@ -133,7 +132,7 @@ export interface RecordStoreResponse {
|
||||
/**
|
||||
* Key that the record was stored under in the object store.
|
||||
*/
|
||||
key: Key;
|
||||
key: IDBValidKey;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
@ -145,7 +144,7 @@ export interface Backend {
|
||||
beginTransaction(
|
||||
conn: DatabaseConnection,
|
||||
objectStores: string[],
|
||||
mode: TransactionMode,
|
||||
mode: IDBTransactionMode,
|
||||
): Promise<DatabaseTransaction>;
|
||||
|
||||
enterVersionChange(
|
||||
@ -200,7 +199,7 @@ export interface Backend {
|
||||
btx: DatabaseTransaction,
|
||||
indexName: string,
|
||||
objectStoreName: string,
|
||||
keyPath: KeyPath,
|
||||
keyPath: IDBKeyPath,
|
||||
multiEntry: boolean,
|
||||
unique: boolean,
|
||||
): void;
|
||||
|
2053
packages/idb-bridge/src/bridge-idb.ts
Normal file
2053
packages/idb-bridge/src/bridge-idb.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,3 @@
|
||||
import { BridgeIDBFactory, DatabaseList } from "./BridgeIDBFactory";
|
||||
import { BridgeIDBCursor } from "./BridgeIDBCursor";
|
||||
import { BridgeIDBIndex } from "./BridgeIDBIndex";
|
||||
import { BridgeIDBDatabase } from "./BridgeIDBDatabase";
|
||||
import { BridgeIDBKeyRange } from "./BridgeIDBKeyRange";
|
||||
import { BridgeIDBObjectStore } from "./BridgeIDBObjectStore";
|
||||
import { BridgeIDBOpenDBRequest } from "./BridgeIDBOpenDBRequest";
|
||||
import { BridgeIDBRequest } from "./BridgeIDBRequest";
|
||||
import { BridgeIDBTransaction } from "./BridgeIDBTransaction";
|
||||
import { BridgeIDBVersionChangeEvent } from "./BridgeIDBVersionChangeEvent";
|
||||
import {
|
||||
Value,
|
||||
CursorSource,
|
||||
CursorRange,
|
||||
BridgeIDBCursorDirection,
|
||||
Key,
|
||||
KeyPath,
|
||||
TransactionMode,
|
||||
FakeDOMStringList,
|
||||
RequestObj,
|
||||
BridgeIDBDatabaseInfo,
|
||||
EventType,
|
||||
} from "./util/types";
|
||||
import {
|
||||
DatabaseTransaction,
|
||||
RecordGetResponse,
|
||||
@ -45,12 +22,9 @@ import {
|
||||
MemoryBackendDump,
|
||||
} from "./MemoryBackend";
|
||||
import { Event } from "./idbtypes";
|
||||
|
||||
export {
|
||||
import {
|
||||
BridgeIDBCursor,
|
||||
BridgeIDBCursorDirection,
|
||||
BridgeIDBDatabase,
|
||||
BridgeIDBDatabaseInfo,
|
||||
BridgeIDBFactory,
|
||||
BridgeIDBIndex,
|
||||
BridgeIDBKeyRange,
|
||||
@ -58,33 +32,40 @@ export {
|
||||
BridgeIDBOpenDBRequest,
|
||||
BridgeIDBRequest,
|
||||
BridgeIDBTransaction,
|
||||
Value,
|
||||
CursorSource,
|
||||
CursorRange,
|
||||
Key,
|
||||
DatabaseList,
|
||||
RequestObj,
|
||||
} from "./bridge-idb";
|
||||
|
||||
export {
|
||||
BridgeIDBCursor,
|
||||
BridgeIDBDatabase,
|
||||
BridgeIDBFactory,
|
||||
BridgeIDBIndex,
|
||||
BridgeIDBKeyRange,
|
||||
BridgeIDBObjectStore,
|
||||
BridgeIDBOpenDBRequest,
|
||||
BridgeIDBRequest,
|
||||
BridgeIDBTransaction,
|
||||
StoreLevel,
|
||||
ResultLevel,
|
||||
};
|
||||
export type {
|
||||
DatabaseTransaction,
|
||||
RecordGetRequest,
|
||||
RecordGetResponse,
|
||||
KeyPath,
|
||||
Schema,
|
||||
Backend,
|
||||
TransactionMode,
|
||||
DatabaseList,
|
||||
RecordStoreRequest,
|
||||
RecordStoreResponse,
|
||||
FakeEventTarget,
|
||||
DatabaseConnection,
|
||||
FakeDOMStringList,
|
||||
ObjectStoreProperties,
|
||||
RequestObj,
|
||||
StoreLevel,
|
||||
ResultLevel,
|
||||
DatabaseDump,
|
||||
ObjectStoreDump,
|
||||
IndexDump,
|
||||
IndexRecord,
|
||||
ObjectStoreRecord,
|
||||
EventType,
|
||||
IndexProperties,
|
||||
MemoryBackendDump,
|
||||
Event,
|
||||
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: MIT
|
||||
// Original repository: https://github.com/qwertie/btree-typescript
|
||||
|
||||
import { ISortedMap, ISortedMapF } from "./interfaces";
|
||||
export {
|
||||
export type {
|
||||
ISetSource,
|
||||
ISetSink,
|
||||
ISet,
|
||||
|
@ -15,9 +15,18 @@
|
||||
*/
|
||||
|
||||
import FakeEventTarget from "./FakeEventTarget";
|
||||
import { EventType } from "./types";
|
||||
import { Event, EventTarget } from "../idbtypes";
|
||||
|
||||
/** @public */
|
||||
export type EventType =
|
||||
| "abort"
|
||||
| "blocked"
|
||||
| "complete"
|
||||
| "error"
|
||||
| "success"
|
||||
| "upgradeneeded"
|
||||
| "versionchange";
|
||||
|
||||
export class FakeEvent implements Event {
|
||||
public eventPath: FakeEventTarget[] = [];
|
||||
public type: EventType;
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
|
||||
import { InvalidStateError } from "./errors";
|
||||
import FakeEvent from "./FakeEvent";
|
||||
import { EventType } from "./types";
|
||||
import FakeEvent, { EventType } from "./FakeEvent";
|
||||
import {
|
||||
EventTarget,
|
||||
Event,
|
||||
|
@ -14,10 +14,10 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { KeyPath, Value } from "./types";
|
||||
import { IDBKeyPath } from "../idbtypes";
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#check-that-a-key-could-be-injected-into-a-value
|
||||
const canInjectKey = (keyPath: KeyPath, value: Value) => {
|
||||
const canInjectKey = (keyPath: IDBKeyPath, value: any) => {
|
||||
if (Array.isArray(keyPath)) {
|
||||
// tslint:disable-next-line max-line-length
|
||||
throw new Error(
|
||||
|
@ -24,7 +24,7 @@ const isArray = Array.isArray;
|
||||
const keyList = Object.keys;
|
||||
const hasProp = Object.prototype.hasOwnProperty;
|
||||
|
||||
function deepEquals(a: any, b: any): boolean {
|
||||
export function deepEquals(a: any, b: any): boolean {
|
||||
if (a === b) return true;
|
||||
|
||||
if (a && b && typeof a == "object" && typeof b == "object") {
|
||||
|
@ -15,13 +15,13 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { Key, KeyPath, Value } from "./types";
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
import valueToKey from "./valueToKey";
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-extracting-a-key-from-a-value-using-a-key-path
|
||||
const extractKey = (keyPath: KeyPath, value: Value) => {
|
||||
const extractKey = (keyPath: IDBKeyPath | IDBKeyPath[], value: any) => {
|
||||
if (Array.isArray(keyPath)) {
|
||||
const result: Key[] = [];
|
||||
const result: IDBValidKey[] = [];
|
||||
|
||||
for (let item of keyPath) {
|
||||
// This doesn't make sense to me based on the spec, but it is needed to pass the W3C KeyPath tests (see same
|
||||
|
@ -14,11 +14,15 @@
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { FakeDOMStringList } from "./types";
|
||||
/** @public */
|
||||
export interface FakeDOMStringList extends Array<string> {
|
||||
contains: (value: string) => boolean;
|
||||
item: (i: number) => string | undefined;
|
||||
}
|
||||
|
||||
// Would be nicer to sublcass Array, but I'd have to sacrifice Node 4 support to do that.
|
||||
|
||||
const fakeDOMStringList = (arr: string[]): FakeDOMStringList => {
|
||||
export const fakeDOMStringList = (arr: string[]): FakeDOMStringList => {
|
||||
const arr2 = arr.slice();
|
||||
|
||||
Object.defineProperty(arr2, "contains", {
|
||||
@ -33,5 +37,3 @@ const fakeDOMStringList = (arr: string[]): FakeDOMStringList => {
|
||||
|
||||
return arr2 as FakeDOMStringList;
|
||||
};
|
||||
|
||||
export default fakeDOMStringList;
|
||||
|
@ -15,15 +15,15 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { Key, Value, KeyPath } from "./types";
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
import extractKey from "./extractKey";
|
||||
import valueToKey from "./valueToKey";
|
||||
|
||||
export function getIndexKeys(
|
||||
value: Value,
|
||||
keyPath: KeyPath,
|
||||
value: any,
|
||||
keyPath: IDBKeyPath | IDBKeyPath[],
|
||||
multiEntry: boolean,
|
||||
): Key[] {
|
||||
): IDBValidKey[] {
|
||||
if (multiEntry && Array.isArray(keyPath)) {
|
||||
const keys = [];
|
||||
for (const subkeyPath of keyPath) {
|
||||
@ -36,9 +36,11 @@ export function getIndexKeys(
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
} else {
|
||||
} else if (typeof keyPath === "string" || Array.isArray(keyPath)) {
|
||||
let key = extractKey(keyPath, value);
|
||||
return [valueToKey(key)];
|
||||
} else {
|
||||
throw Error(`unsupported key path: ${typeof keyPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,14 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { KeyPath, Value, Key } from "./types";
|
||||
import canInjectKey from "./canInjectKey";
|
||||
import { DataError } from "./errors";
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
import structuredClone from "./structuredClone";
|
||||
|
||||
export function injectKey(keyPath: KeyPath, value: Value, key: Key): Value {
|
||||
export function injectKey(
|
||||
keyPath: IDBKeyPath,
|
||||
value: any,
|
||||
key: IDBValidKey,
|
||||
): any {
|
||||
if (Array.isArray(keyPath)) {
|
||||
// tslint:disable-next-line max-line-length
|
||||
throw new Error(
|
||||
|
@ -14,25 +14,25 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { Value, Key, KeyPath } from "./types";
|
||||
import extractKey from "./extractKey";
|
||||
import { DataError } from "./errors";
|
||||
import valueToKey from "./valueToKey";
|
||||
import structuredClone from "./structuredClone";
|
||||
import injectKey from "./injectKey";
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
|
||||
export interface StoreKeyResult {
|
||||
updatedKeyGenerator: number;
|
||||
key: Key;
|
||||
value: Value;
|
||||
key: IDBValidKey;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export function makeStoreKeyValue(
|
||||
value: Value,
|
||||
key: Key | undefined,
|
||||
value: any,
|
||||
key: IDBValidKey | undefined,
|
||||
currentKeyGenerator: number,
|
||||
autoIncrement: boolean,
|
||||
keyPath: KeyPath | null,
|
||||
keyPath: IDBKeyPath | null,
|
||||
): StoreKeyResult {
|
||||
const haveKey = key !== null && key !== undefined;
|
||||
const haveKeyPath = keyPath !== null && keyPath !== undefined;
|
||||
@ -89,7 +89,7 @@ export function makeStoreKeyValue(
|
||||
updatedKeyGenerator = currentKeyGenerator;
|
||||
}
|
||||
return {
|
||||
key: key,
|
||||
key: key!,
|
||||
value: value,
|
||||
updatedKeyGenerator,
|
||||
};
|
||||
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 Jeremy Scheff
|
||||
Copyright 2019 Florian Dold
|
||||
|
||||
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 { BridgeIDBRequest } from "../BridgeIDBRequest";
|
||||
import { BridgeIDBKeyRange } from "../BridgeIDBKeyRange";
|
||||
import { BridgeIDBIndex } from "../BridgeIDBIndex";
|
||||
import { BridgeIDBObjectStore } from "../BridgeIDBObjectStore";
|
||||
import { Event } from "../idbtypes";
|
||||
|
||||
/** @public */
|
||||
export type EventType =
|
||||
| "abort"
|
||||
| "blocked"
|
||||
| "complete"
|
||||
| "error"
|
||||
| "success"
|
||||
| "upgradeneeded"
|
||||
| "versionchange";
|
||||
|
||||
/** @public */
|
||||
export type CursorSource = BridgeIDBIndex | BridgeIDBObjectStore;
|
||||
|
||||
/** @public */
|
||||
export interface FakeDOMStringList extends Array<string> {
|
||||
contains: (value: string) => boolean;
|
||||
item: (i: number) => string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type BridgeIDBCursorDirection =
|
||||
| "next"
|
||||
| "nextunique"
|
||||
| "prev"
|
||||
| "prevunique";
|
||||
|
||||
/** @public */
|
||||
export type KeyPath = string | string[];
|
||||
|
||||
/** @public */
|
||||
export type Key = any;
|
||||
|
||||
/** @public */
|
||||
export type CursorRange = Key | BridgeIDBKeyRange | undefined;
|
||||
|
||||
/** @public */
|
||||
export type Value = any;
|
||||
|
||||
/** @public */
|
||||
export interface Record {
|
||||
key: Key;
|
||||
value: Key | Value; // For indexes, will be Key. For object stores, will be Value.
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type TransactionMode = "readonly" | "readwrite" | "versionchange";
|
||||
|
||||
/** @public */
|
||||
export interface BridgeIDBDatabaseInfo {
|
||||
name: string;
|
||||
version: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface RequestObj {
|
||||
operation: () => Promise<any>;
|
||||
request?: BridgeIDBRequest | undefined;
|
||||
source?: any;
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -14,11 +14,14 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
import { IDBValidKey } from "..";
|
||||
import { DataError } from "./errors";
|
||||
import { Key } from "./types";
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#convert-a-value-to-a-input
|
||||
function valueToKey(input: any, seen?: Set<object>): Key | Key[] {
|
||||
function valueToKey(
|
||||
input: any,
|
||||
seen?: Set<object>,
|
||||
): IDBValidKey | IDBValidKey[] {
|
||||
if (typeof input === "number") {
|
||||
if (isNaN(input)) {
|
||||
throw new DataError();
|
||||
|
@ -16,6 +16,7 @@
|
||||
"rootDir": "./src",
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"typeRoots": ["./node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
|
@ -3,10 +3,9 @@ importers:
|
||||
specifiers: {}
|
||||
packages/idb-bridge:
|
||||
dependencies:
|
||||
'@types/node': 14.14.22
|
||||
tslib: 2.1.0
|
||||
devDependencies:
|
||||
'@microsoft/api-extractor': 7.13.0
|
||||
'@types/node': 14.14.22
|
||||
ava: 3.15.0
|
||||
esm: 3.2.25
|
||||
prettier: 2.2.1
|
||||
@ -14,7 +13,6 @@ importers:
|
||||
rollup: 2.37.1
|
||||
typescript: 4.1.3
|
||||
specifiers:
|
||||
'@microsoft/api-extractor': ^7.13.0
|
||||
'@types/node': ^14.14.22
|
||||
ava: ^3.15.0
|
||||
esm: ^3.2.25
|
||||
|
Loading…
Reference in New Issue
Block a user