diff --git a/src/storage/storageFactory.ts b/src/storage/storageFactory.ts index 88abf590..c001c382 100644 --- a/src/storage/storageFactory.ts +++ b/src/storage/storageFactory.ts @@ -42,6 +42,14 @@ export interface StorageOptions { /** Root directory for filesystem storage. */ rootDirectory?: string + /** + * Alias for `rootDirectory`. The `storage: { type: 'filesystem', path: '…' }` + * shape is widely used (and shown throughout the docs), so it is honored as a + * first-class top-level key — not silently ignored in favour of the default + * directory. + */ + path?: string + /** * Nested options block (backward-compat for `BrainyConfig.storage.options`). * Recognized keys: `rootDirectory`, `path`. @@ -94,6 +102,7 @@ async function pickAdapter(options: StorageOptions): Promise { async function createFilesystemStorage(options: StorageOptions): Promise { const rootDir = options.rootDirectory ?? + options.path ?? options.options?.rootDirectory ?? options.options?.path ?? './brainy-data' diff --git a/src/types/brainy.types.ts b/src/types/brainy.types.ts index 15d4072f..b0ba5978 100644 --- a/src/types/brainy.types.ts +++ b/src/types/brainy.types.ts @@ -1202,6 +1202,9 @@ export interface BrainyConfig { /** Root directory for filesystem storage. Passed through to storage factories * including plugin-provided factories (e.g. native mmap providers). */ rootDirectory?: string + /** Alias for `rootDirectory`. The `storage: { type: 'filesystem', path: '…' }` + * shape is honored as a first-class key so existing configs keep working. */ + path?: string options?: any } | StorageAdapter diff --git a/tests/unit/storage/storage-path-config.test.ts b/tests/unit/storage/storage-path-config.test.ts new file mode 100644 index 00000000..ffbfd94c --- /dev/null +++ b/tests/unit/storage/storage-path-config.test.ts @@ -0,0 +1,53 @@ +/** + * Storage root-directory resolution from `StorageOptions`. + * + * `storage: { type: 'filesystem', path: '…' }` is a widely-used, doc-promoted + * config shape. A refactor once dropped the top-level `path` key from the + * resolution chain, so it was silently ignored and every brain wrote to the + * default `./brainy-data` instead — a quiet data-misplacement footgun on + * upgrade. These tests pin every accepted spelling to the directory it must + * resolve to. + */ +import { describe, it, expect } from 'vitest' +import { createStorage } from '../../../src/storage/storageFactory.js' + +/** Read the resolved root directory off the concrete FileSystemStorage. */ +function rootDirOf(storage: unknown): string { + return (storage as { rootDir: string }).rootDir +} + +describe('createStorage — filesystem root-directory resolution', () => { + it('honors top-level rootDirectory', async () => { + const storage = await createStorage({ type: 'filesystem', rootDirectory: '/tmp/brainy-rd' }) + expect(rootDirOf(storage)).toBe('/tmp/brainy-rd') + }) + + it('honors top-level path (the documented shorthand)', async () => { + const storage = await createStorage({ type: 'filesystem', path: '/tmp/brainy-path' }) + expect(rootDirOf(storage)).toBe('/tmp/brainy-path') + }) + + it('honors nested options.rootDirectory', async () => { + const storage = await createStorage({ type: 'filesystem', options: { rootDirectory: '/tmp/brainy-ord' } }) + expect(rootDirOf(storage)).toBe('/tmp/brainy-ord') + }) + + it('honors nested options.path', async () => { + const storage = await createStorage({ type: 'filesystem', options: { path: '/tmp/brainy-opath' } }) + expect(rootDirOf(storage)).toBe('/tmp/brainy-opath') + }) + + it('prefers top-level rootDirectory over a nested options.path', async () => { + const storage = await createStorage({ + type: 'filesystem', + rootDirectory: '/tmp/brainy-win', + options: { path: '/tmp/brainy-lose' } + }) + expect(rootDirOf(storage)).toBe('/tmp/brainy-win') + }) + + it('falls back to ./brainy-data only when no directory is supplied', async () => { + const storage = await createStorage({ type: 'filesystem' }) + expect(rootDirOf(storage)).toBe('./brainy-data') + }) +})