Skip to content

Commit

Permalink
feat(deno/idb): implement IDBObjectStore.get
Browse files Browse the repository at this point in the history
  • Loading branch information
hasundue committed Apr 22, 2024
1 parent 1668cf4 commit 8cc8754
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 22 deletions.
5 changes: 2 additions & 3 deletions deno/idb/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ export interface IDBFactory {
deleteDatabase(name: string): IDBOpenDBRequest<undefined, null>;
}

const kv = await Deno.openKv();

export const indexedDB: IDBFactory = {
open(
name: string,
version: number = 1,
): IDBOpenDBRequest<IDBDatabase, null> {
return new _IDBOpenDBRequest(async function () {
using kv = await Deno.openKv();
// Check if the database already exists and is up to date.
const existed = await kv.get<IDBDatabaseInfo>($.database(name));
if (existed.value?.version && existed.value.version >= version) {
Expand Down Expand Up @@ -68,14 +69,12 @@ export const indexedDB: IDBFactory = {
},

async databases(): Promise<IDBDatabaseInfo[]> {
using kv = await Deno.openKv();
const iter = kv.list<IDBDatabaseInfo>({ prefix: $.database.prefix });
return (await Array.fromAsync(iter)).map((it) => it.value);
},

deleteDatabase(name: string): IDBOpenDBRequest<undefined, null> {
return new _IDBOpenDBRequest(async () => {
using kv = await Deno.openKv();
await kv.delete($.database(name));
return undefined;
}) as IDBOpenDBRequest<undefined, null>;
Expand Down
6 changes: 3 additions & 3 deletions deno/idb/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IDBObjectStore } from "./stores.ts";
import { AnyIDBTransaction, IDBTransaction } from "./transactions.ts";

export interface IDBRequest<
Result,
Result = unknown,
Transaction extends AnyIDBTransaction | null = AnyIDBTransaction | null,
> extends EventTarget {
readonly error: DOMException | null;
Expand All @@ -21,7 +21,7 @@ export interface IDBRequest<
onsuccess: EventHandler | null;
}

export class _IDBRequest<Result> extends EventTarget
export class _IDBRequest<Result = unknown> extends EventTarget
implements IDBRequest<Result> {
constructor(
readonly source: IDBRequestSource | null,
Expand Down Expand Up @@ -65,7 +65,7 @@ export class _IDBRequest<Result> extends EventTarget
onsuccess: EventHandler | null = null;
}

type IDBRequestSource = IDBObjectStore | IDBIndex | IDBCursor;
export type IDBRequestSource = IDBObjectStore | IDBIndex | IDBCursor;

export interface IDBOpenDBRequest<
Result extends IDBDatabase | undefined = IDBDatabase,
Expand Down
29 changes: 22 additions & 7 deletions deno/idb/stores.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { match, placeholder as _, RegularPlaceholder } from "@core/match";
import { associateWith } from "@std/collections/associate-with";

import { _IDBDatabase } from "./databases.ts";
import { _IDBIndex, IDBIndex } from "./indexes.ts";
import { IDBValidKey, isIDBKey, KvKeyFactoryRecord as $ } from "./keys.ts";
import { _IDBRequest, IDBRequest } from "./requests.ts";
import {
_IDBTransaction,
AnyIDBTransaction,
IDBTransaction,
} from "./transactions.ts";
import { _IDBTransaction, IDBTransaction } from "./transactions.ts";

export interface IDBObjectStore<
M extends IDBTransactionMode = IDBTransactionMode,
Expand All @@ -28,6 +25,8 @@ export interface IDBObjectStore<
keyPath: string | string[],
options?: IDBIndexParameters,
) => IDBIndex;

get(key: IDBValidKey): IDBRequest<unknown>;
}

/**
Expand All @@ -47,6 +46,7 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
transaction,
autoIncrement,

// @ts-ignore: TS doesn't understand the conditional type
add: transaction.mode === "readonly" ? undefined : (
value: unknown,
key?: IDBValidKey,
Expand All @@ -59,9 +59,8 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
}
const result = ensureKey(options, value, key);
const parts = Array.isArray(result) ? result : [result];
return new _IDBRequest<IDBValidKey>(
return transaction._createRequest(
store,
transaction as AnyIDBTransaction,
() => {
(transaction as _IDBTransaction<M>)._atomic.set(
$.value(transaction.db.name, name, ...parts),
Expand All @@ -72,6 +71,7 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
);
},

// @ts-ignore: TS doesn't understand the conditional type
createIndex: transaction.mode !== "versionchange" ? undefined : (
indexName: string,
keyPath: string | string[],
Expand All @@ -85,6 +85,21 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
}
return new _IDBIndex(indexName, keyPath, options);
},

get(key: IDBValidKey): IDBRequest<unknown> {
const keyArray = Array.isArray(key) ? key : [key];
const request = transaction._createRequest(
store,
async () => {
const kv = (this.transaction.db as _IDBDatabase)._kv;
const result = await kv.get(
$.value(transaction.db.name, name, ...keyArray),
);
return result.versionstamp ? result.value : undefined;
},
);
return request;
},
};
return store;
}
Expand Down
34 changes: 34 additions & 0 deletions deno/idb/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DOMStringList } from "@lophus/lib/legacy";
import { EventHandler } from "./events.ts";
import { _IDBDatabase, IDBDatabase } from "./databases.ts";
import { _IDBObjectStore, IDBObjectStore } from "./stores.ts";
import { _IDBRequest, IDBRequestSource } from "./requests.ts";

export interface IDBTransaction<
Mode extends IDBTransactionMode = IDBTransactionMode,
Expand Down Expand Up @@ -62,4 +63,37 @@ export class _IDBTransaction<Mode extends IDBTransactionMode>
}
return _IDBObjectStore(this, name, options);
}

/**
* An internal method to add a request to the transaction.
* @internal
*/
_createRequest<R>(
source: IDBRequestSource | null,
operation: () => Promise<R>,
): _IDBRequest<R> {
if (this._completed) {
throw new DOMException(
"The transaction has already completed.",
"TransactionInactiveError",
);
}
const request = new _IDBRequest<R>(
source,
this as AnyIDBTransaction,
operation,
);
this._requests.add(request);
request.addEventListener("success", async () => {
this._requests.delete(request);
if (this._requests.size === 0) {
await this._atomic.commit();
this._completed = true;
this.dispatchEvent(new Event("complete"));
}
});
return request;
}
readonly _requests: Set<_IDBRequest> = new Set();
_completed: boolean = false;
}
35 changes: 26 additions & 9 deletions deno/idb_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
it,
} from "@std/testing/bdd";
import type {
IDBFactory,
IDBDatabase,
IDBFactory,
IDBIndex,
IDBObjectStore,
IDBOpenDBRequest,
Expand Down Expand Up @@ -173,11 +173,13 @@ describe('IDBObjectStore<"readonly">', () => {

beforeAll(async () => {
const request = self.indexedDB.open(name);
await new Promise((resolve) => {
request.onupgradeneeded = (event) =>
resolve(
event.target.result.createObjectStore("events", { keyPath: "id" }),
);
await new Promise<void>((resolve) => {
request.onupgradeneeded = (event) => {
const db = event.target.result;
const store = db.createObjectStore("events", { keyPath: "id" });
store.add({ id: 1, pubkey: "pubkey" });
resolve();
};
});
await ensure(request, "success");
store = request.result.transaction("events", "readonly").objectStore(
Expand Down Expand Up @@ -213,6 +215,14 @@ describe('IDBObjectStore<"readonly">', () => {
);
});
});

describe("get", () => {
it("should get an object", async () => {
const request = store.get(1);
await ensure(store.transaction, "complete");
assertEquals(request.result, { id: 1, pubkey: "pubkey" });
});
});
});

describe('IDBObjectStore<"readwrite">', () => {
Expand All @@ -228,9 +238,9 @@ describe('IDBObjectStore<"readwrite">', () => {
);
});
await ensure(request, "success");
store = request.result.transaction("events", "readwrite").objectStore(
"events",
);
store = request.result
.transaction("events", "readwrite")
.objectStore("events");
});

afterAll(async () => {
Expand Down Expand Up @@ -265,6 +275,13 @@ describe('IDBObjectStore<"readwrite">', () => {
);
});
});

describe("get", () => {
it("should throw a TransactionInactiveError once the transaction is completed", async () => {
await ensure(store.transaction, "complete");
assertThrows(() => store.get(1));
});
});
});

describe('IDBObjectStore<"versionchange">', () => {
Expand Down

0 comments on commit 8cc8754

Please sign in to comment.