Skip to content

fix(storage): in-memory fallback when browser storage rejects writes (Safari private mode) (#613) - #655

Merged
nonsobethel0-dev merged 2 commits into
Parashield-Protocol:mainfrom
jakespepe:fix/613-storage-memory-fallback
Sep 24, 2026
Merged

nonsobethel0-dev merged 2 commits into
Parashield-Protocol:mainfrom
jakespepe:fix/613-storage-memory-fallback

Conversation

@jakespepe

@jakespepe jakespepe commented Sep 24, 2026 •

Copy link
Copy Markdown

Summary

In Safari private browsing, localStorage.setItem and sessionStorage.setItem throw (QuotaExceededError) even for tiny writes. The same happens when storage is full or blocked by site settings. src/lib/storage.ts caught the error but threw the value away, so things like the stored wallet address, network, and the auth token in sessionStorage silently disappeared. The next get returned null.

Fix

In src/lib/storage.ts:

  • Added an in-memory fallback map for each storage area (memoryLocal, memorySession).
  • Writes (set, setJSON, setSession): if browser storage throws, the value goes into the matching map instead of being lost. A successful write clears any copy in memory, so browser storage stays the source of truth whenever it works.
  • Reads (get, getJSON, getSession): if browser storage has no value or throws (Safari can raise SecurityError on reads too), the value comes from memory.
  • Removes (remove, removeSession): clear both memory and browser storage.
  • The API is unchanged:
    • set and setSession still return false when the value wasn't saved to browser storage, so the existing tests and any callers checking the result behave as before.
    • The existing one-time SecurityError warning is kept.
    • Quota errors are still not logged.

Why memory rather than falling back to sessionStorage: in Safari private mode sessionStorage has the same write restriction, so memory is the only fallback that always works. The values live only as long as the page, which matches what private browsing should do anyway.

This way nothing has to "detect private mode". Browser-sniffing is fragile, and the fallback only kicks in when a write actually fails.

Tests

Added to src/__tests__/storage.test.ts:

  • A failed localStorage write is still readable through get and getJSON, and remove clears it
  • If both getItem and setItem throw SecurityError, the value is still readable from memory
  • Once a later write succeeds, browser storage is used again and the memory copy doesn't stick around
  • A failed sessionStorage write (such as the auth token) is still readable through getSession, and removeSession clears it
npx vitest run src/__tests__/storage.test.ts
 Tests  11 passed (11)          # with the old storage.ts: 4 failed | 7 passed

Full suite: 60 failed / 413 passed on this branch vs 61 failed / 408 passed on main. The failures that were already on main are unrelated, and this PR adds no new ones. tsc --noEmit reports no errors in storage.ts.
Closes #613
Closes #612
Closes #614
Closes #615

Safari private browsing (and full/blocked storage) makes
localStorage/sessionStorage setItem throw even for tiny writes. The error
was caught but the value was dropped, so wallet data and the auth token
silently vanished.

Keep a per-area in-memory Map: failed writes are stored there, reads fall
back to it when browser storage has no value or throws, and removes clear
both. Successful writes clear the memory copy so browser storage stays the
source of truth. set/setSession still return false when the value wasn't
persisted to browser storage, so the API contract is unchanged.

Closes Parashield-Protocol#613
Failed localStorage/sessionStorage writes (QuotaExceededError,
SecurityError on read and write) stay readable via get/getJSON/getSession,
remove clears the fallback, and browser storage wins again once a write
succeeds.

Refs Parashield-Protocol#613
@drips-wave

drips-wave Bot commented Sep 24, 2026

Copy link
Copy Markdown

@jakespepe Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@netlify

netlify Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

❌ Deploy Preview for boisterous-sunshine-dd4c4c failed.

Name Link
🔨 Latest commit 6e05766
🔍 Latest deploy log https://app.netlify.com/projects/boisterous-sunshine-dd4c4c/deploys/6ab513bd2074400008808508

@nonsobethel0-dev
nonsobethel0-dev merged commit 1393eee into Parashield-Protocol:main Sep 24, 2026
0 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants