/** * @module storage/storageFactory * @description Storage adapter factory for Brainy 8.0. * * Brainy 8.0 ships **two storage adapters**: * - `'filesystem'` (default) — Node.js + Bun + Deno; persistent on disk. * - `'memory'` — in-memory; ephemeral; the right choice for tests + ephemeral * workloads. * * Cloud-storage adapters (GCS, S3, R2, Azure) and the browser-only OPFS * adapter were removed in 8.0. The path * forward for cloud backup is operator tooling: persist locally with * `db.persist()`, sync the resulting on-disk artefact with `gsutil` / * `aws s3 cp` / `rclone` / `azcopy`. This is the standard pattern every * production database uses; bundling cloud SDKs into the library buys * nothing and ships ~13 K LOC of code we don't maintain well. */ import type { StorageAdapter } from '../coreTypes.js' import { MemoryStorage } from './adapters/memoryStorage.js' import type { OperationConfig } from '../utils/operationUtils.js' /** * Options for creating a storage adapter (Brainy 8.0). */ export interface StorageOptions { /** * Storage backend to use. * - `'auto'` (default) — `'filesystem'` on Node-like runtimes, `'memory'` * in a browser. * - `'memory'` — in-memory; ephemeral. * - `'filesystem'` — persistent disk storage; Node-like runtimes only. */ type?: 'auto' | 'memory' | 'filesystem' /** Force memory storage regardless of environment. */ forceMemoryStorage?: boolean /** Force filesystem storage. Throws in a browser environment. */ forceFileSystemStorage?: boolean /** Root directory for filesystem storage. */ rootDirectory?: string /** * Nested options block (backward-compat for `BrainyConfig.storage.options`). * Recognized keys: `rootDirectory`, `path`. */ options?: { rootDirectory?: string path?: string [key: string]: any } /** Operation tuning (timeouts, retry budgets) passed through to the adapter. */ operationConfig?: OperationConfig } /** * Resolve `StorageOptions` to a concrete storage adapter. * * - `'auto'` (default) picks `FileSystemStorage`, falling back to * `MemoryStorage` only if filesystem init fails (e.g. no write permission). * - `forceMemoryStorage: true` returns `MemoryStorage` regardless. * - `forceFileSystemStorage: true` returns `FileSystemStorage`. */ export async function createStorage(options: StorageOptions = {}): Promise { return pickAdapter(options) } async function pickAdapter(options: StorageOptions): Promise { if (options.forceMemoryStorage) { return new MemoryStorage() } const requestedType = options.type ?? 'auto' if (requestedType === 'memory') { return new MemoryStorage() } if (requestedType === 'filesystem' || options.forceFileSystemStorage) { return await createFilesystemStorage(options) } // 'auto': prefer filesystem, fall back to memory if init fails. try { return await createFilesystemStorage(options) } catch { return new MemoryStorage() } } async function createFilesystemStorage(options: StorageOptions): Promise { const rootDir = options.rootDirectory ?? options.options?.rootDirectory ?? options.options?.path ?? './brainy-data' // Dynamic import so browser bundles don't pull node:fs. const { FileSystemStorage } = await import('./adapters/fileSystemStorage.js') return new FileSystemStorage(rootDir) as unknown as StorageAdapter } // Re-export the surviving adapters for direct construction by callers // that want to skip the factory. export { MemoryStorage } from './adapters/memoryStorage.js'