import { assertValidPath, assertValidPrefix, type BucketStore, type StoreEntry } from './store'; /** * Confines another store to `/`, 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 { return this.inner.get(this.full(path)); } async put(path: string, bytes: Uint8Array, contentType?: string): Promise { return this.inner.put(this.full(path), bytes, contentType); } async append(path: string, bytes: Uint8Array): Promise { return this.inner.append(this.full(path), bytes); } async list(prefix: string): Promise { 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 { return this.inner.delete(paths.map((p) => this.full(p))); } async copy(src: string, dst: string): Promise { 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 { const entries = await this.inner.list(this.prefix); await this.inner.delete(entries.map((e) => e.path)); return entries.length; } }