Loading CHANGES.md +4 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,10 @@ To be released. - Implemented `list()` method in `DenoKvStore`. [[#498]] ### @fedify/cfworkers - Implemented `list()` method in `WorkersKvStore`. [[#498]] Version 1.9.2 ------------- Loading packages/cfworkers/src/mod.test.ts +37 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,43 @@ describe("WorkersKvStore", { await store.delete(["foo", "bar"]); strictEqual(await store.get(["foo", "bar"]), undefined); }); test("list()", async (t) => { const { env } = t as unknown as { env: Record<string, KVNamespace<string>>; }; const store = new WorkersKvStore(env.KV1); await store.set(["prefix", "a"], "value-a"); await store.set(["prefix", "b"], "value-b"); await store.set(["prefix", "nested", "c"], "value-c"); await store.set(["other", "x"], "value-x"); const entries: { key: readonly unknown[]; value: unknown }[] = []; for await (const entry of store.list!({ prefix: ["prefix"] })) { entries.push({ key: entry.key, value: entry.value }); } strictEqual(entries.length, 3); }); test("list() - single element key", async (t) => { const { env } = t as unknown as { env: Record<string, KVNamespace<string>>; }; const store = new WorkersKvStore(env.KV1); await store.set(["a"], "value-a"); await store.set(["b"], "value-b"); const entries: { key: readonly unknown[]; value: unknown }[] = []; for await (const entry of store.list!({ prefix: ["a"] })) { entries.push({ key: entry.key, value: entry.value }); } strictEqual(entries.length, 1); strictEqual(entries[0].value, "value-a"); }); }); describe("WorkersMessageQueue", { Loading packages/cfworkers/src/mod.ts +57 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ import type { import type { KvKey, KvStore, KvStoreListEntry, KvStoreListOptions, KvStoreSetOptions, MessageQueue, MessageQueueEnqueueOptions, Loading Loading @@ -83,6 +85,61 @@ export class WorkersKvStore implements KvStore { delete(key: KvKey): Promise<void> { return this.#namespace.delete(this.#encodeKey(key)); } /** * {@inheritDoc KvStore.list} * @since 1.10.0 */ async *list( options: KvStoreListOptions, ): AsyncIterable<KvStoreListEntry> { // Keys are JSON encoded: '["prefix","a"]' // Pattern to match keys starting with prefix: '["prefix",' matches children // Also check for exact match: '["prefix"]' const exactKey = this.#encodeKey(options.prefix); const childrenPattern = JSON.stringify(options.prefix).slice(0, -1) + ","; // First, check if the exact prefix key exists const { value, metadata } = await this.#namespace.getWithMetadata( exactKey, "json", ); if ( value != null && (metadata == null || (metadata as KvMetadata).expires == null || (metadata as KvMetadata).expires! >= Date.now()) ) { yield { key: options.prefix, value, }; } // Then list all keys starting with prefix let cursor: string | undefined; do { const result = await this.#namespace.list<KvMetadata>({ prefix: childrenPattern, cursor, }); cursor = result.list_complete ? undefined : result.cursor; for (const keyInfo of result.keys) { const metadata = keyInfo.metadata as KvMetadata | undefined; if (metadata?.expires != null && metadata.expires < Date.now()) { continue; } const value = await this.#namespace.get(keyInfo.name, "json"); if (value == null) continue; yield { key: JSON.parse(keyInfo.name) as KvKey, value, }; } } while (cursor != null); } } /** Loading Loading
CHANGES.md +4 −0 Original line number Diff line number Diff line Loading @@ -72,6 +72,10 @@ To be released. - Implemented `list()` method in `DenoKvStore`. [[#498]] ### @fedify/cfworkers - Implemented `list()` method in `WorkersKvStore`. [[#498]] Version 1.9.2 ------------- Loading
packages/cfworkers/src/mod.test.ts +37 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,43 @@ describe("WorkersKvStore", { await store.delete(["foo", "bar"]); strictEqual(await store.get(["foo", "bar"]), undefined); }); test("list()", async (t) => { const { env } = t as unknown as { env: Record<string, KVNamespace<string>>; }; const store = new WorkersKvStore(env.KV1); await store.set(["prefix", "a"], "value-a"); await store.set(["prefix", "b"], "value-b"); await store.set(["prefix", "nested", "c"], "value-c"); await store.set(["other", "x"], "value-x"); const entries: { key: readonly unknown[]; value: unknown }[] = []; for await (const entry of store.list!({ prefix: ["prefix"] })) { entries.push({ key: entry.key, value: entry.value }); } strictEqual(entries.length, 3); }); test("list() - single element key", async (t) => { const { env } = t as unknown as { env: Record<string, KVNamespace<string>>; }; const store = new WorkersKvStore(env.KV1); await store.set(["a"], "value-a"); await store.set(["b"], "value-b"); const entries: { key: readonly unknown[]; value: unknown }[] = []; for await (const entry of store.list!({ prefix: ["a"] })) { entries.push({ key: entry.key, value: entry.value }); } strictEqual(entries.length, 1); strictEqual(entries[0].value, "value-a"); }); }); describe("WorkersMessageQueue", { Loading
packages/cfworkers/src/mod.ts +57 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ import type { import type { KvKey, KvStore, KvStoreListEntry, KvStoreListOptions, KvStoreSetOptions, MessageQueue, MessageQueueEnqueueOptions, Loading Loading @@ -83,6 +85,61 @@ export class WorkersKvStore implements KvStore { delete(key: KvKey): Promise<void> { return this.#namespace.delete(this.#encodeKey(key)); } /** * {@inheritDoc KvStore.list} * @since 1.10.0 */ async *list( options: KvStoreListOptions, ): AsyncIterable<KvStoreListEntry> { // Keys are JSON encoded: '["prefix","a"]' // Pattern to match keys starting with prefix: '["prefix",' matches children // Also check for exact match: '["prefix"]' const exactKey = this.#encodeKey(options.prefix); const childrenPattern = JSON.stringify(options.prefix).slice(0, -1) + ","; // First, check if the exact prefix key exists const { value, metadata } = await this.#namespace.getWithMetadata( exactKey, "json", ); if ( value != null && (metadata == null || (metadata as KvMetadata).expires == null || (metadata as KvMetadata).expires! >= Date.now()) ) { yield { key: options.prefix, value, }; } // Then list all keys starting with prefix let cursor: string | undefined; do { const result = await this.#namespace.list<KvMetadata>({ prefix: childrenPattern, cursor, }); cursor = result.list_complete ? undefined : result.cursor; for (const keyInfo of result.keys) { const metadata = keyInfo.metadata as KvMetadata | undefined; if (metadata?.expires != null && metadata.expires < Date.now()) { continue; } const value = await this.#namespace.get(keyInfo.name, "json"); if (value == null) continue; yield { key: JSON.parse(keyInfo.name) as KvKey, value, }; } } while (cursor != null); } } /** Loading