Skip to content

[Feature] Add browser storage utilities#671

Merged
titouanmathis merged 11 commits intomainfrom
feature/storage
Mar 26, 2026
Merged

[Feature] Add browser storage utilities#671
titouanmathis merged 11 commits intomainfrom
feature/storage

Conversation

@titouanmathis
Copy link
Copy Markdown
Contributor

@titouanmathis titouanmathis commented Oct 2, 2025

🔗 Linked issue

No issue.

❓ Type of change

  • 📖 Documentation (updates to the documentation, readme or JSdoc annotations)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality like performance)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

Add browser storage utilities with a unified API supporting multiple backends.

Features

  • createStorage<T>(options) — Factory to create a typed, multi-key storage instance
  • useLocalStorage() — localStorage with cross-tab sync via storage event
  • useSessionStorage() — sessionStorage for session-scoped data
  • useUrlSearchParams() — URL search params (?key=value) for shareable state
  • useUrlSearchParamsInHash() — URL hash params (#key=value) for client-side state

API

  • get(key) / get(key, defaultValue) — Read values with optional defaults
  • set(key, value) — Write values (pass null to remove)
  • has(key) — Check if a key exists
  • keys() — List all keys (prefix-filtered when namespaced)
  • clear() — Remove all entries (prefix-scoped when namespaced)
  • subscribe(key, callback) — React to changes (local + external sync)
  • destroy() — Clean up all listeners

Options

  • prefix — Key namespacing (e.g. "myapp:" stores "theme" as "myapp:theme")
  • serializer — Custom serialize/deserialize (defaults to JSON)
  • push — URL providers: use pushState instead of replaceState (default: false)

Architecture

  • Provider pattern with syncEvent capability flag for automatic event sync
  • replaceState by default for URL providers (opt into pushState with { push: true })
  • SSR-safe — All providers use hasWindow() guards
  • Small footprint — ~750 bytes per utility, tree-shakeable

Documentation

Full docs added under /utils/storage/ with pages for each utility, providers guide, and custom provider example.

📝 Checklist

  • I have linked an issue or discussion.
  • I have added tests (if possible).
  • I have updated the documentation accordingly.
  • I have updated the changelog.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 2, 2025

Export Size

@studiometa/js-toolkit

Name Size Diff
useUrlSearchParamsInHash 919 B +919 B (+100.00%) 🔺
useUrlSearchParams 863 B +863 B (+100.00%) 🔺
useSessionStorage 669 B +669 B (+100.00%) 🔺
useLocalStorage 637 B +637 B (+100.00%) 🔺
createStorage 613 B +613 B (+100.00%) 🔺
urlSearchParamsInHashProvider 376 B +376 B (+100.00%) 🔺
createUrlSearchParamsInHashProvider 365 B +365 B (+100.00%) 🔺
urlSearchParamsProvider 335 B +335 B (+100.00%) 🔺
createUrlSearchParamsProvider 324 B +324 B (+100.00%) 🔺
sessionStorageProvider 184 B +184 B (+100.00%) 🔺
localStorageProvider 181 B +181 B (+100.00%) 🔺
UTILS 8.9 kB +976 B (+12.31%) 🔺
ALL 19.62 kB +961 B (+5.15%) 🔺
Unchanged

@studiometa/js-toolkit

Name Size Diff
AbstractService 528 B -
addClass 226 B -
addStyle 238 B -
animate 3.07 kB -
Base 8.55 kB -
BASE 8.59 kB -
boundingRectToCircle 154 B -
cache 194 B -
camelCase 401 B -
clamp 67 B -
clamp01 87 B -
closestComponent 537 B -
collideCircleCircle 99 B -
collideCircleRect 159 B -
collidePointCircle 112 B -
collidePointRect 103 B -
collideRectRect 99 B -
createApp 468 B -
createEaseInOut 116 B -
createEaseOut 71 B -
createElement 596 B -
createRange 90 B -
damp 78 B -
dashCase 376 B -
debounce 92 B -
DECORATORS 7.41 kB -
defineFeatures 322 B -
domScheduler 296 B -
DragService 1.92 kB -
ease 435 B -
easeInCirc 68 B -
easeInCubic 59 B -
easeInExpo 80 B -
easeInOutCirc 141 B -
easeInOutCubic 130 B -
easeInOutExpo 134 B -
easeInOutQuad 128 B -
easeInOutQuart 133 B -
easeInOutQuint 152 B -
easeInOutSine 151 B -
easeInQuad 63 B -
easeInQuart 61 B -
easeInQuint 62 B -
easeInSine 77 B -
easeLinear 49 B -
easeOutCirc 115 B -
easeOutCubic 103 B -
easeOutExpo 112 B -
easeOutQuad 103 B -
easeOutQuart 100 B -
easeOutQuint 103 B -
easeOutSine 121 B -
endsWith 88 B -
FRAMEWORK 13.21 kB -
getAncestorWhere 91 B -
getAncestorWhereUntil 119 B -
getClosestParent 182 B -
getComponentResolver 138 B -
getDirectChildren 201 B -
getInstanceFromElement 92 B -
getInstances 185 B -
getOffsetSizes 159 B -
hasWindow 62 B -
HELPERS 2.12 kB -
historyPush 499 B -
historyReplace 503 B -
importOnInteraction 891 B -
importOnMediaQuery 236 B -
importWhenIdle 223 B -
importWhenPrefersMotion 275 B -
importWhenVisible 911 B -
inertiaFinalValue 142 B -
isArray 70 B -
isBoolean 78 B -
isDefined 86 B -
isDev 72 B -
isDirectChild 219 B -
isEmpty 207 B -
isEmptyString 93 B -
isFunction 72 B -
isNull 72 B -
isNumber 84 B -
isObject 105 B -
isString 86 B -
keyCodes 97 B -
KeyService 854 B -
lerp 57 B -
loadElement 169 B -
loadIframe 189 B -
loadImage 188 B -
loadLink 186 B -
loadScript 197 B -
LoadService 593 B -
lowerCase 60 B -
map 71 B -
matrix 106 B -
mean 91 B -
memo 100 B -
memoize 189 B -
MutationService 799 B -
nextFrame 162 B -
nextMicrotask 111 B -
nextTick 134 B -
noop 39 B -
noopValue 49 B -
objectToURLSearchParams 302 B -
pascalCase 377 B -
PointerService 1.07 kB -
queryComponent 485 B -
queryComponentAll 494 B -
Queue 226 B -
RafService 912 B -
random 64 B -
randomInt 77 B -
randomItem 211 B -
registerComponent 103 B -
removeClass 222 B -
removeStyle 238 B -
ResizeService 1.01 kB -
round 56 B -
saveActiveElement 56 B -
ScrollService 1.12 kB -
scrollTo 2.27 kB -
SERVICES 3.83 kB -
SmartQueue 411 B -
smoothTo 473 B -
snakeCase 378 B -
spring 115 B -
startsWith 87 B -
throttle 101 B -
toggleClass 225 B -
transform 325 B -
transition 916 B -
trapFocus 363 B -
tween 1.69 kB -
untrapFocus 45 B -
upperCase 54 B -
useDrag 1.94 kB -
useKey 871 B -
useLoad 610 B -
useMutation 829 B -
usePointer 1.09 kB -
useRaf 930 B -
useResize 1.02 kB -
useScheduler 290 B -
useScroll 1.13 kB -
wait 79 B -
withBreakpointManager 1.42 kB -
withBreakpointObserver 1.61 kB -
withDrag 2.06 kB -
withExtraConfig 135 B -
withFreezedOptions 140 B -
withGroup 252 B -
withIntersectionObserver 260 B -
withLeadingCharacters 88 B -
withLeadingSlash 107 B -
withMountOnMediaQuery 322 B -
withMountWhenInView 286 B -
withMountWhenPrefersMotion 355 B -
withMutation 959 B -
withName 81 B -
withoutLeadingCharacters 86 B -
withoutLeadingCharactersRecursive 124 B -
withoutLeadingSlash 93 B -
withoutTrailingCharacters 98 B -
withoutTrailingCharactersRecursive 129 B -
withoutTrailingSlash 103 B -
withRelativePointer 1.23 kB -
withResponsiveOptions 2.26 kB -
withScrolledInView 2.8 kB -
withTrailingCharacters 96 B -
withTrailingSlash 120 B -
wrap 77 B -

@codecov
Copy link
Copy Markdown

codecov bot commented Oct 2, 2025

Codecov Report

❌ Patch coverage is 94.41624% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.64%. Comparing base (d910fae) to head (af79100).
⚠️ Report is 12 commits behind head on main.

Files with missing lines Patch % Lines
packages/js-toolkit/utils/storage.ts 94.41% 9 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #671      +/-   ##
==========================================
- Coverage   98.96%   98.64%   -0.32%     
==========================================
  Files         117      118       +1     
  Lines        2604     2801     +197     
  Branches      655      719      +64     
==========================================
+ Hits         2577     2763     +186     
- Misses         25       34       +9     
- Partials        2        4       +2     
Flag Coverage Δ
unittests 98.64% <94.41%> (-0.32%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@titouanmathis titouanmathis changed the base branch from develop to main March 25, 2026 11:22
- Replace provider identity checks with capability-based syncEvent flag
- Add prefix option for key namespacing
- Add default value support in get()
- Add createUrlSearchParamsProvider() and createUrlSearchParamsInHashProvider() factories with replace option (replaceState vs pushState)
- Use hasWindow() helper consistently
- Preserve hash when setting URL search params
- Omit provider from convenience hook options type
- Update and expand tests (47 tests)
Switch URL providers to use replaceState by default to avoid polluting
browser history. Add push option to opt into pushState when needed.
- has(key): check if a key exists in storage
- keys(): list all keys (filtered by prefix when set)
- clear(): remove all entries (only prefixed keys when prefix is set,
  notifies all subscribers with null)
- Added keys() and clear() to StorageProvider interface
- Implemented on all 4 providers (localStorage, sessionStorage,
  URL search params, URL hash params)
- 10 new tests (58 total)
@titouanmathis titouanmathis merged commit 3072211 into main Mar 26, 2026
4 checks passed
@titouanmathis titouanmathis deleted the feature/storage branch March 26, 2026 20:32
titouanmathis added a commit that referenced this pull request Apr 4, 2026
- Remove syncEvent from sessionStorageProvider (no cross-tab sync)
- Add storageArea to StorageProvider interface for StorageEvent filtering
- Filter StorageEvents by storageArea to prevent cross-provider interference
- Add try/catch in JSON deserializer for malformed data resilience
- Fall through to default value when deserialization fails
- Remove unused generic from StorageProvider interface
- Add keys() and clear() stubs to custom provider test mocks
- Add JSON serialization notes to URL storage docs
- Add cleanup warning to createStorage docs
- Add tests for storageArea filtering and JSON resilience
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