summaryrefslogtreecommitdiff
path: root/packages/idb-bridge/src/bridge-idb.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/idb-bridge/src/bridge-idb.ts')
-rw-r--r--packages/idb-bridge/src/bridge-idb.ts588
1 files changed, 340 insertions, 248 deletions
diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts
index f015d2a9f..afb3f4224 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -17,14 +17,18 @@
import {
Backend,
+ ConnectResult,
DatabaseConnection,
DatabaseTransaction,
- RecordGetRequest,
+ IndexGetQuery,
+ IndexMeta,
+ ObjectStoreGetQuery,
+ ObjectStoreMeta,
+ RecordGetResponse,
RecordStoreRequest,
ResultLevel,
- Schema,
StoreLevel,
-} from "./backend-interface";
+} from "./backend-interface.js";
import {
DOMException,
DOMStringList,
@@ -41,10 +45,10 @@ import {
IDBTransaction,
IDBTransactionMode,
IDBValidKey,
-} from "./idbtypes";
-import { canInjectKey } from "./util/canInjectKey";
-import { compareKeys } from "./util/cmp";
-import { enforceRange } from "./util/enforceRange";
+} from "./idbtypes.js";
+import { canInjectKey } from "./util/canInjectKey.js";
+import { compareKeys } from "./util/cmp.js";
+import { enforceRange } from "./util/enforceRange.js";
import {
AbortError,
ConstraintError,
@@ -56,29 +60,26 @@ import {
ReadOnlyError,
TransactionInactiveError,
VersionError,
-} from "./util/errors";
-import { FakeDOMStringList, fakeDOMStringList } from "./util/fakeDOMStringList";
-import FakeEvent from "./util/FakeEvent";
-import FakeEventTarget from "./util/FakeEventTarget";
-import { makeStoreKeyValue } from "./util/makeStoreKeyValue";
-import { normalizeKeyPath } from "./util/normalizeKeyPath";
-import { openPromise } from "./util/openPromise";
-import queueTask from "./util/queueTask";
-import { structuredClone } from "./util/structuredClone";
-import { validateKeyPath } from "./util/validateKeyPath";
-import { valueToKey } from "./util/valueToKey";
-
-/** @public */
+} from "./util/errors.js";
+import { fakeDOMStringList } from "./util/fakeDOMStringList.js";
+import FakeEvent from "./util/FakeEvent.js";
+import FakeEventTarget from "./util/FakeEventTarget.js";
+import { makeStoreKeyValue } from "./util/makeStoreKeyValue.js";
+import { normalizeKeyPath } from "./util/normalizeKeyPath.js";
+import { openPromise } from "./util/openPromise.js";
+import queueTask from "./util/queueTask.js";
+import { checkStructuredCloneOrThrow } from "./util/structuredClone.js";
+import { validateKeyPath } from "./util/validateKeyPath.js";
+import { valueToKey } from "./util/valueToKey.js";
+
export type CursorSource = BridgeIDBIndex | BridgeIDBObjectStore;
-/** @public */
export interface RequestObj {
operation: () => Promise<any>;
request?: BridgeIDBRequest | undefined;
source?: any;
}
-/** @public */
export interface BridgeIDBDatabaseInfo {
name: string;
version: number;
@@ -98,8 +99,6 @@ function simplifyRange(
/**
* http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#cursor
- *
- * @public
*/
export class BridgeIDBCursor implements IDBCursor {
_request: BridgeIDBRequest | undefined;
@@ -204,29 +203,56 @@ export class BridgeIDBCursor implements IDBCursor {
);
BridgeIDBFactory.enableTracing &&
console.log("cursor type ", this.toString());
- const isIndex = this._indexName !== undefined;
- const recordGetRequest: RecordGetRequest = {
- direction: this.direction,
- indexName: this._indexName,
- lastIndexPosition: this._indexPosition,
- lastObjectStorePosition: this._objectStorePosition,
- limit: 1,
- range: simplifyRange(this._range),
- objectStoreName: this._objectStoreName,
- advanceIndexKey: isIndex ? key : undefined,
- advancePrimaryKey: isIndex ? primaryKey : key,
- resultLevel: this._keyOnly ? ResultLevel.OnlyKeys : ResultLevel.Full,
- };
+ const indexName = this._indexName;
const { btx } = this.source._confirmStartedBackendTransaction();
- let response = await this._backend.getRecords(btx, recordGetRequest);
+ let response: RecordGetResponse;
+
+ if (indexName != null) {
+ const indexRecordGetRequest: IndexGetQuery = {
+ direction: this.direction,
+ indexName: indexName,
+ lastIndexPosition: this._indexPosition,
+ lastObjectStorePosition: this._objectStorePosition,
+ limit: 1,
+ range: simplifyRange(this._range),
+ objectStoreName: this._objectStoreName,
+ advanceIndexKey: key,
+ advancePrimaryKey: primaryKey,
+ resultLevel: this._keyOnly ? ResultLevel.OnlyKeys : ResultLevel.Full,
+ };
+ response = await this._backend.getIndexRecords(
+ btx,
+ indexRecordGetRequest,
+ );
+ } else {
+ if (primaryKey != null) {
+ // Only allowed for index cursors
+ throw new InvalidAccessError();
+ }
+ const objStoreGetRequest: ObjectStoreGetQuery = {
+ direction: this.direction,
+ lastObjectStorePosition: this._objectStorePosition,
+ limit: 1,
+ range: simplifyRange(this._range),
+ objectStoreName: this._objectStoreName,
+ advancePrimaryKey: key,
+ resultLevel: this._keyOnly ? ResultLevel.OnlyKeys : ResultLevel.Full,
+ };
+ response = await this._backend.getObjectStoreRecords(
+ btx,
+ objStoreGetRequest,
+ );
+ }
if (response.count === 0) {
if (BridgeIDBFactory.enableTracing) {
console.log("cursor is returning empty result");
}
this._gotValue = false;
+ this._key = undefined;
+ this._value = undefined;
return null;
}
@@ -234,11 +260,6 @@ export class BridgeIDBCursor implements IDBCursor {
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 {
@@ -303,7 +324,7 @@ export class BridgeIDBCursor implements IDBCursor {
try {
// Only called for the side effect of throwing an exception
- structuredClone(value);
+ checkStructuredCloneOrThrow(value);
} catch (e) {
throw new DataCloneError();
}
@@ -327,6 +348,7 @@ export class BridgeIDBCursor implements IDBCursor {
}
const { btx } = this.source._confirmStartedBackendTransaction();
await this._backend.storeRecord(btx, storeReq);
+ // FIXME: update the index position here!
};
return transaction._execRequestAsync({
operation,
@@ -546,7 +568,6 @@ const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
};
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
-/** @public */
export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
_closePending = false;
_closed = false;
@@ -557,7 +578,16 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
_backendConnection: DatabaseConnection;
_backend: Backend;
- _schema: Schema;
+ _name: string;
+
+ _initialVersion: number;
+
+ _version: number;
+
+ // "object store set" from the spec
+ _objectStoreSet: string[];
+
+ // _schema: Schema;
/**
* Name that can be set to identify the object store in logs.
@@ -565,17 +595,15 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
_debugName: string | undefined = undefined;
get name(): string {
- return this._schema.databaseName;
+ return this._name;
}
get version(): number {
- return this._schema.databaseVersion;
+ return this._version;
}
get objectStoreNames(): DOMStringList {
- return fakeDOMStringList(
- Object.keys(this._schema.objectStores),
- ).sort() as DOMStringList;
+ return fakeDOMStringList([...this._objectStoreSet]).sort() as DOMStringList;
}
/**
@@ -602,13 +630,13 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
}
}
- constructor(backend: Backend, backendConnection: DatabaseConnection) {
+ constructor(name: string, backend: Backend, connResult: ConnectResult) {
super();
-
- this._schema = backend.getSchema(backendConnection);
-
+ this._name = name;
+ this._version = this._initialVersion = connResult.version;
this._backend = backend;
- this._backendConnection = backendConnection;
+ this._backendConnection = connResult.conn;
+ this._objectStoreSet = connResult.objectStores;
}
// http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
@@ -641,7 +669,8 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
validateKeyPath(keyPath);
}
- if (Object.keys(this._schema.objectStores).includes(name)) {
+ if (this._objectStoreSet.includes(name)) {
+ // Already exists
throw new ConstraintError();
}
@@ -656,7 +685,9 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
autoIncrement,
);
- this._schema = this._backend.getCurrentTransactionSchema(backendTx);
+ transaction._scope.add(name);
+ this._objectStoreSet.push(name);
+ this._objectStoreSet.sort();
const newObjectStore = transaction.objectStore(name);
newObjectStore._justCreated = true;
@@ -678,6 +709,10 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
os._deleted = true;
transaction._objectStoresCache.delete(name);
}
+ transaction._cachedObjectStoreNames = undefined;
+ transaction._scope.delete(name);
+ const nameIdx = this._objectStoreSet.indexOf(name);
+ this._objectStoreSet.splice(nameIdx, 1);
}
public _internalTransaction(
@@ -700,7 +735,9 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
}
if (this._closePending) {
- throw new InvalidStateError();
+ throw new InvalidStateError(
+ `tried to start transaction on ${this._name}, but a close is pending`,
+ );
}
if (!Array.isArray(storeNames)) {
@@ -762,10 +799,8 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
}
}
-/** @public */
export type DatabaseList = Array<{ name: string; version: number }>;
-/** @public */
export class BridgeIDBFactory {
public cmp = compareKeys;
private backend: Backend;
@@ -806,8 +841,10 @@ export class BridgeIDBFactory {
});
request.dispatchEvent(event2);
} catch (err: any) {
- request.error = new Error();
- request.error.name = err.name;
+ const myErr = new Error();
+ myErr.name = err.name;
+ myErr.message = err.message;
+ request.error = myErr;
request.readyState = "done";
const event = new FakeEvent("error", {
@@ -837,27 +874,26 @@ export class BridgeIDBFactory {
const request = new BridgeIDBOpenDBRequest();
queueTask(async () => {
- let dbconn: DatabaseConnection;
+ let dbConnRes: ConnectResult;
try {
if (BridgeIDBFactory.enableTracing) {
console.log("TRACE: connecting to database");
}
- dbconn = await this.backend.connectDatabase(name);
+ dbConnRes = await this.backend.connectDatabase(name);
if (BridgeIDBFactory.enableTracing) {
console.log("TRACE: connected!");
}
} catch (err: any) {
if (BridgeIDBFactory.enableTracing) {
console.log(
- "TRACE: caught exception while trying to connect with backend",
+ "TRACE: caught exception while trying to connect with backend:",
+ err,
);
}
request._finishWithError(err);
return;
}
-
- const schema = this.backend.getSchema(dbconn);
- const existingVersion = schema.databaseVersion;
+ const existingVersion = dbConnRes.version;
if (version === undefined) {
version = existingVersion !== 0 ? existingVersion : 1;
@@ -875,7 +911,7 @@ export class BridgeIDBFactory {
return;
}
- const db = new BridgeIDBDatabase(this.backend, dbconn);
+ const db = new BridgeIDBDatabase(name, this.backend, dbConnRes);
if (existingVersion == requestedVersion) {
request.result = db;
@@ -896,6 +932,9 @@ export class BridgeIDBFactory {
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
for (const otherConn of this.connections) {
+ if (otherConn._name != db._name) {
+ continue;
+ }
if (otherConn._closePending) {
continue;
}
@@ -925,17 +964,14 @@ export class BridgeIDBFactory {
}
const backendTransaction = await this.backend.enterVersionChange(
- dbconn,
+ dbConnRes.conn,
requestedVersion,
);
// We need to expose the new version number to the upgrade transaction.
- db._schema = this.backend.getCurrentTransactionSchema(
- backendTransaction,
- );
-
+ db._version = version;
const transaction = db._internalTransaction(
- [],
+ dbConnRes.objectStores,
"versionchange",
backendTransaction,
request,
@@ -961,7 +997,7 @@ export class BridgeIDBFactory {
await transaction._waitDone();
- // We re-use the same transaction (as per spec) here.
+ // We reuse the same transaction (as per spec) here.
transaction._active = true;
if (db._closed || db._closePending) {
@@ -1027,37 +1063,48 @@ export class BridgeIDBFactory {
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
-/** @public */
export class BridgeIDBIndex implements IDBIndex {
_objectStore: BridgeIDBObjectStore;
+ _indexMeta: IndexMeta;
+ _originalName: string | undefined = undefined;
+ _deleted: boolean = false;
+ _name: string;
+
+ /**
+ * Was this index newly created in the current transaction?
+ */
+ _justCreated: boolean = false;
get objectStore(): IDBObjectStore {
return this._objectStore;
}
- get _schema(): Schema {
- return this._objectStore._transaction._db._schema;
- }
-
get keyPath(): IDBKeyPath | IDBKeyPath[] {
- return this._schema.objectStores[this._objectStore.name].indexes[this._name]
- .keyPath;
+ return this._indexMeta.keyPath;
}
get multiEntry(): boolean {
- return this._schema.objectStores[this._objectStore.name].indexes[this._name]
- .multiEntry;
+ return this._indexMeta.multiEntry;
}
get unique(): boolean {
- return this._schema.objectStores[this._objectStore.name].indexes[this._name]
- .unique;
+ return this._indexMeta.multiEntry;
}
get _backend(): Backend {
return this._objectStore._backend;
}
+ constructor(
+ objectStore: BridgeIDBObjectStore,
+ name: string,
+ indexMeta: IndexMeta,
+ ) {
+ this._name = name;
+ this._objectStore = objectStore;
+ this._indexMeta = indexMeta;
+ }
+
_confirmStartedBackendTransaction(): { btx: DatabaseTransaction } {
return this._objectStore._confirmStartedBackendTransaction();
}
@@ -1066,20 +1113,6 @@ export class BridgeIDBIndex implements IDBIndex {
this._objectStore._confirmActiveTransaction();
}
- private _name: string;
-
- public _deleted: boolean = false;
-
- /**
- * Was this index newly created in the current transaction?
- */
- _justCreated: boolean = false;
-
- constructor(objectStore: BridgeIDBObjectStore, name: string) {
- this._name = name;
- this._objectStore = objectStore;
- }
-
get name() {
return this._name;
}
@@ -1104,19 +1137,39 @@ export class BridgeIDBIndex implements IDBIndex {
if (newName === oldName) {
return;
}
-
+ if (this._originalName != null) {
+ this._originalName = oldName;
+ }
this._backend.renameIndex(btx, this._objectStore.name, oldName, newName);
+ this._applyNameChange(oldName, newName);
+ if (this._objectStore._objectStoreMeta.indexSet.indexOf(name) >= 0) {
+ throw new Error("internal invariant violated");
+ }
+ }
- this._objectStore._transaction._db._schema = this._backend.getCurrentTransactionSchema(
- btx,
- );
-
- this._objectStore._indexesCache.delete(oldName);
- this._objectStore._indexesCache.set(newName, this);
+ _applyNameChange(oldName: string, newName: string) {
+ this._objectStore._indexHandlesCache.delete(oldName);
+ this._objectStore._indexHandlesCache.set(newName, this);
+ const indexSet = this._objectStore._objectStoreMeta.indexSet;
+ const indexIdx = indexSet.indexOf(oldName);
+ indexSet[indexIdx] = newName;
+ indexSet.sort();
this._name = newName;
+ }
- if (this._objectStore._indexNames.indexOf(name) >= 0) {
- throw new Error("internal invariant violated");
+ _applyDelete() {
+ this._objectStore._indexHandlesCache.delete(this._name);
+ const indexSet = this._objectStore._objectStoreMeta.indexSet;
+ const indexIdx = indexSet.indexOf(this._name);
+ indexSet.splice(indexIdx, 1);
+ }
+
+ _abort() {
+ if (this._originalName != null) {
+ this._applyNameChange(this._name, this._originalName);
+ }
+ if (this._justCreated) {
+ this._deleted = true;
}
}
@@ -1132,8 +1185,6 @@ export class BridgeIDBIndex implements IDBIndex {
);
}
- BridgeIDBFactory.enableTracing && console.log("opening cursor on", this);
-
this._confirmActiveTransaction();
range = simplifyRange(range);
@@ -1199,34 +1250,23 @@ export class BridgeIDBIndex implements IDBIndex {
}
private _confirmIndexExists() {
- const storeSchema = this._schema.objectStores[this._objectStore._name];
- if (!storeSchema) {
- throw new InvalidStateError(
- `no schema for object store '${this._objectStore._name}'`,
- );
- }
- if (!storeSchema.indexes[this._name]) {
- throw new InvalidStateError(
- `no schema for index '${this._name}' of object store '${this._objectStore._name}'`,
- );
- }
- }
-
- get(key: BridgeIDBKeyRange | IDBValidKey) {
if (this._deleted) {
throw new InvalidStateError();
}
if (this._objectStore._deleted) {
throw new InvalidStateError();
}
- this._confirmActiveTransaction();
+ }
+
+ get(key: BridgeIDBKeyRange | IDBValidKey) {
this._confirmIndexExists();
+ this._confirmActiveTransaction();
if (!(key instanceof BridgeIDBKeyRange)) {
key = BridgeIDBKeyRange._valueToKeyRange(key);
}
- const getReq: RecordGetRequest = {
+ const getReq: IndexGetQuery = {
direction: "next",
indexName: this._name,
limit: 1,
@@ -1237,7 +1277,7 @@ export class BridgeIDBIndex implements IDBIndex {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, getReq);
+ const result = await this._backend.getIndexRecords(btx, getReq);
if (result.count == 0) {
return undefined;
}
@@ -1273,7 +1313,7 @@ export class BridgeIDBIndex implements IDBIndex {
count = -1;
}
- const getReq: RecordGetRequest = {
+ const getReq: IndexGetQuery = {
direction: "next",
indexName: this._name,
limit: count,
@@ -1284,7 +1324,7 @@ export class BridgeIDBIndex implements IDBIndex {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, getReq);
+ const result = await this._backend.getIndexRecords(btx, getReq);
const values = result.values;
if (!values) {
throw Error("invariant violated");
@@ -1307,7 +1347,7 @@ export class BridgeIDBIndex implements IDBIndex {
key = BridgeIDBKeyRange._valueToKeyRange(key);
}
- const getReq: RecordGetRequest = {
+ const getReq: IndexGetQuery = {
direction: "next",
indexName: this._name,
limit: 1,
@@ -1318,7 +1358,7 @@ export class BridgeIDBIndex implements IDBIndex {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, getReq);
+ const result = await this._backend.getIndexRecords(btx, getReq);
if (result.count == 0) {
return undefined;
}
@@ -1351,7 +1391,7 @@ export class BridgeIDBIndex implements IDBIndex {
count = -1;
}
- const getReq: RecordGetRequest = {
+ const getReq: IndexGetQuery = {
direction: "next",
indexName: this._name,
limit: count,
@@ -1362,7 +1402,7 @@ export class BridgeIDBIndex implements IDBIndex {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, getReq);
+ const result = await this._backend.getIndexRecords(btx, getReq);
const primaryKeys = result.primaryKeys;
if (!primaryKeys) {
throw Error("invariant violated");
@@ -1388,7 +1428,7 @@ export class BridgeIDBIndex implements IDBIndex {
key = BridgeIDBKeyRange.only(valueToKey(key));
}
- const getReq: RecordGetRequest = {
+ const getReq: IndexGetQuery = {
direction: "next",
indexName: this._name,
limit: 1,
@@ -1399,7 +1439,7 @@ export class BridgeIDBIndex implements IDBIndex {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, getReq);
+ const result = await this._backend.getIndexRecords(btx, getReq);
return result.count;
};
@@ -1415,7 +1455,6 @@ export class BridgeIDBIndex implements IDBIndex {
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#range-concept
-/** @public */
export class BridgeIDBKeyRange {
public static only(value: IDBValidKey) {
if (arguments.length === 0) {
@@ -1525,10 +1564,8 @@ export class BridgeIDBKeyRange {
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
-/** @public */
export class BridgeIDBObjectStore implements IDBObjectStore {
- _indexesCache: Map<string, BridgeIDBIndex> = new Map();
-
+ _indexHandlesCache: Map<string, BridgeIDBIndex> = new Map();
_transaction: BridgeIDBTransaction;
/**
@@ -1536,41 +1573,43 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
*/
_debugName: string | undefined = undefined;
+ // Was the object store (not the handle, but the underlying store)
+ // created in this upgrade transaction?
_justCreated: boolean = false;
+ _originalName: string | undefined = undefined;
+ _objectStoreMeta: ObjectStoreMeta;
+
get transaction(): IDBTransaction {
return this._transaction;
}
get autoIncrement(): boolean {
- return this._schema.objectStores[this._name].autoIncrement;
- }
-
- get _indexNames(): FakeDOMStringList {
- return fakeDOMStringList(
- Object.keys(this._schema.objectStores[this._name].indexes),
- ).sort();
+ return this._objectStoreMeta.autoIncrement;
}
get indexNames(): DOMStringList {
- return this._indexNames as DOMStringList;
+ return fakeDOMStringList([...this._objectStoreMeta.indexSet]);
}
get keyPath(): IDBKeyPath | IDBKeyPath[] {
- return this._schema.objectStores[this._name].keyPath!;
+ // Bug in th official type declarations. The spec
+ // allows returning null here.
+ return this._objectStoreMeta.keyPath!;
}
_name: string;
- get _schema(): Schema {
- return this._transaction._db._schema;
- }
-
_deleted: boolean = false;
- constructor(transaction: BridgeIDBTransaction, name: string) {
+ constructor(
+ transaction: BridgeIDBTransaction,
+ name: string,
+ objectStoreMeta: ObjectStoreMeta,
+ ) {
this._name = name;
this._transaction = transaction;
+ this._objectStoreMeta = objectStoreMeta;
}
get name() {
@@ -1620,27 +1659,56 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
let { btx } = this._confirmStartedBackendTransaction();
newName = String(newName);
-
const oldName = this._name;
-
if (newName === oldName) {
return;
}
-
+ if (this._originalName == null) {
+ this._originalName = this._name;
+ }
this._backend.renameObjectStore(btx, oldName, newName);
- this._transaction._db._schema = this._backend.getCurrentTransactionSchema(
- btx,
- );
+ this._applyNameChange(oldName, newName);
+ }
+ _applyNameChange(oldName: string, newName: string) {
+ this._transaction._scope.delete(oldName);
+ this._transaction._scope.add(newName);
// We don't modify scope, as the scope of the transaction
// doesn't matter if we're in an upgrade transaction.
this._transaction._objectStoresCache.delete(oldName);
this._transaction._objectStoresCache.set(newName, this);
this._transaction._cachedObjectStoreNames = undefined;
-
+ const objectStoreSet = this._transaction._db._objectStoreSet;
+ const oldIdx = objectStoreSet.indexOf(oldName);
+ objectStoreSet[oldIdx] = newName;
+ objectStoreSet.sort();
this._name = newName;
}
+ _applyDelete() {
+ this._deleted = true;
+ this._transaction._objectStoresCache.delete(this._name);
+ this._transaction._cachedObjectStoreNames = undefined;
+ const objectStoreSet = this._transaction._db._objectStoreSet;
+ const oldIdx = objectStoreSet.indexOf(this._name);
+ objectStoreSet.splice(oldIdx, 1);
+ }
+
+ /**
+ * Roll back changes to the handle after an abort.
+ */
+ _abort() {
+ if (this._originalName != null) {
+ this._applyNameChange(this._name, this._originalName);
+ }
+ if (this._justCreated) {
+ this._applyDelete();
+ }
+ }
+
+ /**
+ * "To add or put with handle, value, key, and no-overwrite flag, run these steps:"
+ */
public _store(value: any, key: IDBValidKey | undefined, overwrite: boolean) {
if (BridgeIDBFactory.enableTracing) {
console.log(
@@ -1648,6 +1716,12 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
);
}
+ if (this._deleted) {
+ throw new InvalidStateError(
+ "tried to call 'put' on a deleted object store",
+ );
+ }
+
if (!this._transaction._active) {
throw new TransactionInactiveError();
}
@@ -1656,14 +1730,21 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new ReadOnlyError();
}
- const { keyPath, autoIncrement } = this._schema.objectStores[this._name];
+ const { keyPath, autoIncrement } = this._objectStoreMeta;
if (key !== null && key !== undefined) {
valueToKey(key);
}
// We only call this to synchronously verify the request.
- makeStoreKeyValue(value, key, 1, autoIncrement, keyPath);
+ // FIXME: The backend should do that!
+ makeStoreKeyValue({
+ value: value,
+ key: key,
+ currentKeyGenerator: 1,
+ autoIncrement,
+ keyPath,
+ });
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
@@ -1685,11 +1766,6 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
if (arguments.length === 0) {
throw new TypeError();
}
- if (this._deleted) {
- throw new InvalidStateError(
- "tried to call 'put' on a deleted object store",
- );
- }
return this._store(value, key, true);
}
@@ -1697,9 +1773,6 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
if (arguments.length === 0) {
throw new TypeError();
}
- if (!this._schema.objectStores[this._name]) {
- throw new InvalidStateError("object store does not exist");
- }
return this._store(value, key, false);
}
@@ -1768,10 +1841,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
}
}
- const recordRequest: RecordGetRequest = {
+ const recordRequest: ObjectStoreGetQuery = {
objectStoreName: this._name,
- indexName: undefined,
- lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: 1,
@@ -1784,7 +1855,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
console.log("running get operation:", recordRequest);
}
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, recordRequest);
+ const result = await this._backend.getObjectStoreRecords(
+ btx,
+ recordRequest,
+ );
if (BridgeIDBFactory.enableTracing) {
console.log("get operation result count:", result.count);
@@ -1834,10 +1908,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
let keyRange: BridgeIDBKeyRange | null = simplifyRange(query);
- const recordRequest: RecordGetRequest = {
+ const recordRequest: ObjectStoreGetQuery = {
objectStoreName: this._name,
- indexName: undefined,
- lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: count,
@@ -1850,7 +1922,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
console.log("running getAll operation:", recordRequest);
}
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, recordRequest);
+ const result = await this._backend.getObjectStoreRecords(
+ btx,
+ recordRequest,
+ );
if (BridgeIDBFactory.enableTracing) {
console.log("get operation result count:", result.count);
@@ -1888,10 +1963,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
let keyRange: BridgeIDBKeyRange | null = simplifyRange(query);
- const recordRequest: RecordGetRequest = {
+ const recordRequest: ObjectStoreGetQuery = {
objectStoreName: this._name,
- indexName: undefined,
- lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: 1,
@@ -1904,7 +1977,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
console.log("running getKey operation:", recordRequest);
}
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, recordRequest);
+ const result = await this._backend.getObjectStoreRecords(
+ btx,
+ recordRequest,
+ );
if (BridgeIDBFactory.enableTracing) {
console.log("getKey operation result count:", result.count);
@@ -1966,10 +2042,8 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
}
}
- const recordRequest: RecordGetRequest = {
+ const recordRequest: ObjectStoreGetQuery = {
objectStoreName: this._name,
- indexName: undefined,
- lastIndexPosition: undefined,
lastObjectStorePosition: undefined,
direction: "next",
limit: count,
@@ -1979,7 +2053,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, recordRequest);
+ const result = await this._backend.getObjectStoreRecords(
+ btx,
+ recordRequest,
+ );
const primaryKeys = result.primaryKeys;
if (!primaryKeys) {
@@ -2122,7 +2199,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new InvalidStateError();
}
- if (this._indexNames.indexOf(indexName) >= 0) {
+ if (this._objectStoreMeta.indexSet.indexOf(indexName) >= 0) {
throw new ConstraintError();
}
@@ -2141,6 +2218,9 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
unique,
);
+ this._objectStoreMeta.indexSet.push(indexName);
+ this._objectStoreMeta.indexSet.sort();
+
const idx = this.index(indexName);
idx._justCreated = true;
return idx;
@@ -2155,13 +2235,20 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
if (this._transaction._finished) {
throw new InvalidStateError();
}
-
- const index = this._indexesCache.get(name);
+ const index = this._indexHandlesCache.get(name);
if (index !== undefined) {
return index;
}
- const newIndex = new BridgeIDBIndex(this, name);
- this._indexesCache.set(name, newIndex);
+ const indexMeta = this._backend.getIndexMeta(
+ this._backendConnection,
+ this._name,
+ name,
+ );
+ if (!indexMeta) {
+ throw new NotFoundError();
+ }
+ const newIndex = new BridgeIDBIndex(this, name, indexMeta);
+ this._indexHandlesCache.set(name, newIndex);
this._transaction._usedIndexes.push(newIndex);
return newIndex;
}
@@ -2181,12 +2268,15 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
const { btx } = this._confirmStartedBackendTransaction();
- const index = this._indexesCache.get(indexName);
+ const index = this._indexHandlesCache.get(indexName);
if (index !== undefined) {
index._deleted = true;
- this._indexesCache.delete(indexName);
+ this._indexHandlesCache.delete(indexName);
}
+ const indexIdx = this._objectStoreMeta.indexSet.indexOf(indexName);
+ this._objectStoreMeta.indexSet.splice(indexIdx, 1);
+
this._backend.deleteIndex(btx, this._name, indexName);
}
@@ -2199,11 +2289,9 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
key = BridgeIDBKeyRange.only(valueToKey(key));
}
- const recordGetRequest: RecordGetRequest = {
+ const recordGetRequest: ObjectStoreGetQuery = {
direction: "next",
- indexName: undefined,
- lastIndexPosition: undefined,
- limit: -1,
+ limit: 0,
objectStoreName: this._name,
lastObjectStorePosition: undefined,
range: key,
@@ -2212,7 +2300,10 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
const operation = async () => {
const { btx } = this._confirmStartedBackendTransaction();
- const result = await this._backend.getRecords(btx, recordGetRequest);
+ const result = await this._backend.getObjectStoreRecords(
+ btx,
+ recordGetRequest,
+ );
return result.count;
};
@@ -2224,7 +2315,6 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
}
}
-/** @public */
export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
_result: any = null;
_error: Error | null | undefined = null;
@@ -2234,11 +2324,8 @@ export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
}
return this._source;
}
- _source:
- | BridgeIDBCursor
- | BridgeIDBIndex
- | BridgeIDBObjectStore
- | null = null;
+ _source: BridgeIDBCursor | BridgeIDBIndex | BridgeIDBObjectStore | null =
+ null;
transaction: BridgeIDBTransaction | null = null;
readyState: "done" | "pending" = "pending";
onsuccess: EventListener | null = null;
@@ -2298,10 +2385,10 @@ export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
}
}
-/** @public */
export class BridgeIDBOpenDBRequest
extends BridgeIDBRequest
- implements IDBOpenDBRequest {
+ implements IDBOpenDBRequest
+{
public onupgradeneeded: EventListener | null = null;
public onblocked: EventListener | null = null;
@@ -2346,10 +2433,10 @@ function waitMacroQueue(): Promise<void> {
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
-/** @public */
export class BridgeIDBTransaction
extends FakeEventTarget
- implements IDBTransaction {
+ implements IDBTransaction
+{
_committed: boolean = false;
/**
* A transaction is active as long as new operations can be
@@ -2384,6 +2471,8 @@ export class BridgeIDBTransaction
return this._committed || this._aborted;
}
+ _counter = 0;
+
_openRequest: BridgeIDBOpenDBRequest | null = null;
_backendTransaction?: DatabaseTransaction;
@@ -2392,13 +2481,9 @@ export class BridgeIDBTransaction
get objectStoreNames(): DOMStringList {
if (!this._cachedObjectStoreNames) {
- if (this._openRequest) {
- this._cachedObjectStoreNames = this._db.objectStoreNames;
- } else {
- this._cachedObjectStoreNames = fakeDOMStringList(
- Array.from(this._scope).sort(),
- );
- }
+ this._cachedObjectStoreNames = fakeDOMStringList(
+ Array.from(this._scope).sort(),
+ );
}
return this._cachedObjectStoreNames;
}
@@ -2498,41 +2583,34 @@ export class BridgeIDBTransaction
}
}
+ // All steps before happened synchronously. Now
+ // we asynchronously roll back the backend transaction,
+ // if necessary/possible.
+
+ const maybeBtx = this._backendTransaction;
+ if (maybeBtx) {
+ this._backend.rollback(maybeBtx);
+ }
+
// "Any object stores and indexes which were created during the
// transaction are now considered deleted for the purposes of other
// algorithms."
if (this._db._upgradeTransaction) {
for (const os of this._usedObjectStores) {
- if (os._justCreated) {
- os._deleted = true;
- }
+ os._abort();
}
for (const ind of this._usedIndexes) {
- if (ind._justCreated) {
- ind._deleted = true;
- }
+ ind._abort();
}
}
+ this._db._version = this._db._initialVersion;
+
// ("abort a transaction", step 5.1)
if (this._openRequest) {
this._db._upgradeTransaction = null;
}
- // All steps before happened synchronously. Now
- // we asynchronously roll back the backend transaction,
- // if necessary/possible.
-
- const maybeBtx = this._backendTransaction;
- if (maybeBtx) {
- this._db._schema = this._backend.getInitialTransactionSchema(maybeBtx);
- // Only roll back if we actually executed the scheduled operations.
- await this._backend.rollback(maybeBtx);
- this._backendTransaction = undefined;
- } else {
- this._db._schema = this._backend.getSchema(this._db._backendConnection);
- }
-
queueTask(() => {
const event = new FakeEvent("abort", {
bubbles: true,
@@ -2562,22 +2640,29 @@ export class BridgeIDBTransaction
throw new TransactionInactiveError();
}
- if (!this._db._schema.objectStores[name]) {
+ if (!this._scope.has(name)) {
throw new NotFoundError();
}
- if (!this._db._upgradeTransaction) {
- if (!this._scope.has(name)) {
- throw new NotFoundError();
- }
- }
-
const objectStore = this._objectStoresCache.get(name);
if (objectStore !== undefined) {
return objectStore;
}
- const newObjectStore = new BridgeIDBObjectStore(this, name);
+ const objectStoreMeta = this._backend.getObjectStoreMeta(
+ this._db._backendConnection,
+ name,
+ );
+
+ if (!objectStoreMeta) {
+ throw new NotFoundError();
+ }
+
+ const newObjectStore = new BridgeIDBObjectStore(
+ this,
+ name,
+ objectStoreMeta,
+ );
this._objectStoresCache.set(name, newObjectStore);
this._usedObjectStores.push(newObjectStore);
return newObjectStore;
@@ -2657,13 +2742,20 @@ export class BridgeIDBTransaction
}
}
- await waitMacroQueue();
-
if (!request._source) {
// Special requests like indexes that just need to run some code,
// with error handling already built into operation
await operation();
} else {
+ this._counter++;
+ if (this._counter > 100) {
+ this._counter = 0;
+ // Give a chance for macro tasks to do something
+ // If we don't do this at all, we break WPT tests.
+ // If we always wait, performance is bad.
+ await waitMacroQueue();
+ }
+
let event;
try {
BridgeIDBFactory.enableTracing &&