idb: implement missing methods

This commit is contained in:
Florian Dold 2021-02-23 20:16:10 +01:00
parent 29d23b192d
commit 9c85f6277b
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 387 additions and 11 deletions

View File

@ -860,6 +860,45 @@ export class MemoryBackend implements Backend {
}); });
} }
async clearObjectStore(
btx: DatabaseTransaction,
objectStoreName: string,
): Promise<void> {
const myConn = this.requireConnectionFromTransaction(btx);
const db = this.databases[myConn.dbName];
if (!db) {
throw Error("db not found");
}
if (db.txLevel < TransactionLevel.Write) {
throw Error("only allowed in write transaction");
}
if (
db.txRestrictObjectStores &&
!db.txRestrictObjectStores.includes(objectStoreName)
) {
throw Error(
`Not allowed to access store '${objectStoreName}', transaction is over ${JSON.stringify(
db.txRestrictObjectStores,
)}`,
);
}
const schema = myConn.modifiedSchema;
const objectStoreMapEntry = myConn.objectStoreMap[objectStoreName];
objectStoreMapEntry.store.modifiedData = new BTree([], compareKeys);
for (const indexName of Object.keys(
schema.objectStores[objectStoreName].indexes,
)) {
const index = myConn.objectStoreMap[objectStoreName].indexMap[indexName];
if (!index) {
throw Error("index referenced by object store does not exist");
}
index.modifiedData = new BTree([], compareKeys);
}
}
async deleteRecord( async deleteRecord(
btx: DatabaseTransaction, btx: DatabaseTransaction,
objectStoreName: string, objectStoreName: string,

View File

@ -216,4 +216,9 @@ export interface Backend {
btx: DatabaseTransaction, btx: DatabaseTransaction,
storeReq: RecordStoreRequest, storeReq: RecordStoreRequest,
): Promise<RecordStoreResponse>; ): Promise<RecordStoreResponse>;
clearObjectStore(
btx: DatabaseTransaction,
objectStoreName: string,
): Promise<void>
} }

View File

@ -1234,11 +1234,48 @@ export class BridgeIDBIndex implements IDBIndex {
query?: BridgeIDBKeyRange | IDBValidKey, query?: BridgeIDBKeyRange | IDBValidKey,
count?: number, count?: number,
): IDBRequest<any[]> { ): IDBRequest<any[]> {
throw Error("not implemented"); this._confirmIndexExists();
this._confirmActiveTransaction();
if (this._deleted) {
throw new InvalidStateError();
}
if (!(query instanceof BridgeIDBKeyRange)) {
query = BridgeIDBKeyRange._valueToKeyRange(query);
}
if (count === undefined) {
count = -1;
}
const getReq: RecordGetRequest = {
direction: "next",
indexName: this._name,
limit: count,
range: query,
objectStoreName: this._objectStore._name,
resultLevel: ResultLevel.Full,
};
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
const result = await this._backend.getRecords(btx, getReq);
const values = result.values;
if (!values) {
throw Error("invariant violated");
}
return values.map((x) => structuredRevive(x));
};
return this._objectStore._transaction._execRequestAsync({
operation,
source: this,
});
} }
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-getKey-IDBRequest-any-key // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-getKey-IDBRequest-any-key
public getKey(key: BridgeIDBKeyRange | IDBValidKey) { public getKey(key: BridgeIDBKeyRange | IDBValidKey) {
this._confirmIndexExists();
this._confirmActiveTransaction(); this._confirmActiveTransaction();
if (!(key instanceof BridgeIDBKeyRange)) { if (!(key instanceof BridgeIDBKeyRange)) {
@ -1278,11 +1315,45 @@ export class BridgeIDBIndex implements IDBIndex {
query?: BridgeIDBKeyRange | IDBValidKey, query?: BridgeIDBKeyRange | IDBValidKey,
count?: number, count?: number,
): IDBRequest<IDBValidKey[]> { ): IDBRequest<IDBValidKey[]> {
throw Error("not implemented"); this._confirmIndexExists();
this._confirmActiveTransaction();
if (!(query instanceof BridgeIDBKeyRange)) {
query = BridgeIDBKeyRange._valueToKeyRange(query);
}
if (count === undefined) {
count = -1;
}
const getReq: RecordGetRequest = {
direction: "next",
indexName: this._name,
limit: count,
range: query,
objectStoreName: this._objectStore._name,
resultLevel: ResultLevel.OnlyKeys,
};
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
const result = await this._backend.getRecords(btx, getReq);
const primaryKeys = result.primaryKeys;
if (!primaryKeys) {
throw Error("invariant violated");
}
return primaryKeys;
};
return this._objectStore._transaction._execRequestAsync({
operation,
source: this,
});
} }
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-count-IDBRequest-any-key // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBIndex-count-IDBRequest-any-key
public count(key: BridgeIDBKeyRange | IDBValidKey | null | undefined) { public count(key: BridgeIDBKeyRange | IDBValidKey | null | undefined) {
this._confirmIndexExists();
this._confirmActiveTransaction(); this._confirmActiveTransaction();
if (key === null) { if (key === null) {
@ -1718,14 +1789,147 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
query?: BridgeIDBKeyRange | IDBValidKey, query?: BridgeIDBKeyRange | IDBValidKey,
count?: number, count?: number,
): IDBRequest<any[]> { ): IDBRequest<any[]> {
throw Error("not implemented"); if (BridgeIDBFactory.enableTracing) {
console.log(`getting from object store ${this._name} key ${query}`);
}
if (arguments.length === 0) {
throw new TypeError();
}
if (!this._transaction._active) {
throw new TransactionInactiveError();
}
if (this._deleted) {
throw new InvalidStateError(
"tried to call 'delete' on a deleted object store",
);
}
if (count === undefined) {
count = -1;
}
let keyRange: BridgeIDBKeyRange;
if (query instanceof BridgeIDBKeyRange) {
keyRange = query;
} else {
try {
keyRange = BridgeIDBKeyRange.only(valueToKey(query));
} catch (e) {
throw new DataError(
`invalid key (type ${typeof query}) for object store '${this._name}'`,
);
}
}
const recordRequest: RecordGetRequest = {
objectStoreName: this._name,
indexName: undefined,
lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: count,
resultLevel: ResultLevel.Full,
range: keyRange,
};
const operation = async () => {
if (BridgeIDBFactory.enableTracing) {
console.log("running getAll operation:", recordRequest);
}
const { btx } = this._confirmStartedBackendTransaction();
const result = await this._backend.getRecords(btx, recordRequest);
if (BridgeIDBFactory.enableTracing) {
console.log("get operation result count:", result.count);
}
const values = result.values;
if (!values) {
throw Error("invariant violated");
}
return values.map((x) => structuredRevive(x));
};
return this._transaction._execRequestAsync({
operation,
source: this,
});
} }
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
public getKey( public getKey(
key?: BridgeIDBKeyRange | IDBValidKey, query?: BridgeIDBKeyRange | IDBValidKey,
): IDBRequest<IDBValidKey | undefined> { ): IDBRequest<IDBValidKey | undefined> {
throw Error("not implemented"); if (arguments.length === 0) {
throw new TypeError();
}
if (!this._transaction._active) {
throw new TransactionInactiveError();
}
if (this._deleted) {
throw new InvalidStateError(
"tried to call 'delete' on a deleted object store",
);
}
let keyRange: BridgeIDBKeyRange;
if (query instanceof BridgeIDBKeyRange) {
keyRange = query;
} else {
try {
keyRange = BridgeIDBKeyRange.only(valueToKey(query));
} catch (e) {
throw new DataError(
`invalid key (type ${typeof query}) for object store '${this._name}'`,
);
}
}
const recordRequest: RecordGetRequest = {
objectStoreName: this._name,
indexName: undefined,
lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: 1,
resultLevel: ResultLevel.OnlyKeys,
range: keyRange,
};
const operation = async () => {
if (BridgeIDBFactory.enableTracing) {
console.log("running get operation:", recordRequest);
}
const { btx } = this._confirmStartedBackendTransaction();
const result = await this._backend.getRecords(btx, recordRequest);
if (BridgeIDBFactory.enableTracing) {
console.log("get operation result count:", result.count);
}
if (result.count === 0) {
return undefined;
}
const primaryKeys = result.primaryKeys;
if (!primaryKeys) {
throw Error("invariant violated");
}
if (primaryKeys.length !== 1) {
throw Error("invariant violated");
}
return structuredRevive(primaryKeys[0]);
};
return this._transaction._execRequestAsync({
operation,
source: this,
});
} }
// http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
@ -1733,11 +1937,86 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
query?: BridgeIDBKeyRange | IDBValidKey, query?: BridgeIDBKeyRange | IDBValidKey,
count?: number, count?: number,
): IDBRequest<any[]> { ): IDBRequest<any[]> {
throw Error("not implemented"); if (arguments.length === 0) {
throw new TypeError();
} }
public clear(): IDBRequest<undefined> { if (!this._transaction._active) {
throw Error("not implemented"); throw new TransactionInactiveError();
}
if (this._deleted) {
throw new InvalidStateError(
"tried to call 'delete' on a deleted object store",
);
}
if (count === undefined) {
count = -1;
}
let keyRange: BridgeIDBKeyRange;
if (query instanceof BridgeIDBKeyRange) {
keyRange = query;
} else {
try {
keyRange = BridgeIDBKeyRange.only(valueToKey(query));
} catch (e) {
throw new DataError(
`invalid key (type ${typeof query}) for object store '${this._name}'`,
);
}
}
const recordRequest: RecordGetRequest = {
objectStoreName: this._name,
indexName: undefined,
lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: count,
resultLevel: ResultLevel.OnlyKeys,
range: keyRange,
};
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
const result = await this._backend.getRecords(btx, recordRequest);
const primaryKeys = result.primaryKeys;
if (!primaryKeys) {
throw Error("invariant violated");
}
return primaryKeys.map((x) => structuredRevive(x));
};
return this._transaction._execRequestAsync({
operation,
source: this,
});
}
public clear(): IDBRequest {
if (!this._transaction._active) {
throw new TransactionInactiveError();
}
if (this._deleted) {
throw new InvalidStateError(
"tried to call 'delete' on a deleted object store",
);
}
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
await this._backend.clearObjectStore(btx, this._name);
};
return this._transaction._execRequestAsync({
operation,
source: this,
});
} }
public openCursor( public openCursor(
@ -2228,12 +2507,12 @@ export class BridgeIDBTransaction
if (this._db._upgradeTransaction) { if (this._db._upgradeTransaction) {
for (const os of this._usedObjectStores) { for (const os of this._usedObjectStores) {
if (os._justCreated) { if (os._justCreated) {
os._deleted = true os._deleted = true;
} }
} }
for (const ind of this._usedIndexes) { for (const ind of this._usedIndexes) {
if (ind._justCreated) { if (ind._justCreated) {
ind._deleted = true ind._deleted = true;
} }
} }
} }

View File

@ -7,6 +7,9 @@ importers:
typeson: 5.18.2 typeson: 5.18.2
typeson-registry: 1.0.0-alpha.38 typeson-registry: 1.0.0-alpha.38
devDependencies: devDependencies:
'@rollup/plugin-commonjs': 17.1.0_rollup@2.37.1
'@rollup/plugin-json': 4.1.0_rollup@2.37.1
'@rollup/plugin-node-resolve': 11.2.0_rollup@2.37.1
'@types/node': 14.14.22 '@types/node': 14.14.22
ava: 3.15.0 ava: 3.15.0
esm: 3.2.25 esm: 3.2.25
@ -15,6 +18,9 @@ importers:
rollup: 2.37.1 rollup: 2.37.1
typescript: 4.1.3 typescript: 4.1.3
specifiers: specifiers:
'@rollup/plugin-commonjs': ^17.1.0
'@rollup/plugin-json': ^4.1.0
'@rollup/plugin-node-resolve': ^11.2.0
'@types/node': ^14.14.22 '@types/node': ^14.14.22
ava: ^3.15.0 ava: ^3.15.0
esm: ^3.2.25 esm: ^3.2.25
@ -520,6 +526,23 @@ packages:
rollup: ^2.30.0 rollup: ^2.30.0
resolution: resolution:
integrity: sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA== integrity: sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA==
/@rollup/plugin-commonjs/17.1.0_rollup@2.37.1:
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.37.1
commondir: 1.0.1
estree-walker: 2.0.2
glob: 7.1.6
is-reference: 1.2.1
magic-string: 0.25.7
resolve: 1.20.0
rollup: 2.37.1
dev: true
engines:
node: '>= 8.0.0'
peerDependencies:
rollup: ^2.30.0
resolution:
integrity: sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==
/@rollup/plugin-json/4.1.0_rollup@2.37.1: /@rollup/plugin-json/4.1.0_rollup@2.37.1:
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.37.1 '@rollup/pluginutils': 3.1.0_rollup@2.37.1
@ -545,6 +568,22 @@ packages:
rollup: ^1.20.0||^2.0.0 rollup: ^1.20.0||^2.0.0
resolution: resolution:
integrity: sha512-ouBBppRdWJKCllDXGzJ7ZIkYbaq+5TmyP0smt1vdJCFfoZhLi31vhpmjLhyo8lreHf4RoeSNllaWrvSqHpHRog== integrity: sha512-ouBBppRdWJKCllDXGzJ7ZIkYbaq+5TmyP0smt1vdJCFfoZhLi31vhpmjLhyo8lreHf4RoeSNllaWrvSqHpHRog==
/@rollup/plugin-node-resolve/11.2.0_rollup@2.37.1:
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.37.1
'@types/resolve': 1.17.1
builtin-modules: 3.2.0
deepmerge: 4.2.2
is-module: 1.0.0
resolve: 1.20.0
rollup: 2.37.1
dev: true
engines:
node: '>= 10.0.0'
peerDependencies:
rollup: ^1.20.0||^2.0.0
resolution:
integrity: sha512-qHjNIKYt5pCcn+5RUBQxK8krhRvf1HnyVgUCcFFcweDS7fhkOLZeYh0mhHK6Ery8/bb9tvN/ubPzmfF0qjDCTA==
/@rollup/plugin-replace/2.3.4_rollup@2.37.1: /@rollup/plugin-replace/2.3.4_rollup@2.37.1:
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.37.1 '@rollup/pluginutils': 3.1.0_rollup@2.37.1
@ -684,6 +723,10 @@ packages:
/@types/node/14.14.22: /@types/node/14.14.22:
resolution: resolution:
integrity: sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== integrity: sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==
/@types/node/14.14.31:
dev: true
resolution:
integrity: sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
/@types/normalize-package-data/2.4.0: /@types/normalize-package-data/2.4.0:
dev: true dev: true
resolution: resolution:
@ -707,7 +750,7 @@ packages:
integrity: sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== integrity: sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==
/@types/resolve/1.17.1: /@types/resolve/1.17.1:
dependencies: dependencies:
'@types/node': 14.14.22 '@types/node': 14.14.31
dev: true dev: true
resolution: resolution:
integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
@ -3296,6 +3339,7 @@ packages:
resolution: resolution:
integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA= integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=
/lodash.sortby/4.7.0: /lodash.sortby/4.7.0:
dev: false
resolution: resolution:
integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
/lodash/4.17.20: /lodash/4.17.20:
@ -4239,6 +4283,13 @@ packages:
dev: true dev: true
resolution: resolution:
integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
/resolve/1.20.0:
dependencies:
is-core-module: 2.2.0
path-parse: 1.0.6
dev: true
resolution:
integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
/responselike/1.0.2: /responselike/1.0.2:
dependencies: dependencies:
lowercase-keys: 1.0.1 lowercase-keys: 1.0.1
@ -4781,6 +4832,7 @@ packages:
/tr46/2.0.2: /tr46/2.0.2:
dependencies: dependencies:
punycode: 2.1.1 punycode: 2.1.1
dev: false
engines: engines:
node: '>=8' node: '>=8'
resolution: resolution:
@ -5015,6 +5067,7 @@ packages:
resolution: resolution:
integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
/webidl-conversions/6.1.0: /webidl-conversions/6.1.0:
dev: false
engines: engines:
node: '>=10.4' node: '>=10.4'
resolution: resolution: