Description
An audit of the useStorage composable has revealed several critical issues related to data integrity, reference safety, and API semantics that could lead to bugs in production environments.
Critical Issues (P1)
1. Shared Reference Mutation in defaultValue
When get(key, defaultValue) is called and the key is absent in storage, the defaultValue is assigned directly to the reactive ref. If defaultValue is an object or array, any mutation to ref.value will mutate the original defaultValue object.
- Impact: Multiple components using the same default object for different keys will inadvertently share state.
- Recommended Fix: Implement protective cloning of
defaultValue when initializing the ref.
const defaults = { theme: 'light', flags: [] }
const a = storage.get('a', defaults)
const b = storage.get('b', defaults)
a.value.theme = 'dark'
2. Data Loss for Empty String Values
In readStored, the check if (!raw) return undefined incorrectly treats an empty string "" as an absent value.
- Impact: Legitimate empty string values stored by custom serializers are ignored and replaced by the default value.
- Recommended Fix: Change the check to
if (raw == null).
Semantic Issues (P2)
3. Inconsistent has/remove/clear Behavior
Current implementations of has, remove, and clear only interact with the local cache (keys already accessed via get) rather than the underlying adapter storage.
- Example:
storage.has('key') returns false even if the key exists in localStorage, provided it hasn't been accessed in the current session yet.
- Impact: Unpredictable behavior for developers expecting standard storage-like semantics.
- Recommended Fix: Align these methods with the adapter's physical state or rename them to reflect their cache-only scope.
4. TTL Object Wrapping Transparency
When ttl is enabled, values are wrapped in an internal { __ttl, __v, __t } structure before being passed to the serializer.write method.
- Impact: Custom serializers might fail if they expect the raw value type.
- Recommended Fix: Either unwrap the value before serialization or explicitly document this internal structure for custom serializer authors.
Environment & Sync Issues (P3)
5. Restricted Cross-Tab Synchronization
Cross-tab sync is hardcoded to work only with window.localStorage and only for already cached keys.
- Impact: No synchronization for custom adapters (e.g., indexedDB-based).
- Recommended Fix: Abstract the sync mechanism into the adapter interface or provide an event-driven sync option.
Proposed Action Plan
- Fix
defaultValue cloning and empty string handling (P1).
- Update
has/remove/clear to check the adapter state (P2).
- Update documentation to clarify TTL wrapping and object support limitations (P2).
- Expand test suite to cover cyclic references and cross-adapter sync edge cases (P3).
Description
An audit of the
useStoragecomposable has revealed several critical issues related to data integrity, reference safety, and API semantics that could lead to bugs in production environments.Critical Issues (P1)
1. Shared Reference Mutation in
defaultValueWhen
get(key, defaultValue)is called and the key is absent in storage, thedefaultValueis assigned directly to the reactiveref. IfdefaultValueis an object or array, any mutation toref.valuewill mutate the originaldefaultValueobject.defaultValuewhen initializing theref.2. Data Loss for Empty String Values
In
readStored, the checkif (!raw) return undefinedincorrectly treats an empty string""as an absent value.if (raw == null).Semantic Issues (P2)
3. Inconsistent
has/remove/clearBehaviorCurrent implementations of
has,remove, andclearonly interact with the localcache(keys already accessed viaget) rather than the underlyingadapterstorage.storage.has('key')returnsfalseeven if the key exists inlocalStorage, provided it hasn't been accessed in the current session yet.4. TTL Object Wrapping Transparency
When
ttlis enabled, values are wrapped in an internal{ __ttl, __v, __t }structure before being passed to theserializer.writemethod.Environment & Sync Issues (P3)
5. Restricted Cross-Tab Synchronization
Cross-tab sync is hardcoded to work only with
window.localStorageand only for already cached keys.Proposed Action Plan
defaultValuecloning and empty string handling (P1).has/remove/clearto check the adapter state (P2).