storage/basic.ts

99 lines
2.6 KiB
TypeScript
Raw Normal View History

2021-06-21 22:05:08 +00:00
import { uuid1 } from "./deps.ts";
/**
* Standard interface for the simplest key-value store
*/
export interface KeyValueStorage {
2021-06-21 22:05:08 +00:00
get(key: string): Promise<string | null>;
set(key: string, value: string | null): Promise<void>;
}
/**
* Basic memory implementation
*/
export class MemoryStorage implements KeyValueStorage {
2021-06-21 22:05:08 +00:00
private store: { [key: string]: string } = {};
async get(key: string): Promise<string | null> {
const result = this.store[key];
return (typeof result == "undefined") ? null : result;
}
async set(key: string, value: string | null): Promise<void> {
if (value === null) {
delete this.store[key];
} else {
this.store[key] = value;
}
}
}
2021-06-21 22:05:08 +00:00
type StorageDelegate = KeyValueStorage | (() => KeyValueStorage);
function fromDelegate(delegate: StorageDelegate): KeyValueStorage {
return (typeof delegate === "function") ? delegate() : delegate;
}
/**
2019-11-07 21:33:23 +00:00
* Wrap a storage, scoping the keys using a suffix
*/
export class ScopedStorage implements KeyValueStorage {
2021-06-21 22:05:08 +00:00
private readonly target: KeyValueStorage;
2019-11-07 21:33:23 +00:00
constructor(target: StorageDelegate, private readonly suffix: string) {
this.target = fromDelegate(target);
}
async get(key: string): Promise<string | null> {
return await this.target.get(`${key}#${this.suffix}`);
}
async set(key: string, value: string | null): Promise<void> {
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)
*/
2019-11-07 21:33:23 +00:00
export class RefScopedStorage implements KeyValueStorage {
2021-06-21 22:05:08 +00:00
private readonly reference: KeyValueStorage;
private readonly target: KeyValueStorage;
private inner?: KeyValueStorage;
constructor(reference: StorageDelegate, target: StorageDelegate) {
this.reference = fromDelegate(reference);
this.target = fromDelegate(target);
}
2019-11-07 21:33:23 +00:00
private async init(): Promise<KeyValueStorage> {
const refkey = "tk-storage-scope-ref";
const suffix = await this.reference.get(refkey);
if (suffix) {
2019-11-07 21:33:23 +00:00
return new ScopedStorage(this.target, suffix);
} else {
2021-06-21 22:05:08 +00:00
const suffix = uuid1.generate().toString();
await this.reference.set(refkey, suffix);
2019-11-07 21:33:23 +00:00
return new ScopedStorage(this.target, suffix);
}
}
async get(key: string): Promise<string | null> {
2019-11-07 21:33:23 +00:00
if (!this.inner) {
this.inner = await this.init();
}
2019-11-07 21:33:23 +00:00
return await this.inner.get(key);
}
async set(key: string, value: string | null): Promise<void> {
2019-11-07 21:33:23 +00:00
if (!this.inner) {
this.inner = await this.init();
}
2019-11-07 21:33:23 +00:00
return await this.inner.set(key, value);
}
}