Skip to content

fix(utils): prefix watch callback strips prefix and filters external events#785

Open
LeSingh1 wants to merge 1 commit into
unjs:mainfrom
LeSingh1:fix/prefix-storage-watch
Open

fix(utils): prefix watch callback strips prefix and filters external events#785
LeSingh1 wants to merge 1 commit into
unjs:mainfrom
LeSingh1:fix/prefix-storage-watch

Conversation

@LeSingh1

@LeSingh1 LeSingh1 commented Jun 12, 2026

Copy link
Copy Markdown

When you call watch() on a prefixStorage instance, two things go wrong today:

  1. The callback receives the full key including the namespace prefix (e.g. "ns:foo" instead of "foo"), which is inconsistent with every other method on the prefixed storage.
  2. Events for keys outside the prefix are delivered to the callback even though the caller only asked to watch a subset of the storage.

The root cause is that prefixStorage spreads the underlying storage object and then overrides individual methods, but watch was never overridden. Because of this, nsStorage.watch is just storage.watch -- the full-store watcher with no filtering or key transformation.

The fix adds a watch override on the prefixed storage that wraps the caller's callback. The wrapper checks whether each incoming key starts with the base prefix, drops the event if it does not, and strips the prefix from the key before invoking the callback. This makes watch consistent with how getKeys, getItem, and the other prefixed methods already behave.

A regression test covers both the key-stripping and the event-filtering behaviour.

Summary by CodeRabbit

  • New Features
    • Prefixed storage namespaces now support reactive change watching. Watch callbacks automatically filter events to the namespace scope and strip the prefix from keys for cleaner application integration.

…events

When calling watch() on a prefixStorage, the callback received full
storage keys (including the prefix) and was triggered for events on
keys outside the namespace. Override watch on the prefixed storage
so the callback only fires for matching keys and receives the key
relative to the prefix, consistent with how getKeys and getItem work.
@LeSingh1 LeSingh1 requested a review from pi0 as a code owner June 12, 2026 06:33
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds watch event support to prefixStorage by implementing a watch method that wraps the underlying storage's watch, filters incoming events to the prefixed namespace, and strips the prefix from keys before delivering them to the caller's callback. A test case validates the filtering and prefix-stripping behavior.

Changes

Watch support in prefixed storage

Layer / File(s) Summary
Watch implementation in prefixStorage
src/utils.ts
prefixStorage assigns nsStorage.watch to delegate to storage.watch, filter events matching the computed base prefix, and map watched keys by removing the prefix before invoking the provided callback.
Watch behavior test
test/storage.test.ts
Test case verifies that prefixed storage watch callbacks receive only events for keys within the namespace with the prefix stripped, and events outside the namespace are filtered out.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A watcher in prefix's nest,
Filters events, strips names with zest,
Keys arrive trimmed and tidy and true,
Storage dreams wrapped in a namespace view! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: adding a watch callback implementation to prefixStorage that strips the prefix and filters external events, which matches the core fix described in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/storage.test.ts (1)

278-298: 💤 Low value

Consider adding explicit event type and count assertions.

The test correctly validates prefix-stripping and filtering, but could be more thorough:

  1. The assertion on line 295 checks for the presence of the expected event but doesn't verify it's the only event with that key.
  2. The event type ("update") is implicitly checked by toContainEqual, but an explicit assertion would make the test clearer.
  3. Testing "remove" events in addition to "update" would provide better coverage.
📋 Suggested enhancement
  // key delivered to the callback should be relative (no "ns:" prefix)
  expect(events).toContainEqual({ event: "update", key: "foo" });
+ expect(events).toHaveLength(1);
+ expect(events[0].event).toBe("update");
  // events for keys outside the prefix must be filtered out
  expect(events.every((e) => !e.key.startsWith("other"))).toBe(true);

Or add a separate test case for "remove" events:

await pStorage.removeItem("foo");
await new Promise((resolve) => setTimeout(resolve, 10));
expect(events).toContainEqual({ event: "remove", key: "foo" });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/storage.test.ts` around lines 278 - 298, The test for prefixStorage
should assert explicit event types and counts: after calling
pStorage.setItem("foo", "bar") and waiting, assert that events includes an
{event: "update", key: "foo"} and that there is exactly one update event for key
"foo" (e.g. filter events by key === "foo" and assert count), then call await
pStorage.removeItem("foo"), wait again, and assert events includes an {event:
"remove", key: "foo"}; use the existing symbols pStorage.watch,
pStorage.setItem, pStorage.removeItem, storage.setItem and the events array to
locate and change the assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/storage.test.ts`:
- Around line 278-298: The test for prefixStorage should assert explicit event
types and counts: after calling pStorage.setItem("foo", "bar") and waiting,
assert that events includes an {event: "update", key: "foo"} and that there is
exactly one update event for key "foo" (e.g. filter events by key === "foo" and
assert count), then call await pStorage.removeItem("foo"), wait again, and
assert events includes an {event: "remove", key: "foo"}; use the existing
symbols pStorage.watch, pStorage.setItem, pStorage.removeItem, storage.setItem
and the events array to locate and change the assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a543675-a4c6-4d42-b245-22efd60a06bd

📥 Commits

Reviewing files that changed from the base of the PR and between 2727956 and 6759adf.

📒 Files selected for processing (2)
  • src/utils.ts
  • test/storage.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant