
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
storage-watcher
Advanced tools
Zero-dependency reactive watcher for localStorage & sessionStorage changes. Detect key mutations, watch future keys, and get notified of new key creation — all within the same tab.
A zero-dependency, lightweight npm package that lets you subscribe to localStorage and sessionStorage changes in real-time within the same browser tab.
Normally, there's no way to know when a value in storage changes within the same tab — the browser's native storage event only fires in other tabs. This package solves that by intercepting storage write operations and notifying your callbacks instantly.
npm install storage-watcher
import { StorageWatcher } from 'storage-watcher';
const watcher = new StorageWatcher();
// Watch a specific key
const unsub = watcher.on('user-token', (newValue, oldValue) => {
console.log(`Token changed: ${oldValue} → ${newValue}`);
});
// Later, when done watching
unsub();
// Clean up everything
watcher.destroy();
new StorageWatcher(options?)Creates a new watcher instance.
| Option | Type | Default | Description |
|---|---|---|---|
storageType | 'localStorage' | 'sessionStorage' | 'localStorage' | Which storage backend to watch |
// Watch localStorage (default)
const watcher = new StorageWatcher();
// Watch sessionStorage
const sessionWatcher = new StorageWatcher({ storageType: 'sessionStorage' });
watcher.on(key, callback) → UnsubscribeWatch a specific key for changes. Works even if the key doesn't exist yet — the callback fires the moment the key is created.
// Watch an existing key
watcher.on('theme', (newValue, oldValue) => {
console.log(`Theme changed from ${oldValue} to ${newValue}`);
});
// Watch a key that doesn't exist yet (future key)
watcher.on('future-config', (newValue, oldValue) => {
// oldValue will be null (key didn't exist before)
console.log(`Config was created with value: ${newValue}`);
});
Callback signature: (newValue: string | null, oldValue: string | null) => void
newValue is null when the key is removed or clearedoldValue is null when the key is newly createdwatcher.onAny(callback) → UnsubscribeWatch all key changes across the storage.
watcher.onAny((key, newValue, oldValue) => {
console.log(`${key} changed: ${oldValue} → ${newValue}`);
});
Callback signature: (key: string, newValue: string | null, oldValue: string | null) => void
watcher.onNew(callback) → UnsubscribeWatch for brand-new keys being added to storage. Fires only when a key that didn't previously exist is created via setItem().
watcher.onNew((key, value) => {
console.log(`New key "${key}" created with value: ${value}`);
});
localStorage.setItem('brand-new', 'hello'); // ✅ fires (new key)
localStorage.setItem('brand-new', 'world'); // ❌ doesn't fire (key already exists)
localStorage.removeItem('brand-new');
localStorage.setItem('brand-new', 'again'); // ✅ fires (key was removed, now re-created)
Callback signature: (key: string, value: string) => void
watcher.destroy()Destroy the watcher instance. Removes all listeners and cleans up internal patches. After calling destroy(), any calls to on(), onAny(), or onNew() will throw an AlreadyDestroyedError.
watcher.destroy();
This method is idempotent — calling it multiple times is safe.
All watch methods return an unsubscribe function:
const unsub = watcher.on('key', callback);
// Stop watching this specific callback
unsub();
// Calling again is safe (idempotent)
unsub();
The package monkey-patches Storage.prototype.setItem, .removeItem, and .clear to intercept mutations. Key details:
StorageWatcher instance is created and restored when the last instance is destroyedclear() handling — when clear() is called, individual events fire for each key that existed, so per-key watchers work correctly| Storage Operation | on(key) | onAny() | onNew() |
|---|---|---|---|
setItem(key, value) — new key | ✅ | ✅ | ✅ |
setItem(key, value) — existing key | ✅ | ✅ | ❌ |
removeItem(key) | ✅ | ✅ | ❌ |
clear() | ✅ (per key) | ✅ (per key) | ❌ |
| Error | When |
|---|---|
StorageUnavailableError | Storage backend is not available (e.g., private browsing) |
AlreadyDestroyedError | Method called on a destroyed instance |
Both extend StorageWatcherError, which extends Error.
import { StorageWatcherError, AlreadyDestroyedError } from 'storage-watcher';
try {
watcher.on('key', cb);
} catch (err) {
if (err instanceof AlreadyDestroyedError) {
// handle destroyed instance
}
}
Full TypeScript support with exported types:
import type {
StorageWatcherOptions,
StorageType,
WatchCallback,
WatchAnyCallback,
WatchNewCallback,
Unsubscribe,
} from 'storage-watcher';
MIT
FAQs
Zero-dependency reactive watcher for localStorage & sessionStorage changes. Detect key mutations, watch future keys, and get notified of new key creation — all within the same tab.
We found that storage-watcher demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.