idb: add first web platform tests, fix issues detected by them
This commit is contained in:
parent
5ff5a686e4
commit
8c92499d85
@ -25,7 +25,9 @@
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
"tslib": "^2.1.0",
|
||||
"typeson": "^5.18.2",
|
||||
"typeson-registry": "^1.0.0-alpha.38"
|
||||
},
|
||||
"ava": {
|
||||
"require": [
|
||||
|
@ -27,7 +27,11 @@ import {
|
||||
StoreLevel,
|
||||
RecordStoreResponse,
|
||||
} from "./backend-interface";
|
||||
import structuredClone from "./util/structuredClone";
|
||||
import {
|
||||
structuredClone,
|
||||
structuredEncapsulate,
|
||||
structuredRevive,
|
||||
} from "./util/structuredClone";
|
||||
import {
|
||||
InvalidStateError,
|
||||
InvalidAccessError,
|
||||
@ -39,7 +43,12 @@ import compareKeys from "./util/cmp";
|
||||
import { StoreKeyResult, makeStoreKeyValue } from "./util/makeStoreKeyValue";
|
||||
import getIndexKeys from "./util/getIndexKeys";
|
||||
import openPromise from "./util/openPromise";
|
||||
import { IDBKeyPath, IDBKeyRange, IDBTransactionMode, IDBValidKey } from "./idbtypes";
|
||||
import {
|
||||
IDBKeyPath,
|
||||
IDBKeyRange,
|
||||
IDBTransactionMode,
|
||||
IDBValidKey,
|
||||
} from "./idbtypes";
|
||||
import { BridgeIDBKeyRange } from "./bridge-idb";
|
||||
|
||||
type Key = IDBValidKey;
|
||||
@ -742,7 +751,7 @@ export class MemoryBackend implements Backend {
|
||||
createObjectStore(
|
||||
btx: DatabaseTransaction,
|
||||
name: string,
|
||||
keyPath: string | string[] | null,
|
||||
keyPath: string[] | null,
|
||||
autoIncrement: boolean,
|
||||
): void {
|
||||
if (this.enableTracing) {
|
||||
@ -776,9 +785,6 @@ 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,
|
||||
@ -791,7 +797,7 @@ export class MemoryBackend implements Backend {
|
||||
btx: DatabaseTransaction,
|
||||
indexName: string,
|
||||
objectStoreName: string,
|
||||
keyPath: IDBKeyPath,
|
||||
keyPath: string[],
|
||||
multiEntry: boolean,
|
||||
unique: boolean,
|
||||
): void {
|
||||
@ -1401,9 +1407,10 @@ export class MemoryBackend implements Backend {
|
||||
schema.objectStores[storeReq.objectStoreName].autoIncrement;
|
||||
const keyPath = schema.objectStores[storeReq.objectStoreName].keyPath;
|
||||
let storeKeyResult: StoreKeyResult;
|
||||
const revivedValue = structuredRevive(storeReq.value);
|
||||
try {
|
||||
storeKeyResult = makeStoreKeyValue(
|
||||
storeReq.value,
|
||||
revivedValue,
|
||||
storeReq.key,
|
||||
keygen,
|
||||
autoIncrement,
|
||||
@ -1413,7 +1420,9 @@ export class MemoryBackend implements Backend {
|
||||
if (e instanceof DataError) {
|
||||
const kp = JSON.stringify(keyPath);
|
||||
const n = storeReq.objectStoreName;
|
||||
const m = `Could not extract key from value, objectStore=${n}, keyPath=${kp}`;
|
||||
const m = `Could not extract key from value, objectStore=${n}, keyPath=${kp}, value=${JSON.stringify(
|
||||
storeReq.value,
|
||||
)}`;
|
||||
if (this.enableTracing) {
|
||||
console.error(e);
|
||||
console.error("value was:", storeReq.value);
|
||||
|
@ -25,14 +25,14 @@ import {
|
||||
|
||||
/** @public */
|
||||
export interface ObjectStoreProperties {
|
||||
keyPath: IDBKeyPath | null;
|
||||
keyPath: string[] | null;
|
||||
autoIncrement: boolean;
|
||||
indexes: { [nameame: string]: IndexProperties };
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface IndexProperties {
|
||||
keyPath: IDBKeyPath;
|
||||
keyPath: string[];
|
||||
multiEntry: boolean;
|
||||
unique: boolean;
|
||||
}
|
||||
@ -199,7 +199,7 @@ export interface Backend {
|
||||
btx: DatabaseTransaction,
|
||||
indexName: string,
|
||||
objectStoreName: string,
|
||||
keyPath: IDBKeyPath,
|
||||
keyPath: string | string[],
|
||||
multiEntry: boolean,
|
||||
unique: boolean,
|
||||
): void;
|
||||
|
@ -25,7 +25,23 @@ import {
|
||||
Schema,
|
||||
StoreLevel,
|
||||
} from "./backend-interface";
|
||||
import { EventListener, IDBCursorDirection, IDBKeyPath, IDBKeyRange, IDBTransactionMode, IDBValidKey } from "./idbtypes";
|
||||
import {
|
||||
DOMException,
|
||||
DOMStringList,
|
||||
EventListener,
|
||||
IDBCursor,
|
||||
IDBCursorDirection,
|
||||
IDBDatabase,
|
||||
IDBIndex,
|
||||
IDBKeyPath,
|
||||
IDBKeyRange,
|
||||
IDBObjectStore,
|
||||
IDBOpenDBRequest,
|
||||
IDBRequest,
|
||||
IDBTransaction,
|
||||
IDBTransactionMode,
|
||||
IDBValidKey,
|
||||
} from "./idbtypes";
|
||||
import compareKeys from "./util/cmp";
|
||||
import enforceRange from "./util/enforceRange";
|
||||
import {
|
||||
@ -42,9 +58,10 @@ import {
|
||||
import { fakeDOMStringList } from "./util/fakeDOMStringList";
|
||||
import FakeEvent from "./util/FakeEvent";
|
||||
import FakeEventTarget from "./util/FakeEventTarget";
|
||||
import { normalizeKeyPath } from "./util/normalizeKeyPath";
|
||||
import openPromise from "./util/openPromise";
|
||||
import queueTask from "./util/queueTask";
|
||||
import structuredClone from "./util/structuredClone";
|
||||
import { structuredClone, structuredEncapsulate, structuredRevive } from "./util/structuredClone";
|
||||
import validateKeyPath from "./util/validateKeyPath";
|
||||
import valueToKey from "./util/valueToKey";
|
||||
|
||||
@ -87,7 +104,7 @@ function simplifyRange(
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class BridgeIDBCursor {
|
||||
export class BridgeIDBCursor implements IDBCursor {
|
||||
_request: BridgeIDBRequest | undefined;
|
||||
|
||||
private _gotValue: boolean = false;
|
||||
@ -127,7 +144,7 @@ export class BridgeIDBCursor {
|
||||
if (this.source instanceof BridgeIDBObjectStore) {
|
||||
return this.source;
|
||||
}
|
||||
return this.source.objectStore;
|
||||
return this.source._objectStore;
|
||||
}
|
||||
|
||||
get _backend(): Backend {
|
||||
@ -149,15 +166,23 @@ export class BridgeIDBCursor {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
get key() {
|
||||
return this._key;
|
||||
get key(): IDBValidKey {
|
||||
const k = this._key;
|
||||
if (k === null || k === undefined) {
|
||||
throw Error("no key");
|
||||
}
|
||||
return k;
|
||||
}
|
||||
set key(val) {
|
||||
/* For babel */
|
||||
}
|
||||
|
||||
get primaryKey() {
|
||||
return this._primaryKey;
|
||||
get primaryKey(): IDBValidKey {
|
||||
const k = this._primaryKey;
|
||||
if (k === 0 || k === undefined) {
|
||||
throw Error("no key");
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
set primaryKey(val) {
|
||||
@ -221,7 +246,7 @@ export class BridgeIDBCursor {
|
||||
this._primaryKey = response.primaryKeys![0];
|
||||
|
||||
if (!this._keyOnly) {
|
||||
this._value = response.values![0];
|
||||
this._value = structuredRevive(response.values![0]);
|
||||
}
|
||||
|
||||
this._gotValue = true;
|
||||
@ -239,7 +264,7 @@ export class BridgeIDBCursor {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
const transaction = this._effectiveObjectStore._transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
@ -266,7 +291,7 @@ export class BridgeIDBCursor {
|
||||
|
||||
const storeReq: RecordStoreRequest = {
|
||||
key: this._primaryKey,
|
||||
value: value,
|
||||
value: structuredEncapsulate(value),
|
||||
objectStoreName: this._objectStoreName,
|
||||
storeLevel: StoreLevel.UpdateExisting,
|
||||
};
|
||||
@ -295,7 +320,7 @@ export class BridgeIDBCursor {
|
||||
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-continue-void-any-key
|
||||
*/
|
||||
public continue(key?: IDBValidKey) {
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
const transaction = this._effectiveObjectStore._transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
@ -357,7 +382,7 @@ export class BridgeIDBCursor {
|
||||
}
|
||||
|
||||
public delete() {
|
||||
const transaction = this._effectiveObjectStore.transaction;
|
||||
const transaction = this._effectiveObjectStore._transaction;
|
||||
|
||||
if (transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
@ -547,7 +572,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
|
||||
transaction._backend.createObjectStore(
|
||||
backendTx,
|
||||
name,
|
||||
keyPath,
|
||||
(keyPath !== null) ? normalizeKeyPath(keyPath) : null,
|
||||
autoIncrement,
|
||||
);
|
||||
|
||||
@ -583,7 +608,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
|
||||
return (
|
||||
transaction._state === "active" &&
|
||||
transaction.mode === "versionchange" &&
|
||||
transaction.db === this
|
||||
transaction._db === this
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -656,7 +681,7 @@ export class BridgeIDBFactory {
|
||||
// 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;
|
||||
request._source = null;
|
||||
|
||||
queueTask(async () => {
|
||||
const databases = await this.backend.getDatabases();
|
||||
@ -709,7 +734,7 @@ export class BridgeIDBFactory {
|
||||
|
||||
// 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) {
|
||||
public open(name: string, version?: number): BridgeIDBOpenDBRequest {
|
||||
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
|
||||
@ -761,9 +786,7 @@ export class BridgeIDBFactory {
|
||||
});
|
||||
event2.eventPath = [request];
|
||||
request.dispatchEvent(event2);
|
||||
}
|
||||
|
||||
if (existingVersion < requestedVersion) {
|
||||
} else 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) {
|
||||
@ -803,7 +826,9 @@ export class BridgeIDBFactory {
|
||||
request.transaction = transaction;
|
||||
request.dispatchEvent(event);
|
||||
|
||||
console.log("awaiting until initial transaction is done");
|
||||
await transaction._waitDone();
|
||||
console.log("initial transaction is done");
|
||||
|
||||
// We don't explicitly exit the versionchange transaction,
|
||||
// since this is already done by the BridgeIDBTransaction.
|
||||
@ -842,47 +867,51 @@ export class BridgeIDBFactory {
|
||||
const confirmActiveTransaction = (
|
||||
index: BridgeIDBIndex,
|
||||
): BridgeIDBTransaction => {
|
||||
if (index._deleted || index.objectStore._deleted) {
|
||||
if (index._deleted || index._objectStore._deleted) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (index.objectStore.transaction._state !== "active") {
|
||||
if (index._objectStore._transaction._state !== "active") {
|
||||
throw new TransactionInactiveError();
|
||||
}
|
||||
|
||||
return index.objectStore.transaction;
|
||||
return index._objectStore._transaction;
|
||||
};
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
|
||||
/** @public */
|
||||
export class BridgeIDBIndex {
|
||||
objectStore: BridgeIDBObjectStore;
|
||||
export class BridgeIDBIndex implements IDBIndex {
|
||||
_objectStore: BridgeIDBObjectStore;
|
||||
|
||||
get _schema(): Schema {
|
||||
return this.objectStore.transaction.db._schema;
|
||||
get objectStore(): IDBObjectStore {
|
||||
return this._objectStore;
|
||||
}
|
||||
|
||||
get keyPath(): IDBKeyPath {
|
||||
return this._schema.objectStores[this.objectStore.name].indexes[this._name]
|
||||
get _schema(): Schema {
|
||||
return this._objectStore._transaction._db._schema;
|
||||
}
|
||||
|
||||
get keyPath(): IDBKeyPath | IDBKeyPath[] {
|
||||
return this._schema.objectStores[this._objectStore.name].indexes[this._name]
|
||||
.keyPath;
|
||||
}
|
||||
|
||||
get multiEntry(): boolean {
|
||||
return this._schema.objectStores[this.objectStore.name].indexes[this._name]
|
||||
return this._schema.objectStores[this._objectStore.name].indexes[this._name]
|
||||
.multiEntry;
|
||||
}
|
||||
|
||||
get unique(): boolean {
|
||||
return this._schema.objectStores[this.objectStore.name].indexes[this._name]
|
||||
return this._schema.objectStores[this._objectStore.name].indexes[this._name]
|
||||
.unique;
|
||||
}
|
||||
|
||||
get _backend(): Backend {
|
||||
return this.objectStore._backend;
|
||||
return this._objectStore._backend;
|
||||
}
|
||||
|
||||
_confirmActiveTransaction(): { btx: DatabaseTransaction } {
|
||||
return this.objectStore._confirmActiveTransaction();
|
||||
return this._objectStore._confirmActiveTransaction();
|
||||
}
|
||||
|
||||
private _name: string;
|
||||
@ -891,7 +920,7 @@ export class BridgeIDBIndex {
|
||||
|
||||
constructor(objectStore: BridgeIDBObjectStore, name: string) {
|
||||
this._name = name;
|
||||
this.objectStore = objectStore;
|
||||
this._objectStore = objectStore;
|
||||
}
|
||||
|
||||
get name() {
|
||||
@ -900,9 +929,9 @@ export class BridgeIDBIndex {
|
||||
|
||||
// https://w3c.github.io/IndexedDB/#dom-idbindex-name
|
||||
set name(name: any) {
|
||||
const transaction = this.objectStore.transaction;
|
||||
const transaction = this._objectStore._transaction;
|
||||
|
||||
if (!transaction.db._runningVersionchangeTransaction) {
|
||||
if (!transaction._db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
@ -919,9 +948,9 @@ export class BridgeIDBIndex {
|
||||
return;
|
||||
}
|
||||
|
||||
this._backend.renameIndex(btx, this.objectStore.name, oldName, newName);
|
||||
this._backend.renameIndex(btx, this._objectStore.name, oldName, newName);
|
||||
|
||||
if (this.objectStore.indexNames.indexOf(name) >= 0) {
|
||||
if (this._objectStore._indexNames.indexOf(name) >= 0) {
|
||||
throw new ConstraintError();
|
||||
}
|
||||
}
|
||||
@ -942,12 +971,12 @@ export class BridgeIDBIndex {
|
||||
}
|
||||
|
||||
const request = new BridgeIDBRequest();
|
||||
request.source = this;
|
||||
request.transaction = this.objectStore.transaction;
|
||||
request._source = this;
|
||||
request.transaction = this._objectStore._transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursorWithValue(
|
||||
this,
|
||||
this.objectStore.name,
|
||||
this._objectStore.name,
|
||||
this._name,
|
||||
range,
|
||||
direction,
|
||||
@ -958,7 +987,7 @@ export class BridgeIDBIndex {
|
||||
return cursor._iterate();
|
||||
};
|
||||
|
||||
return this.objectStore.transaction._execRequestAsync({
|
||||
return this._objectStore._transaction._execRequestAsync({
|
||||
operation,
|
||||
request,
|
||||
source: this,
|
||||
@ -981,12 +1010,12 @@ export class BridgeIDBIndex {
|
||||
}
|
||||
|
||||
const request = new BridgeIDBRequest();
|
||||
request.source = this;
|
||||
request.transaction = this.objectStore.transaction;
|
||||
request._source = this;
|
||||
request.transaction = this._objectStore._transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursor(
|
||||
this,
|
||||
this.objectStore.name,
|
||||
this._objectStore.name,
|
||||
this._name,
|
||||
range,
|
||||
direction,
|
||||
@ -994,7 +1023,7 @@ export class BridgeIDBIndex {
|
||||
true,
|
||||
);
|
||||
|
||||
return this.objectStore.transaction._execRequestAsync({
|
||||
return this._objectStore._transaction._execRequestAsync({
|
||||
operation: cursor._iterate.bind(cursor),
|
||||
request,
|
||||
source: this,
|
||||
@ -1013,7 +1042,7 @@ export class BridgeIDBIndex {
|
||||
indexName: this._name,
|
||||
limit: 1,
|
||||
range: key,
|
||||
objectStoreName: this.objectStore._name,
|
||||
objectStoreName: this._objectStore._name,
|
||||
resultLevel: ResultLevel.Full,
|
||||
};
|
||||
|
||||
@ -1027,17 +1056,20 @@ export class BridgeIDBIndex {
|
||||
if (!values) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
return values[0];
|
||||
return structuredRevive(values[0]);
|
||||
};
|
||||
|
||||
return this.objectStore.transaction._execRequestAsync({
|
||||
return this._objectStore._transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbindex-getall
|
||||
public getAll(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
|
||||
public getAll(
|
||||
query?: BridgeIDBKeyRange | IDBValidKey,
|
||||
count?: number,
|
||||
): IDBRequest<any[]> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
@ -1054,7 +1086,7 @@ export class BridgeIDBIndex {
|
||||
indexName: this._name,
|
||||
limit: 1,
|
||||
range: key,
|
||||
objectStoreName: this.objectStore._name,
|
||||
objectStoreName: this._objectStore._name,
|
||||
resultLevel: ResultLevel.OnlyKeys,
|
||||
};
|
||||
|
||||
@ -1071,14 +1103,17 @@ export class BridgeIDBIndex {
|
||||
return primaryKeys[0];
|
||||
};
|
||||
|
||||
return this.objectStore.transaction._execRequestAsync({
|
||||
return this._objectStore._transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbindex-getallkeys
|
||||
public getAllKeys(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
|
||||
public getAllKeys(
|
||||
query?: BridgeIDBKeyRange | IDBValidKey,
|
||||
count?: number,
|
||||
): IDBRequest<IDBValidKey[]> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
@ -1098,7 +1133,7 @@ export class BridgeIDBIndex {
|
||||
indexName: this._name,
|
||||
limit: 1,
|
||||
range: key,
|
||||
objectStoreName: this.objectStore._name,
|
||||
objectStoreName: this._objectStore._name,
|
||||
resultLevel: ResultLevel.OnlyCount,
|
||||
};
|
||||
|
||||
@ -1108,7 +1143,7 @@ export class BridgeIDBIndex {
|
||||
return result.count;
|
||||
};
|
||||
|
||||
return this.objectStore.transaction._execRequestAsync({
|
||||
return this._objectStore._transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
@ -1231,36 +1266,44 @@ export class BridgeIDBKeyRange {
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
|
||||
/** @public */
|
||||
export class BridgeIDBObjectStore {
|
||||
export class BridgeIDBObjectStore implements IDBObjectStore {
|
||||
_indexesCache: Map<string, BridgeIDBIndex> = new Map();
|
||||
|
||||
transaction: BridgeIDBTransaction;
|
||||
_transaction: BridgeIDBTransaction;
|
||||
|
||||
get transaction(): IDBTransaction {
|
||||
return this._transaction;
|
||||
}
|
||||
|
||||
get autoIncrement(): boolean {
|
||||
return this._schema.objectStores[this._name].autoIncrement;
|
||||
}
|
||||
|
||||
get indexNames(): FakeDOMStringList {
|
||||
get _indexNames(): FakeDOMStringList {
|
||||
return fakeDOMStringList(
|
||||
Object.keys(this._schema.objectStores[this._name].indexes),
|
||||
).sort();
|
||||
).sort()
|
||||
}
|
||||
|
||||
get keyPath(): IDBKeyPath | null {
|
||||
return this._schema.objectStores[this._name].keyPath;
|
||||
get indexNames(): DOMStringList {
|
||||
return this._indexNames as DOMStringList;
|
||||
}
|
||||
|
||||
get keyPath(): IDBKeyPath | IDBKeyPath[] {
|
||||
return this._schema.objectStores[this._name].keyPath!;
|
||||
}
|
||||
|
||||
_name: string;
|
||||
|
||||
get _schema(): Schema {
|
||||
return this.transaction.db._schema;
|
||||
return this._transaction._db._schema;
|
||||
}
|
||||
|
||||
_deleted: boolean = false;
|
||||
|
||||
constructor(transaction: BridgeIDBTransaction, name: string) {
|
||||
this._name = name;
|
||||
this.transaction = transaction;
|
||||
this._transaction = transaction;
|
||||
}
|
||||
|
||||
get name() {
|
||||
@ -1268,15 +1311,15 @@ export class BridgeIDBObjectStore {
|
||||
}
|
||||
|
||||
get _backend(): Backend {
|
||||
return this.transaction.db._backend;
|
||||
return this._transaction._db._backend;
|
||||
}
|
||||
|
||||
get _backendConnection(): DatabaseConnection {
|
||||
return this.transaction.db._backendConnection;
|
||||
return this._transaction._db._backendConnection;
|
||||
}
|
||||
|
||||
_confirmActiveTransaction(): { btx: DatabaseTransaction } {
|
||||
const btx = this.transaction._backendTransaction;
|
||||
const btx = this._transaction._backendTransaction;
|
||||
if (!btx) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
@ -1285,9 +1328,9 @@ export class BridgeIDBObjectStore {
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-name
|
||||
set name(newName: any) {
|
||||
const transaction = this.transaction;
|
||||
const transaction = this._transaction;
|
||||
|
||||
if (!transaction.db._runningVersionchangeTransaction) {
|
||||
if (!transaction._db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
@ -1302,7 +1345,7 @@ export class BridgeIDBObjectStore {
|
||||
}
|
||||
|
||||
this._backend.renameObjectStore(btx, oldName, newName);
|
||||
this.transaction.db._schema = this._backend.getSchema(
|
||||
this._transaction._db._schema = this._backend.getSchema(
|
||||
this._backendConnection,
|
||||
);
|
||||
}
|
||||
@ -1311,7 +1354,7 @@ export class BridgeIDBObjectStore {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log(`TRACE: IDBObjectStore._store`);
|
||||
}
|
||||
if (this.transaction.mode === "readonly") {
|
||||
if (this._transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
const operation = async () => {
|
||||
@ -1319,7 +1362,7 @@ export class BridgeIDBObjectStore {
|
||||
const result = await this._backend.storeRecord(btx, {
|
||||
objectStoreName: this._name,
|
||||
key: key,
|
||||
value: value,
|
||||
value: structuredEncapsulate(value),
|
||||
storeLevel: overwrite
|
||||
? StoreLevel.AllowOverwrite
|
||||
: StoreLevel.NoOverwrite,
|
||||
@ -1327,7 +1370,7 @@ export class BridgeIDBObjectStore {
|
||||
return result.key;
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({ operation, source: this });
|
||||
return this._transaction._execRequestAsync({ operation, source: this });
|
||||
}
|
||||
|
||||
public put(value: any, key?: IDBValidKey) {
|
||||
@ -1349,7 +1392,7 @@ export class BridgeIDBObjectStore {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction.mode === "readonly") {
|
||||
if (this._transaction.mode === "readonly") {
|
||||
throw new ReadOnlyError();
|
||||
}
|
||||
|
||||
@ -1366,7 +1409,7 @@ export class BridgeIDBObjectStore {
|
||||
return this._backend.deleteRecord(btx, this._name, keyRange);
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
return this._transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
@ -1424,31 +1467,39 @@ export class BridgeIDBObjectStore {
|
||||
if (!values) {
|
||||
throw Error("invariant violated");
|
||||
}
|
||||
return values[0];
|
||||
return structuredRevive(values[0]);
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
return this._transaction._execRequestAsync({
|
||||
operation,
|
||||
source: this,
|
||||
});
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getall
|
||||
public getAll(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
|
||||
public getAll(
|
||||
query?: BridgeIDBKeyRange | IDBValidKey,
|
||||
count?: number,
|
||||
): IDBRequest<any[]> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
|
||||
public getKey(key?: BridgeIDBKeyRange | IDBValidKey) {
|
||||
public getKey(
|
||||
key?: BridgeIDBKeyRange | IDBValidKey,
|
||||
): IDBRequest<IDBValidKey | undefined> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
|
||||
public getAllKeys(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
|
||||
public getAllKeys(
|
||||
query?: BridgeIDBKeyRange | IDBValidKey,
|
||||
count?: number,
|
||||
): IDBRequest<any[]> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
public clear() {
|
||||
public clear(): IDBRequest<undefined> {
|
||||
throw Error("not implemented");
|
||||
}
|
||||
|
||||
@ -1464,8 +1515,8 @@ export class BridgeIDBObjectStore {
|
||||
}
|
||||
|
||||
const request = new BridgeIDBRequest();
|
||||
request.source = this;
|
||||
request.transaction = this.transaction;
|
||||
request._source = this;
|
||||
request.transaction = this._transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursorWithValue(
|
||||
this,
|
||||
@ -1476,7 +1527,7 @@ export class BridgeIDBObjectStore {
|
||||
request,
|
||||
);
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
return this._transaction._execRequestAsync({
|
||||
operation: () => cursor._iterate(),
|
||||
request,
|
||||
source: this,
|
||||
@ -1499,8 +1550,8 @@ export class BridgeIDBObjectStore {
|
||||
}
|
||||
|
||||
const request = new BridgeIDBRequest();
|
||||
request.source = this;
|
||||
request.transaction = this.transaction;
|
||||
request._source = this;
|
||||
request.transaction = this._transaction;
|
||||
|
||||
const cursor = new BridgeIDBCursor(
|
||||
this,
|
||||
@ -1512,7 +1563,7 @@ export class BridgeIDBObjectStore {
|
||||
true,
|
||||
);
|
||||
|
||||
return this.transaction._execRequestAsync({
|
||||
return this._transaction._execRequestAsync({
|
||||
operation: cursor._iterate.bind(cursor),
|
||||
request,
|
||||
source: this,
|
||||
@ -1530,7 +1581,7 @@ export class BridgeIDBObjectStore {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (!this.transaction.db._runningVersionchangeTransaction) {
|
||||
if (!this._transaction._db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
@ -1545,11 +1596,11 @@ export class BridgeIDBObjectStore {
|
||||
? optionalParameters.unique
|
||||
: false;
|
||||
|
||||
if (this.transaction.mode !== "versionchange") {
|
||||
if (this._transaction.mode !== "versionchange") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (this.indexNames.indexOf(indexName) >= 0) {
|
||||
if (this._indexNames.indexOf(indexName) >= 0) {
|
||||
throw new ConstraintError();
|
||||
}
|
||||
|
||||
@ -1563,7 +1614,7 @@ export class BridgeIDBObjectStore {
|
||||
btx,
|
||||
indexName,
|
||||
this._name,
|
||||
keyPath,
|
||||
normalizeKeyPath(keyPath),
|
||||
multiEntry,
|
||||
unique,
|
||||
);
|
||||
@ -1577,7 +1628,7 @@ export class BridgeIDBObjectStore {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction._state === "finished") {
|
||||
if (this._transaction._state === "finished") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
@ -1594,11 +1645,11 @@ export class BridgeIDBObjectStore {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
if (this.transaction.mode !== "versionchange") {
|
||||
if (this._transaction.mode !== "versionchange") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
if (!this.transaction.db._runningVersionchangeTransaction) {
|
||||
if (!this._transaction._db._runningVersionchangeTransaction) {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
|
||||
@ -1638,7 +1689,7 @@ export class BridgeIDBObjectStore {
|
||||
return result.count;
|
||||
};
|
||||
|
||||
return this.transaction._execRequestAsync({ operation, source: this });
|
||||
return this._transaction._execRequestAsync({ operation, source: this });
|
||||
}
|
||||
|
||||
public toString() {
|
||||
@ -1647,10 +1698,20 @@ export class BridgeIDBObjectStore {
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export class BridgeIDBRequest extends FakeEventTarget {
|
||||
export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
|
||||
_result: any = null;
|
||||
_error: Error | null | undefined = null;
|
||||
source: BridgeIDBCursor | BridgeIDBIndex | BridgeIDBObjectStore | null = null;
|
||||
get source(): IDBObjectStore | IDBIndex | IDBCursor {
|
||||
if (this._source) {
|
||||
return this._source;
|
||||
}
|
||||
throw Error("source is null");
|
||||
}
|
||||
_source:
|
||||
| BridgeIDBCursor
|
||||
| BridgeIDBIndex
|
||||
| BridgeIDBObjectStore
|
||||
| null = null;
|
||||
transaction: BridgeIDBTransaction | null = null;
|
||||
readyState: "done" | "pending" = "pending";
|
||||
onsuccess: EventListener | null = null;
|
||||
@ -1708,14 +1769,16 @@ export class BridgeIDBRequest extends FakeEventTarget {
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export class BridgeIDBOpenDBRequest extends BridgeIDBRequest {
|
||||
export class BridgeIDBOpenDBRequest
|
||||
extends BridgeIDBRequest
|
||||
implements IDBOpenDBRequest {
|
||||
public onupgradeneeded: EventListener | null = null;
|
||||
public onblocked: EventListener | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// https://www.w3.org/TR/IndexedDB/#open-requests
|
||||
this.source = null;
|
||||
this._source = null;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
@ -1725,17 +1788,32 @@ export class BridgeIDBOpenDBRequest extends BridgeIDBRequest {
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
|
||||
/** @public */
|
||||
export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
export class BridgeIDBTransaction
|
||||
extends FakeEventTarget
|
||||
implements IDBTransaction {
|
||||
public _state: "active" | "inactive" | "committing" | "finished" = "active";
|
||||
public _started = false;
|
||||
public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
|
||||
|
||||
public _backendTransaction?: DatabaseTransaction;
|
||||
|
||||
public objectStoreNames: FakeDOMStringList;
|
||||
public _objectStoreNames: FakeDOMStringList;
|
||||
get objectStoreNames(): DOMStringList {
|
||||
return this._objectStoreNames as DOMStringList;
|
||||
}
|
||||
public mode: IDBTransactionMode;
|
||||
public db: BridgeIDBDatabase;
|
||||
public error: Error | null = null;
|
||||
public _db: BridgeIDBDatabase;
|
||||
|
||||
get db(): IDBDatabase {
|
||||
return this.db;
|
||||
}
|
||||
|
||||
public _error: Error | null = null;
|
||||
|
||||
get error(): DOMException {
|
||||
return this._error as DOMException;
|
||||
}
|
||||
|
||||
public onabort: EventListener | null = null;
|
||||
public oncomplete: EventListener | null = null;
|
||||
public onerror: EventListener | null = null;
|
||||
@ -1750,7 +1828,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
}> = [];
|
||||
|
||||
get _backend(): Backend {
|
||||
return this.db._backend;
|
||||
return this._db._backend;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@ -1768,10 +1846,10 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
this._scope = new Set(storeNames);
|
||||
this._backendTransaction = backendTransaction;
|
||||
this.mode = mode;
|
||||
this.db = db;
|
||||
this.objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
|
||||
this._db = db;
|
||||
this._objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
|
||||
|
||||
this.db._transactions.push(this);
|
||||
this._db._transactions.push(this);
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
|
||||
@ -1781,14 +1859,14 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
if (errName !== null) {
|
||||
const e = new Error();
|
||||
e.name = errName;
|
||||
this.error = e;
|
||||
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) {
|
||||
if (request._source) {
|
||||
request.result = undefined;
|
||||
request.error = new AbortError();
|
||||
|
||||
@ -1796,7 +1874,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
event.eventPath = [this.db, this];
|
||||
event.eventPath = [this._db, this];
|
||||
request.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
@ -1813,7 +1891,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
});
|
||||
event.eventPath = [this.db];
|
||||
event.eventPath = [this._db];
|
||||
this.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
@ -1828,7 +1906,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
}
|
||||
|
||||
// http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
|
||||
public objectStore(name: string) {
|
||||
public objectStore(name: string): BridgeIDBObjectStore {
|
||||
if (this._state !== "active") {
|
||||
throw new InvalidStateError();
|
||||
}
|
||||
@ -1858,7 +1936,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
request = new BridgeIDBRequest();
|
||||
} else {
|
||||
request = new BridgeIDBRequest();
|
||||
request.source = source;
|
||||
request._source = source;
|
||||
request.transaction = (source as any).transaction;
|
||||
}
|
||||
}
|
||||
@ -1884,7 +1962,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
|
||||
if (!this._backendTransaction) {
|
||||
this._backendTransaction = await this._backend.beginTransaction(
|
||||
this.db._backendConnection,
|
||||
this._db._backendConnection,
|
||||
Array.from(this._scope),
|
||||
this.mode,
|
||||
);
|
||||
@ -1905,7 +1983,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
}
|
||||
|
||||
if (request && operation) {
|
||||
if (!request.source) {
|
||||
if (!request._source) {
|
||||
// Special requests like indexes that just need to run some code, with error handling already built into
|
||||
// operation
|
||||
await operation();
|
||||
@ -1933,7 +2011,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
});
|
||||
|
||||
try {
|
||||
event.eventPath = [request, this, this.db];
|
||||
event.eventPath = [request, this, this._db];
|
||||
request.dispatchEvent(event);
|
||||
} catch (err) {
|
||||
if (this._state !== "committing") {
|
||||
@ -1959,7 +2037,7 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
});
|
||||
|
||||
try {
|
||||
event.eventPath = [this.db, this];
|
||||
event.eventPath = [this._db, this];
|
||||
request.dispatchEvent(event);
|
||||
} catch (err) {
|
||||
if (this._state !== "committing") {
|
||||
@ -1994,20 +2072,20 @@ export class BridgeIDBTransaction extends FakeEventTarget {
|
||||
|
||||
this._state = "finished";
|
||||
|
||||
if (!this.error) {
|
||||
if (!this._error) {
|
||||
if (BridgeIDBFactory.enableTracing) {
|
||||
console.log("dispatching 'complete' event on transaction");
|
||||
}
|
||||
const event = new FakeEvent("complete");
|
||||
event.eventPath = [this, this.db];
|
||||
event.eventPath = [this, this._db];
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
const idx = this.db._transactions.indexOf(this);
|
||||
const idx = this._db._transactions.indexOf(this);
|
||||
if (idx < 0) {
|
||||
throw Error("invariant failed");
|
||||
}
|
||||
this.db._transactions.splice(idx, 1);
|
||||
this._db._transactions.splice(idx, 1);
|
||||
|
||||
this._resolveWait();
|
||||
}
|
||||
|
3
packages/idb-bridge/src/idb-wpt-ported/README
Normal file
3
packages/idb-bridge/src/idb-wpt-ported/README
Normal file
@ -0,0 +1,3 @@
|
||||
This directory contains test cases from the W3C Web Platform Tests suite for IndexedDB.
|
||||
|
||||
The original code for these tests can be found here: https://github.com/web-platform-tests/wpt/tree/master/IndexedDB
|
191
packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts
Normal file
191
packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import test from "ava";
|
||||
import { assert_key_equals, createdb } from "./wptsupport";
|
||||
|
||||
test("WPT test keypath.htm", async (t) => {
|
||||
function keypath(
|
||||
keypath: any,
|
||||
objects: any[],
|
||||
expected_keys: any[],
|
||||
desc?: string,
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
console.log("key path", keypath);
|
||||
console.log("checking", desc);
|
||||
let db: any;
|
||||
const store_name = "store-" + Date.now() + Math.random();
|
||||
|
||||
var open_rq = createdb(t);
|
||||
open_rq.onupgradeneeded = function (e) {
|
||||
db = (e.target as any).result;
|
||||
var objStore = db.createObjectStore(store_name, { keyPath: keypath });
|
||||
|
||||
for (var i = 0; i < objects.length; i++) objStore.add(objects[i]);
|
||||
};
|
||||
|
||||
open_rq.onsuccess = function (e) {
|
||||
var actual_keys: any[] = [],
|
||||
rq = db.transaction(store_name).objectStore(store_name).openCursor();
|
||||
|
||||
rq.onsuccess = (e: any) => {
|
||||
var cursor = e.target.result;
|
||||
|
||||
if (cursor) {
|
||||
actual_keys.push(cursor.key.valueOf());
|
||||
cursor.continue();
|
||||
} else {
|
||||
assert_key_equals(actual_keys, expected_keys, "keyorder array");
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
await keypath("my.key", [{ my: { key: 10 } }], [10]);
|
||||
|
||||
await keypath("my.køi", [{ my: { køi: 5 } }], [5]);
|
||||
|
||||
await keypath("my.key_ya", [{ my: { key_ya: 10 } }], [10]);
|
||||
|
||||
await keypath("public.key$ya", [{ public: { key$ya: 10 } }], [10]);
|
||||
|
||||
await keypath("true.$", [{ true: { $: 10 } }], [10]);
|
||||
|
||||
await keypath("my._", [{ my: { _: 10 } }], [10]);
|
||||
|
||||
await keypath("delete.a7", [{ delete: { a7: 10 } }], [10]);
|
||||
|
||||
await keypath(
|
||||
"p.p.p.p.p.p.p.p.p.p.p.p.p.p",
|
||||
[
|
||||
{
|
||||
p: {
|
||||
p: {
|
||||
p: {
|
||||
p: {
|
||||
p: {
|
||||
p: { p: { p: { p: { p: { p: { p: { p: { p: 10 } } } } } } } },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
[10],
|
||||
);
|
||||
|
||||
await keypath(
|
||||
"str.length",
|
||||
[{ str: "pony" }, { str: "my" }, { str: "little" }, { str: "" }],
|
||||
[0, 2, 4, 6],
|
||||
);
|
||||
|
||||
await keypath(
|
||||
"arr.length",
|
||||
[
|
||||
{ arr: [0, 0, 0, 0] },
|
||||
{ arr: [{}, 0, "hei", "length", Infinity, []] },
|
||||
{ arr: [10, 10] },
|
||||
{ arr: [] },
|
||||
],
|
||||
[0, 2, 4, 6],
|
||||
);
|
||||
|
||||
await keypath("length", [[10, 10], "123", { length: 20 }], [2, 3, 20]);
|
||||
|
||||
await keypath(
|
||||
"",
|
||||
[["bags"], "bean", 10],
|
||||
[10, "bean", ["bags"]],
|
||||
"'' uses value as key",
|
||||
);
|
||||
|
||||
await keypath(
|
||||
[""],
|
||||
[["bags"], "bean", 10],
|
||||
[[10], ["bean"], [["bags"]]],
|
||||
"[''] uses value as [key]",
|
||||
);
|
||||
|
||||
await keypath(
|
||||
["x", "y"],
|
||||
[
|
||||
{ x: 10, y: 20 },
|
||||
{ y: 1.337, x: 100 },
|
||||
],
|
||||
[
|
||||
[10, 20],
|
||||
[100, 1.337],
|
||||
],
|
||||
"['x', 'y']",
|
||||
);
|
||||
|
||||
await keypath(
|
||||
[["x"], ["y"]],
|
||||
[
|
||||
{ x: 10, y: 20 },
|
||||
{ y: 1.337, x: 100 },
|
||||
],
|
||||
[
|
||||
[10, 20],
|
||||
[100, 1.337],
|
||||
],
|
||||
"[['x'], 'y'] (stringifies)",
|
||||
);
|
||||
|
||||
await keypath(
|
||||
[
|
||||
"x",
|
||||
{
|
||||
toString: function () {
|
||||
return "y";
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{ x: 10, y: 20 },
|
||||
{ y: 1.337, x: 100 },
|
||||
],
|
||||
[
|
||||
[10, 20],
|
||||
[100, 1.337],
|
||||
],
|
||||
"['x', {toString->'y'}] (stringifies)",
|
||||
);
|
||||
|
||||
await keypath(
|
||||
["name", "type"],
|
||||
[
|
||||
{ name: "orange", type: "fruit" },
|
||||
{ name: "orange", type: ["telecom", "french"] },
|
||||
],
|
||||
[
|
||||
["orange", "fruit"],
|
||||
["orange", ["telecom", "french"]],
|
||||
],
|
||||
);
|
||||
|
||||
await keypath(
|
||||
["name", "type.name"],
|
||||
[
|
||||
{ name: "orange", type: { name: "fruit" } },
|
||||
{ name: "orange", type: { name: "telecom" } },
|
||||
],
|
||||
[
|
||||
["orange", "fruit"],
|
||||
["orange", "telecom"],
|
||||
],
|
||||
);
|
||||
|
||||
const loop_array: any[] = [];
|
||||
loop_array.push(loop_array);
|
||||
await keypath(
|
||||
loop_array,
|
||||
["a", 1, ["k"]],
|
||||
[[1], ["a"], [["k"]]],
|
||||
"array loop -> stringify becomes ['']",
|
||||
);
|
||||
|
||||
t.pass();
|
||||
});
|
46
packages/idb-bridge/src/idb-wpt-ported/value.test.ts
Normal file
46
packages/idb-bridge/src/idb-wpt-ported/value.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import test from "ava";
|
||||
import { IDBVersionChangeEvent } from "../idbtypes";
|
||||
import { createdb } from "./wptsupport";
|
||||
|
||||
test.cb("WPT test value.htm, array", (t) => {
|
||||
const value = new Array();
|
||||
const _instanceof = Array;
|
||||
|
||||
t.plan(1);
|
||||
|
||||
createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) {
|
||||
(e.target as any).result.createObjectStore("store").add(value, 1);
|
||||
(e.target as any).onsuccess = (e: any) => {
|
||||
console.log("in first onsuccess");
|
||||
e.target.result
|
||||
.transaction("store")
|
||||
.objectStore("store")
|
||||
.get(1).onsuccess = (e: any) => {
|
||||
t.assert(e.target.result instanceof _instanceof, "instanceof");
|
||||
t.end();
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
test.cb("WPT test value.htm, date", (t) => {
|
||||
const value = new Date();
|
||||
const _instanceof = Date;
|
||||
|
||||
t.plan(1);
|
||||
|
||||
createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) {
|
||||
(e.target as any).result.createObjectStore("store").add(value, 1);
|
||||
(e.target as any).onsuccess = (e: any) => {
|
||||
console.log("in first onsuccess");
|
||||
e.target.result
|
||||
.transaction("store")
|
||||
.objectStore("store")
|
||||
.get(1).onsuccess = (e: any) => {
|
||||
t.assert(e.target.result instanceof _instanceof, "instanceof");
|
||||
t.end();
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
|
30
packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
Normal file
30
packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { ExecutionContext } from "ava";
|
||||
import { BridgeIDBFactory } from "..";
|
||||
import { IDBOpenDBRequest } from "../idbtypes";
|
||||
import MemoryBackend from "../MemoryBackend";
|
||||
import compareKeys from "../util/cmp";
|
||||
|
||||
BridgeIDBFactory.enableTracing = true;
|
||||
const idbFactory = new BridgeIDBFactory(new MemoryBackend());
|
||||
|
||||
const self = {
|
||||
indexedDB: idbFactory,
|
||||
};
|
||||
|
||||
export function createdb(
|
||||
t: ExecutionContext<unknown>,
|
||||
dbname?: string,
|
||||
version?: number,
|
||||
): IDBOpenDBRequest {
|
||||
var rq_open: IDBOpenDBRequest;
|
||||
dbname = dbname ? dbname : "testdb-" + new Date().getTime() + Math.random();
|
||||
if (version) rq_open = self.indexedDB.open(dbname, version);
|
||||
else rq_open = self.indexedDB.open(dbname);
|
||||
return rq_open;
|
||||
}
|
||||
|
||||
export function assert_key_equals(actual: any, expected: any, description?: string) {
|
||||
if (0 != compareKeys(actual, expected)) {
|
||||
throw Error("expected keys to be the same");
|
||||
}
|
||||
}
|
@ -16,10 +16,10 @@
|
||||
*/
|
||||
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
import structuredClone from "./structuredClone";
|
||||
import { structuredClone } from "./structuredClone";
|
||||
|
||||
export function injectKey(
|
||||
keyPath: IDBKeyPath,
|
||||
keyPath: IDBKeyPath | IDBKeyPath[],
|
||||
value: any,
|
||||
key: IDBValidKey,
|
||||
): any {
|
||||
|
@ -17,7 +17,7 @@
|
||||
import extractKey from "./extractKey";
|
||||
import { DataError } from "./errors";
|
||||
import valueToKey from "./valueToKey";
|
||||
import structuredClone from "./structuredClone";
|
||||
import { structuredClone } from "./structuredClone";
|
||||
import injectKey from "./injectKey";
|
||||
import { IDBKeyPath, IDBValidKey } from "../idbtypes";
|
||||
|
||||
@ -32,7 +32,7 @@ export function makeStoreKeyValue(
|
||||
key: IDBValidKey | undefined,
|
||||
currentKeyGenerator: number,
|
||||
autoIncrement: boolean,
|
||||
keyPath: IDBKeyPath | null,
|
||||
keyPath: IDBKeyPath | IDBKeyPath[] | null,
|
||||
): StoreKeyResult {
|
||||
const haveKey = key !== null && key !== undefined;
|
||||
const haveKeyPath = keyPath !== null && keyPath !== undefined;
|
||||
@ -63,7 +63,11 @@ export function makeStoreKeyValue(
|
||||
};
|
||||
} else {
|
||||
// (yes, no, no)
|
||||
throw new DataError();
|
||||
return {
|
||||
key: key!,
|
||||
value: value,
|
||||
updatedKeyGenerator: currentKeyGenerator,
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
41
packages/idb-bridge/src/util/normalizeKeyPath.ts
Normal file
41
packages/idb-bridge/src/util/normalizeKeyPath.ts
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
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 { IDBKeyPath } from "../idbtypes";
|
||||
|
||||
export function normalizeKeyPath(
|
||||
keyPath: IDBKeyPath | IDBKeyPath[],
|
||||
): string | string[] {
|
||||
if (Array.isArray(keyPath)) {
|
||||
const path: string[] = [];
|
||||
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
|
||||
// comment in validateKeyPath)
|
||||
if (
|
||||
item !== undefined &&
|
||||
item !== null &&
|
||||
typeof item !== "string" &&
|
||||
(item as any).toString
|
||||
) {
|
||||
item = (item as any).toString();
|
||||
}
|
||||
path.push(item);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return keyPath;
|
||||
}
|
@ -14,17 +14,24 @@
|
||||
permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
function structuredCloneImpl(val: any, visited: WeakMap<any, boolean>): any {
|
||||
// FIXME: replace with real implementation!
|
||||
return JSON.parse(JSON.stringify(val));
|
||||
// @ts-ignore
|
||||
import Typeson from "typeson";
|
||||
// @ts-ignore
|
||||
import structuredCloningThrowing from "typeson-registry/dist/presets/structured-cloning-throwing";
|
||||
|
||||
const TSON = new Typeson().register(structuredCloningThrowing);
|
||||
|
||||
export function structuredEncapsulate(val: any): any {
|
||||
return TSON.encapsulate(val);
|
||||
}
|
||||
|
||||
export function structuredRevive(val: any): any {
|
||||
return TSON.revive(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Structured clone for IndexedDB.
|
||||
*/
|
||||
export function structuredClone(val: any): any {
|
||||
const visited: WeakMap<any, boolean> = new WeakMap<any, boolean>();
|
||||
return structuredCloneImpl(val, visited);
|
||||
return structuredRevive(structuredEncapsulate(val));
|
||||
}
|
||||
|
||||
export default structuredClone;
|
||||
|
@ -106,7 +106,7 @@ function transactionToPromise(tx: IDBTransaction): Promise<void> {
|
||||
};
|
||||
tx.onerror = () => {
|
||||
console.error("Transaction failed:", stack);
|
||||
reject(tx.error);
|
||||
reject(tx._error);
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -394,8 +394,8 @@ function runWithTransaction<T, StoreTypes extends Store<string, {}>>(
|
||||
logger.error(`${stack}`);
|
||||
};
|
||||
tx.onabort = () => {
|
||||
if (tx.error) {
|
||||
logger.error("Transaction aborted with error:", tx.error);
|
||||
if (tx._error) {
|
||||
logger.error("Transaction aborted with error:", tx._error);
|
||||
} else {
|
||||
logger.error("Trasaction aborted (no error)");
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ importers:
|
||||
packages/idb-bridge:
|
||||
dependencies:
|
||||
tslib: 2.1.0
|
||||
typeson: 5.18.2
|
||||
typeson-registry: 1.0.0-alpha.38
|
||||
devDependencies:
|
||||
'@types/node': 14.14.22
|
||||
ava: 3.15.0
|
||||
@ -21,6 +23,8 @@ importers:
|
||||
rollup: ^2.37.1
|
||||
tslib: ^2.1.0
|
||||
typescript: ^4.1.3
|
||||
typeson: ^5.18.2
|
||||
typeson-registry: ^1.0.0-alpha.38
|
||||
packages/pogen:
|
||||
dependencies:
|
||||
'@types/node': 14.14.22
|
||||
@ -1193,6 +1197,14 @@ packages:
|
||||
/balanced-match/1.0.0:
|
||||
resolution:
|
||||
integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
/base64-arraybuffer-es6/0.6.0:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.10.0'
|
||||
peerDependencies:
|
||||
core-js-bundle: ^3.6.5
|
||||
resolution:
|
||||
integrity: sha512-57nLqKj4ShsDwFJWJsM4sZx6u60WbCge35rWRSevUwqxDtRwwxiKAO800zD2upPv4CfdWjQp//wSLar35nDKvA==
|
||||
/base64-js/1.5.1:
|
||||
dev: true
|
||||
resolution:
|
||||
@ -3283,6 +3295,9 @@ packages:
|
||||
dev: true
|
||||
resolution:
|
||||
integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
/lodash.sortby/4.7.0:
|
||||
resolution:
|
||||
integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||
/lodash/4.17.20:
|
||||
dev: true
|
||||
resolution:
|
||||
@ -4005,7 +4020,6 @@ packages:
|
||||
resolution:
|
||||
integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
/punycode/2.1.1:
|
||||
dev: true
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
@ -4764,6 +4778,13 @@ packages:
|
||||
node: '>=8.0'
|
||||
resolution:
|
||||
integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
/tr46/2.0.2:
|
||||
dependencies:
|
||||
punycode: 2.1.1
|
||||
engines:
|
||||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==
|
||||
/trim-off-newlines/1.0.1:
|
||||
dev: true
|
||||
engines:
|
||||
@ -4871,6 +4892,25 @@ packages:
|
||||
hasBin: true
|
||||
resolution:
|
||||
integrity: sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
|
||||
/typeson-registry/1.0.0-alpha.38:
|
||||
dependencies:
|
||||
base64-arraybuffer-es6: 0.6.0
|
||||
typeson: 5.18.2
|
||||
whatwg-url: 8.4.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=10.0.0'
|
||||
resolution:
|
||||
integrity: sha512-6lt2IhbNT9hyow5hljZqjWtVDXBIaC1X8bBGlBva0Pod2f42g23bVqww09ruquwSC48I8BSSCPi+B2dFHM5ihQ==
|
||||
/typeson/5.18.2:
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=0.1.14'
|
||||
peerDependencies:
|
||||
core-js-bundle: ^3.6.4
|
||||
regenerator-runtime: ^0.13.3
|
||||
resolution:
|
||||
integrity: sha512-Vetd+OGX05P4qHyHiSLdHZ5Z5GuQDrHHwSdjkqho9NSCYVSLSfRMjklD/unpHH8tXBR9Z/R05rwJSuMpMFrdsw==
|
||||
/uglify-js/3.12.5:
|
||||
dev: true
|
||||
engines:
|
||||
@ -4974,12 +5014,27 @@ packages:
|
||||
dev: true
|
||||
resolution:
|
||||
integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
|
||||
/webidl-conversions/6.1.0:
|
||||
engines:
|
||||
node: '>=10.4'
|
||||
resolution:
|
||||
integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||
/well-known-symbols/2.0.0:
|
||||
dev: true
|
||||
engines:
|
||||
node: '>=6'
|
||||
resolution:
|
||||
integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==
|
||||
/whatwg-url/8.4.0:
|
||||
dependencies:
|
||||
lodash.sortby: 4.7.0
|
||||
tr46: 2.0.2
|
||||
webidl-conversions: 6.1.0
|
||||
dev: false
|
||||
engines:
|
||||
node: '>=10'
|
||||
resolution:
|
||||
integrity: sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==
|
||||
/which-module/2.0.0:
|
||||
dev: true
|
||||
resolution:
|
||||
|
Loading…
Reference in New Issue
Block a user