Skip to content

Commit 8cc8754

Browse files
committed
feat(deno/idb): implement IDBObjectStore.get
1 parent 1668cf4 commit 8cc8754

File tree

5 files changed

+87
-22
lines changed

5 files changed

+87
-22
lines changed

deno/idb/mod.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ export interface IDBFactory {
2424
deleteDatabase(name: string): IDBOpenDBRequest<undefined, null>;
2525
}
2626

27+
const kv = await Deno.openKv();
28+
2729
export const indexedDB: IDBFactory = {
2830
open(
2931
name: string,
3032
version: number = 1,
3133
): IDBOpenDBRequest<IDBDatabase, null> {
3234
return new _IDBOpenDBRequest(async function () {
33-
using kv = await Deno.openKv();
3435
// Check if the database already exists and is up to date.
3536
const existed = await kv.get<IDBDatabaseInfo>($.database(name));
3637
if (existed.value?.version && existed.value.version >= version) {
@@ -68,14 +69,12 @@ export const indexedDB: IDBFactory = {
6869
},
6970

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

7676
deleteDatabase(name: string): IDBOpenDBRequest<undefined, null> {
7777
return new _IDBOpenDBRequest(async () => {
78-
using kv = await Deno.openKv();
7978
await kv.delete($.database(name));
8079
return undefined;
8180
}) as IDBOpenDBRequest<undefined, null>;

deno/idb/requests.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { IDBObjectStore } from "./stores.ts";
88
import { AnyIDBTransaction, IDBTransaction } from "./transactions.ts";
99

1010
export interface IDBRequest<
11-
Result,
11+
Result = unknown,
1212
Transaction extends AnyIDBTransaction | null = AnyIDBTransaction | null,
1313
> extends EventTarget {
1414
readonly error: DOMException | null;
@@ -21,7 +21,7 @@ export interface IDBRequest<
2121
onsuccess: EventHandler | null;
2222
}
2323

24-
export class _IDBRequest<Result> extends EventTarget
24+
export class _IDBRequest<Result = unknown> extends EventTarget
2525
implements IDBRequest<Result> {
2626
constructor(
2727
readonly source: IDBRequestSource | null,
@@ -65,7 +65,7 @@ export class _IDBRequest<Result> extends EventTarget
6565
onsuccess: EventHandler | null = null;
6666
}
6767

68-
type IDBRequestSource = IDBObjectStore | IDBIndex | IDBCursor;
68+
export type IDBRequestSource = IDBObjectStore | IDBIndex | IDBCursor;
6969

7070
export interface IDBOpenDBRequest<
7171
Result extends IDBDatabase | undefined = IDBDatabase,

deno/idb/stores.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { match, placeholder as _, RegularPlaceholder } from "@core/match";
22
import { associateWith } from "@std/collections/associate-with";
33

4+
import { _IDBDatabase } from "./databases.ts";
45
import { _IDBIndex, IDBIndex } from "./indexes.ts";
56
import { IDBValidKey, isIDBKey, KvKeyFactoryRecord as $ } from "./keys.ts";
67
import { _IDBRequest, IDBRequest } from "./requests.ts";
7-
import {
8-
_IDBTransaction,
9-
AnyIDBTransaction,
10-
IDBTransaction,
11-
} from "./transactions.ts";
8+
import { _IDBTransaction, IDBTransaction } from "./transactions.ts";
129

1310
export interface IDBObjectStore<
1411
M extends IDBTransactionMode = IDBTransactionMode,
@@ -28,6 +25,8 @@ export interface IDBObjectStore<
2825
keyPath: string | string[],
2926
options?: IDBIndexParameters,
3027
) => IDBIndex;
28+
29+
get(key: IDBValidKey): IDBRequest<unknown>;
3130
}
3231

3332
/**
@@ -47,6 +46,7 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
4746
transaction,
4847
autoIncrement,
4948

49+
// @ts-ignore: TS doesn't understand the conditional type
5050
add: transaction.mode === "readonly" ? undefined : (
5151
value: unknown,
5252
key?: IDBValidKey,
@@ -59,9 +59,8 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
5959
}
6060
const result = ensureKey(options, value, key);
6161
const parts = Array.isArray(result) ? result : [result];
62-
return new _IDBRequest<IDBValidKey>(
62+
return transaction._createRequest(
6363
store,
64-
transaction as AnyIDBTransaction,
6564
() => {
6665
(transaction as _IDBTransaction<M>)._atomic.set(
6766
$.value(transaction.db.name, name, ...parts),
@@ -72,6 +71,7 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
7271
);
7372
},
7473

74+
// @ts-ignore: TS doesn't understand the conditional type
7575
createIndex: transaction.mode !== "versionchange" ? undefined : (
7676
indexName: string,
7777
keyPath: string | string[],
@@ -85,6 +85,21 @@ export function _IDBObjectStore<M extends IDBTransactionMode>(
8585
}
8686
return new _IDBIndex(indexName, keyPath, options);
8787
},
88+
89+
get(key: IDBValidKey): IDBRequest<unknown> {
90+
const keyArray = Array.isArray(key) ? key : [key];
91+
const request = transaction._createRequest(
92+
store,
93+
async () => {
94+
const kv = (this.transaction.db as _IDBDatabase)._kv;
95+
const result = await kv.get(
96+
$.value(transaction.db.name, name, ...keyArray),
97+
);
98+
return result.versionstamp ? result.value : undefined;
99+
},
100+
);
101+
return request;
102+
},
88103
};
89104
return store;
90105
}

deno/idb/transactions.ts

+34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DOMStringList } from "@lophus/lib/legacy";
22
import { EventHandler } from "./events.ts";
33
import { _IDBDatabase, IDBDatabase } from "./databases.ts";
44
import { _IDBObjectStore, IDBObjectStore } from "./stores.ts";
5+
import { _IDBRequest, IDBRequestSource } from "./requests.ts";
56

67
export interface IDBTransaction<
78
Mode extends IDBTransactionMode = IDBTransactionMode,
@@ -62,4 +63,37 @@ export class _IDBTransaction<Mode extends IDBTransactionMode>
6263
}
6364
return _IDBObjectStore(this, name, options);
6465
}
66+
67+
/**
68+
* An internal method to add a request to the transaction.
69+
* @internal
70+
*/
71+
_createRequest<R>(
72+
source: IDBRequestSource | null,
73+
operation: () => Promise<R>,
74+
): _IDBRequest<R> {
75+
if (this._completed) {
76+
throw new DOMException(
77+
"The transaction has already completed.",
78+
"TransactionInactiveError",
79+
);
80+
}
81+
const request = new _IDBRequest<R>(
82+
source,
83+
this as AnyIDBTransaction,
84+
operation,
85+
);
86+
this._requests.add(request);
87+
request.addEventListener("success", async () => {
88+
this._requests.delete(request);
89+
if (this._requests.size === 0) {
90+
await this._atomic.commit();
91+
this._completed = true;
92+
this.dispatchEvent(new Event("complete"));
93+
}
94+
});
95+
return request;
96+
}
97+
readonly _requests: Set<_IDBRequest> = new Set();
98+
_completed: boolean = false;
6599
}

deno/idb_test.ts

+26-9
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
it,
1414
} from "@std/testing/bdd";
1515
import type {
16-
IDBFactory,
1716
IDBDatabase,
17+
IDBFactory,
1818
IDBIndex,
1919
IDBObjectStore,
2020
IDBOpenDBRequest,
@@ -173,11 +173,13 @@ describe('IDBObjectStore<"readonly">', () => {
173173

174174
beforeAll(async () => {
175175
const request = self.indexedDB.open(name);
176-
await new Promise((resolve) => {
177-
request.onupgradeneeded = (event) =>
178-
resolve(
179-
event.target.result.createObjectStore("events", { keyPath: "id" }),
180-
);
176+
await new Promise<void>((resolve) => {
177+
request.onupgradeneeded = (event) => {
178+
const db = event.target.result;
179+
const store = db.createObjectStore("events", { keyPath: "id" });
180+
store.add({ id: 1, pubkey: "pubkey" });
181+
resolve();
182+
};
181183
});
182184
await ensure(request, "success");
183185
store = request.result.transaction("events", "readonly").objectStore(
@@ -213,6 +215,14 @@ describe('IDBObjectStore<"readonly">', () => {
213215
);
214216
});
215217
});
218+
219+
describe("get", () => {
220+
it("should get an object", async () => {
221+
const request = store.get(1);
222+
await ensure(store.transaction, "complete");
223+
assertEquals(request.result, { id: 1, pubkey: "pubkey" });
224+
});
225+
});
216226
});
217227

218228
describe('IDBObjectStore<"readwrite">', () => {
@@ -228,9 +238,9 @@ describe('IDBObjectStore<"readwrite">', () => {
228238
);
229239
});
230240
await ensure(request, "success");
231-
store = request.result.transaction("events", "readwrite").objectStore(
232-
"events",
233-
);
241+
store = request.result
242+
.transaction("events", "readwrite")
243+
.objectStore("events");
234244
});
235245

236246
afterAll(async () => {
@@ -265,6 +275,13 @@ describe('IDBObjectStore<"readwrite">', () => {
265275
);
266276
});
267277
});
278+
279+
describe("get", () => {
280+
it("should throw a TransactionInactiveError once the transaction is completed", async () => {
281+
await ensure(store.transaction, "complete");
282+
assertThrows(() => store.get(1));
283+
});
284+
});
268285
});
269286

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

0 commit comments

Comments
 (0)