idb-bridge: remove cyclic dependencies, rip out api extractor

This commit is contained in:
Florian Dold 2021-02-08 15:23:44 +01:00
parent 883637d3f2
commit 4452984a24
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
33 changed files with 2198 additions and 2638 deletions

View File

@ -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"
}
}
}
}

View File

@ -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"
]
}
}
}

View File

@ -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]";
}
}

View File

@ -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]";
}
}

View File

@ -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]";
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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]";
}
}

View File

@ -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]";
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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]";
}
}

View File

@ -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) => {

View File

@ -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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -15,8 +15,7 @@
*/
import { InvalidStateError } from "./errors";
import FakeEvent from "./FakeEvent";
import { EventType } from "./types";
import FakeEvent, { EventType } from "./FakeEvent";
import {
EventTarget,
Event,

View File

@ -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(

View File

@ -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") {

View File

@ -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

View File

@ -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;

View File

@ -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}`);
}
}

View File

@ -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(

View File

@ -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,
};

View File

@ -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

View File

@ -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();

View File

@ -16,6 +16,7 @@
"rootDir": "./src",
"esModuleInterop": true,
"importHelpers": true,
"isolatedModules": true,
"typeRoots": ["./node_modules/@types"]
},
"include": ["src/**/*"]

View File

@ -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