diff --git a/plugos/lib/kv_primitives.ts b/plugos/lib/kv_primitives.ts index 488ddb4d..81a85cc3 100644 --- a/plugos/lib/kv_primitives.ts +++ b/plugos/lib/kv_primitives.ts @@ -11,46 +11,3 @@ export interface KvPrimitives { query(options: KvQueryOptions): AsyncIterableIterator; close(): void; } - -/** - * Turns any KvPrimitives into a KvPrimitives that automatically prefixes all keys (and removes them again when reading) - */ -export class PrefixedKvPrimitives implements KvPrimitives { - constructor(private wrapped: KvPrimitives, private prefix: KvKey) { - } - - batchGet(keys: KvKey[]): Promise { - return this.wrapped.batchGet(keys.map((key) => this.applyPrefix(key))); - } - - batchSet(entries: KV[]): Promise { - return this.wrapped.batchSet( - entries.map(({ key, value }) => ({ key: this.applyPrefix(key), value })), - ); - } - - batchDelete(keys: KvKey[]): Promise { - return this.wrapped.batchDelete(keys.map((key) => this.applyPrefix(key))); - } - - async *query(options: KvQueryOptions): AsyncIterableIterator { - for await ( - const result of this.wrapped.query({ - prefix: this.applyPrefix(options.prefix), - }) - ) { - yield { key: this.stripPrefix(result.key), value: result.value }; - } - } - close(): void { - this.wrapped.close(); - } - - private applyPrefix(key?: KvKey): KvKey { - return [...this.prefix, ...(key ? key : [])]; - } - - private stripPrefix(key: KvKey): KvKey { - return key.slice(this.prefix.length); - } -} diff --git a/plugos/lib/memory_kv_primitives.test.ts b/plugos/lib/memory_kv_primitives.test.ts new file mode 100644 index 00000000..b9458873 --- /dev/null +++ b/plugos/lib/memory_kv_primitives.test.ts @@ -0,0 +1,8 @@ +import { allTests } from "./kv_primitives.test.ts"; +import { MemoryKvPrimitives } from "./memory_kv_primitives.ts"; + +Deno.test("Test Memory KV Primitives", async () => { + const db = new MemoryKvPrimitives(); + await allTests(db); + db.close(); +}); diff --git a/plugos/lib/memory_kv_primitives.ts b/plugos/lib/memory_kv_primitives.ts new file mode 100644 index 00000000..c06c0378 --- /dev/null +++ b/plugos/lib/memory_kv_primitives.ts @@ -0,0 +1,57 @@ +import { KV, KvKey } from "$sb/types.ts"; +import { KvPrimitives, KvQueryOptions } from "./kv_primitives.ts"; + +const memoryKeySeparator = "\0"; + +export class MemoryKvPrimitives implements KvPrimitives { + protected store = new Map(); + + batchGet(keys: KvKey[]): Promise { + return Promise.resolve( + keys.map((key) => this.store.get(key.join(memoryKeySeparator))), + ); + } + + batchSet(entries: KV[]): Promise { + for (const { key, value } of entries) { + this.store.set(key.join(memoryKeySeparator), value); + } + return Promise.resolve(); + } + + batchDelete(keys: KvKey[]): Promise { + for (const key of keys) { + this.store.delete(key.join(memoryKeySeparator)); + } + return Promise.resolve(); + } + + toJSON(): Record { + const result: Record = {}; + for (const [key, value] of this.store) { + result[key] = value; + } + return result; + } + + static fromJSON(json: Record): MemoryKvPrimitives { + const result = new MemoryKvPrimitives(); + for (const key of Object.keys(json)) { + result.store.set(key, json[key]); + } + return result; + } + + async *query(options: KvQueryOptions): AsyncIterableIterator { + const prefix = options.prefix?.join("/"); + for (const [key, value] of this.store) { + if (prefix && !key.startsWith(prefix)) { + continue; + } + yield { key: key.split(memoryKeySeparator), value }; + } + } + + close(): void { + } +} diff --git a/plugos/lib/prefixed_kv_primitives.ts b/plugos/lib/prefixed_kv_primitives.ts new file mode 100644 index 00000000..e153d066 --- /dev/null +++ b/plugos/lib/prefixed_kv_primitives.ts @@ -0,0 +1,45 @@ +import { KV, KvKey } from "$sb/types.ts"; +import { KvPrimitives, KvQueryOptions } from "./kv_primitives.ts"; + +/** + * Turns any KvPrimitives into a KvPrimitives that automatically prefixes all keys (and removes them again when reading) + */ +export class PrefixedKvPrimitives implements KvPrimitives { + constructor(private wrapped: KvPrimitives, private prefix: KvKey) { + } + + batchGet(keys: KvKey[]): Promise { + return this.wrapped.batchGet(keys.map((key) => this.applyPrefix(key))); + } + + batchSet(entries: KV[]): Promise { + return this.wrapped.batchSet( + entries.map(({ key, value }) => ({ key: this.applyPrefix(key), value })), + ); + } + + batchDelete(keys: KvKey[]): Promise { + return this.wrapped.batchDelete(keys.map((key) => this.applyPrefix(key))); + } + + async *query(options: KvQueryOptions): AsyncIterableIterator { + for await ( + const result of this.wrapped.query({ + prefix: this.applyPrefix(options.prefix), + }) + ) { + yield { key: this.stripPrefix(result.key), value: result.value }; + } + } + close(): void { + this.wrapped.close(); + } + + private applyPrefix(key?: KvKey): KvKey { + return [...this.prefix, ...(key ? key : [])]; + } + + private stripPrefix(key: KvKey): KvKey { + return key.slice(this.prefix.length); + } +}