|
| 1 | +import type { ActorDriver } from "../driver"; |
| 2 | + |
1 | 3 | export const KEYS = { |
2 | 4 | PERSIST_DATA: Uint8Array.from([1]), |
3 | 5 | CONN_PREFIX: Uint8Array.from([2]), // Prefix for connection keys |
4 | 6 | INSPECTOR_TOKEN: Uint8Array.from([3]), // Inspector token key |
| 7 | + KV: Uint8Array.from([4]), // Prefix for user-facing KV storage |
5 | 8 | }; |
6 | 9 |
|
| 10 | +// Helper to create a prefixed key for user-facing KV storage |
| 11 | +function makePrefixedKey(key: Uint8Array): Uint8Array { |
| 12 | + const prefixed = new Uint8Array(KEYS.KV.length + key.length); |
| 13 | + prefixed.set(KEYS.KV, 0); |
| 14 | + prefixed.set(key, KEYS.KV.length); |
| 15 | + return prefixed; |
| 16 | +} |
| 17 | + |
| 18 | +// Helper to remove the prefix from a key |
| 19 | +function removePrefixFromKey(prefixedKey: Uint8Array): Uint8Array { |
| 20 | + return prefixedKey.slice(KEYS.KV.length); |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * User-facing KV storage interface exposed on ActorContext. |
| 25 | + */ |
| 26 | +export class ActorKv { |
| 27 | + #driver: ActorDriver; |
| 28 | + #actorId: string; |
| 29 | + |
| 30 | + constructor(driver: ActorDriver, actorId: string) { |
| 31 | + this.#driver = driver; |
| 32 | + this.#actorId = actorId; |
| 33 | + } |
| 34 | + |
| 35 | + /** |
| 36 | + * Get a single value by key. |
| 37 | + */ |
| 38 | + async get(key: Uint8Array): Promise<Uint8Array | null> { |
| 39 | + const results = await this.#driver.kvBatchGet(this.#actorId, [ |
| 40 | + makePrefixedKey(key), |
| 41 | + ]); |
| 42 | + return results[0] ?? null; |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * Get multiple values by keys. |
| 47 | + */ |
| 48 | + async getBatch(keys: Uint8Array[]): Promise<(Uint8Array | null)[]> { |
| 49 | + const prefixedKeys = keys.map(makePrefixedKey); |
| 50 | + return this.#driver.kvBatchGet(this.#actorId, prefixedKeys); |
| 51 | + } |
| 52 | + |
| 53 | + /** |
| 54 | + * Put a single key-value pair. |
| 55 | + */ |
| 56 | + async put(key: Uint8Array, value: Uint8Array): Promise<void> { |
| 57 | + await this.#driver.kvBatchPut(this.#actorId, [ |
| 58 | + [makePrefixedKey(key), value], |
| 59 | + ]); |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Put multiple key-value pairs. |
| 64 | + */ |
| 65 | + async putBatch(entries: [Uint8Array, Uint8Array][]): Promise<void> { |
| 66 | + const prefixedEntries: [Uint8Array, Uint8Array][] = entries.map( |
| 67 | + ([key, value]) => [makePrefixedKey(key), value], |
| 68 | + ); |
| 69 | + await this.#driver.kvBatchPut(this.#actorId, prefixedEntries); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Delete a single key. |
| 74 | + */ |
| 75 | + async delete(key: Uint8Array): Promise<void> { |
| 76 | + await this.#driver.kvBatchDelete(this.#actorId, [makePrefixedKey(key)]); |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + * Delete multiple keys. |
| 81 | + */ |
| 82 | + async deleteBatch(keys: Uint8Array[]): Promise<void> { |
| 83 | + const prefixedKeys = keys.map(makePrefixedKey); |
| 84 | + await this.#driver.kvBatchDelete(this.#actorId, prefixedKeys); |
| 85 | + } |
| 86 | + |
| 87 | + /** |
| 88 | + * List all keys with a given prefix. |
| 89 | + * Returns key-value pairs where keys have the user prefix removed. |
| 90 | + */ |
| 91 | + async list(prefix: Uint8Array): Promise<[Uint8Array, Uint8Array][]> { |
| 92 | + const prefixedPrefix = makePrefixedKey(prefix); |
| 93 | + const results = await this.#driver.kvListPrefix( |
| 94 | + this.#actorId, |
| 95 | + prefixedPrefix, |
| 96 | + ); |
| 97 | + return results.map(([key, value]) => [removePrefixFromKey(key), value]); |
| 98 | + } |
| 99 | +} |
| 100 | + |
7 | 101 | // Helper to create a connection key |
8 | 102 | export function makeConnKey(connId: string): Uint8Array { |
9 | 103 | const encoder = new TextEncoder(); |
|
0 commit comments