feat(fs,fs-lite): add dataSuffix option to prevent file/directory key collisions#767
feat(fs,fs-lite): add dataSuffix option to prevent file/directory key collisions#767
Conversation
… collisions When both `foo` and `foo:bar` exist as storage keys, the fs drivers try to create a file at `<base>/foo` and a directory at `<base>/foo/`, causing ENOTDIR errors. Add a `dataSuffix` option (e.g. `".data"`) that appends a suffix to all stored file paths on disk. With `dataSuffix: ".data"`, key `foo` is stored as `foo.data` and key `foo:bar` as `foo/bar.data`, eliminating the collision. The suffix is transparently stripped from keys returned by `getKeys()` and watch callbacks. The option defaults to `undefined` (no suffix) for full backward compatibility -- consumers like Nitro can opt in by setting it.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughFilesystem drivers fs and fs-lite gain an optional Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant FSDriver as "FS Driver\n(r / rFile)"
participant Disk as "Filesystem"
participant Watcher as "Chokidar Watcher"
Client->>FSDriver: setItem(key, value)
FSDriver->>FSDriver: r(key) -> rFile(key) (append dataSuffix)
FSDriver->>Disk: write rFile(key)
Disk-->>FSDriver: write OK
FSDriver-->>Client: ack
Disk-->>Watcher: fs event (rFile path)
Watcher->>FSDriver: event relPath
Note right of FSDriver: strip dataSuffix from relPath
FSDriver->>Client: update(key) / remove(key) callback with logical key
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
test/drivers/fs.test.ts (1)
115-121: Consider reusing the storage instance from the test.The
afterEachhook creates a new storage instance for cleanup. Since each test already creates and disposes its own storage instance, you could track the storage instance at the describe level and reuse it for cleanup.♻️ Suggested refactor
describe("dataSuffix option", () => { const suffixDir = resolve(__dirname, "tmp/fs-suffix"); + let storage: ReturnType<typeof createStorage>; afterEach(async () => { - const s = createStorage({ - driver: driver({ base: suffixDir, dataSuffix: ".data" }), - }); - await s.clear(); - await s.dispose(); + if (storage) { + await storage.clear(); + await storage.dispose(); + } }); it("prevents file/directory collision with dataSuffix", async () => { const d = driver({ base: suffixDir, dataSuffix: ".data" }); - const storage = createStorage({ driver: d }); + storage = createStorage({ driver: d }); // ... rest of test without the dispose call at the end🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/drivers/fs.test.ts` around lines 115 - 121, The afterEach currently creates a new storage with createStorage/driver to clean up; instead declare a describe-scoped variable (e.g., let storage = null) and have each test assign its storage instance to that variable when it creates one, then update afterEach to check that variable and call storage.clear() and storage.dispose() if present (and reset it to null); update references in afterEach from the temporary s to this describe-scoped storage and ensure tests still dispose or let afterEach handle disposal consistently (methods: createStorage, driver, afterEach, clear, dispose).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@test/drivers/fs.test.ts`:
- Around line 115-121: The afterEach currently creates a new storage with
createStorage/driver to clean up; instead declare a describe-scoped variable
(e.g., let storage = null) and have each test assign its storage instance to
that variable when it creates one, then update afterEach to check that variable
and call storage.clear() and storage.dispose() if present (and reset it to
null); update references in afterEach from the temporary s to this
describe-scoped storage and ensure tests still dispose or let afterEach handle
disposal consistently (methods: createStorage, driver, afterEach, clear,
dispose).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 40e828ed-5314-4a62-93d0-75b0b27886e2
📒 Files selected for processing (4)
src/drivers/fs-lite.tssrc/drivers/fs.tstest/drivers/fs-lite.test.tstest/drivers/fs.test.ts
…ffix is set getKeys was using map (passing through non-suffixed files as phantom keys) instead of filter+map. Watcher was emitting events for non-suffixed files. Both now ignore files that don't end with the configured dataSuffix.
Summary
When both
fooandfoo:barexist as storage keys, thefsandfs-litedrivers map them to<base>/foo(file) and<base>/foo/bar(inside directoryfoo/). This creates an ENOTDIR collision becausefoocannot be both a file and a directory on disk.This PR adds a
dataSuffixoption to both drivers. When set (e.g.dataSuffix: ".data"):foois stored asfoo.dataon diskfoo:baris stored asfoo/bar.dataon diskfoo.datais a file,foo/is a directoryThe suffix is transparently stripped from keys returned by
getKeys()and watch callbacks. The option defaults toundefinedfor full backward compatibility.Context
This addresses the root cause of file/directory collisions reported in:
Per pi0's feedback, the fix belongs in the unstorage fs driver rather than in Nitro's cache key sanitization.
Changes
src/drivers/fs.ts-- AddeddataSuffixoption, splitr()intor()(directory resolution) andrFile()(file resolution with suffix), updated all file operations to userFile(), strip suffix ingetKeys()and watch callbackssrc/drivers/fs-lite.ts-- Same changes asfs.tstest/drivers/fs.test.ts-- Added test verifyingfoo+foo:barcoexistence withdataSuffix, and basic CRUD with suffixtest/drivers/fs-lite.test.ts-- Same tests asfs.test.tsUsage
Summary by CodeRabbit
New Features
Tests