import { uuid1 } from "./deps.ts"; /** * Standard interface for the simplest key-value store */ export interface KeyValueStorage { get(key: string): Promise; set(key: string, value: string | null): Promise; } /** * Basic memory implementation */ export class MemoryStorage implements KeyValueStorage { private store: { [key: string]: string } = {}; async get(key: string): Promise { const result = this.store[key]; return (typeof result == "undefined") ? null : result; } async set(key: string, value: string | null): Promise { if (value === null) { delete this.store[key]; } else { this.store[key] = value; } } } type StorageDelegate = KeyValueStorage | (() => KeyValueStorage); function fromDelegate(delegate: StorageDelegate): KeyValueStorage { return (typeof delegate === "function") ? delegate() : delegate; } /** * Wrap a storage, scoping the keys using a suffix */ export class ScopedStorage implements KeyValueStorage { private readonly target: KeyValueStorage; constructor(target: StorageDelegate, private readonly suffix: string) { this.target = fromDelegate(target); } async get(key: string): Promise { return await this.target.get(`${key}#${this.suffix}`); } async set(key: string, value: string | null): Promise { return await this.target.set(`${key}#${this.suffix}`, value); } } /** * Use a target unscoped storage, scoping the keys in a virtual random namespace * * The namespace is persisted in a reference storage (used unscoped) */ export class RefScopedStorage implements KeyValueStorage { private readonly reference: KeyValueStorage; private readonly target: KeyValueStorage; private inner?: KeyValueStorage; constructor(reference: StorageDelegate, target: StorageDelegate) { this.reference = fromDelegate(reference); this.target = fromDelegate(target); } private async init(): Promise { const refkey = "tk-storage-scope-ref"; const suffix = await this.reference.get(refkey); if (suffix) { return new ScopedStorage(this.target, suffix); } else { const suffix = uuid1.generate().toString(); await this.reference.set(refkey, suffix); return new ScopedStorage(this.target, suffix); } } async get(key: string): Promise { if (!this.inner) { this.inner = await this.init(); } return await this.inner.get(key); } async set(key: string, value: string | null): Promise { if (!this.inner) { this.inner = await this.init(); } return await this.inner.set(key, value); } }