/** * @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 per `BR-BRAINY-80-STORAGE-SIMPLIFY`. 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 { isBrowser } from '../utils/environment.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 } /** Branch name for COW storage (filesystem only). */ branch?: string /** Whether to enable COW blob compression. Default `true`. */ enableCompression?: boolean /** Operation tuning (timeouts, retry budgets) passed through to the adapter. */ operationConfig?: OperationConfig } /** * Attach the COW (copy-on-write) options to the adapter so the storage's * branch + compression settings are honored when the adapter's * `initializeCOW()` hook fires later in `brain.init()`. */ function configureCOW(storage: any, options?: StorageOptions): void { if (typeof storage?.initializeCOW === 'function') { storage._cowOptions = { branch: options?.branch || 'main', enableCompression: options?.enableCompression !== false } } } /** * Resolve `StorageOptions` to a concrete storage adapter. * * - `'auto'` (default) picks `FileSystemStorage` when the runtime exposes * the Node `fs` API, otherwise falls back to `MemoryStorage`. * - `forceMemoryStorage: true` returns `MemoryStorage` regardless. * - `forceFileSystemStorage: true` returns `FileSystemStorage` and throws in * the browser. */ export async function createStorage(options: StorageOptions = {}): Promise { const storage = await pickAdapter(options) configureCOW(storage, options) return storage } 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) { if (isBrowser()) { throw new Error( "Brainy: 'filesystem' storage is not available in browser environments. " + "Use `type: 'memory'` for in-browser brains, or run on a Node-like runtime." ) } return await createFilesystemStorage(options) } // 'auto': filesystem if we have Node-like fs, otherwise memory. if (!isBrowser()) { try { return await createFilesystemStorage(options) } catch (err) { // Fall back to memory if filesystem init fails for any reason // (e.g. no write permission on rootDirectory). return new MemoryStorage() } } 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'