splitwise / src /lib /server /storage /prefixed-store.ts
assafvayner's picture
assafvayner HF Staff
fix(storage): skip integration suite safely, tighten list contract, drop expand
d594446
Raw
History Blame Contribute Delete
1.74 kB
import { assertValidPath, assertValidPrefix, type BucketStore, type StoreEntry } from './store';
/**
* Confines another store to `<prefix>/`, so several apps (or test runs) can share one
* bucket under different directories. Paths seen by callers never include the prefix.
*/
export class PrefixedStore implements BucketStore {
private readonly prefix: string;
constructor(
private readonly inner: BucketStore,
prefix: string
) {
const trimmed = prefix.replace(/^\/+|\/+$/g, '');
assertValidPath(trimmed);
this.prefix = `${trimmed}/`;
}
private full(path: string): string {
assertValidPath(path);
return this.prefix + path;
}
async get(path: string): Promise<Uint8Array | null> {
return this.inner.get(this.full(path));
}
async put(path: string, bytes: Uint8Array, contentType?: string): Promise<void> {
return this.inner.put(this.full(path), bytes, contentType);
}
async append(path: string, bytes: Uint8Array): Promise<void> {
return this.inner.append(this.full(path), bytes);
}
async list(prefix: string): Promise<StoreEntry[]> {
assertValidPrefix(prefix);
const entries = await this.inner.list(this.prefix + prefix);
return entries.map((e) => ({ ...e, path: e.path.slice(this.prefix.length) }));
}
async delete(paths: string[]): Promise<void> {
return this.inner.delete(paths.map((p) => this.full(p)));
}
async copy(src: string, dst: string): Promise<void> {
return this.inner.copy(this.full(src), this.full(dst));
}
/** Deletes every object under this store's prefix. Returns how many were deleted. */
async wipe(): Promise<number> {
const entries = await this.inner.list(this.prefix);
await this.inner.delete(entries.map((e) => e.path));
return entries.length;
}
}