react-wire-persisted 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,2 @@
1
- (function(n,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@forminator/react-wire"),require("react")):typeof define=="function"&&define.amd?define(["exports","@forminator/react-wire","react"],l):(n=typeof globalThis<"u"?globalThis:n||self,l(n["react-wire-persisted"]={},n.reactWire,n.React))})(this,(function(n,l,f){"use strict";(function(){const s={};try{if(process){process.env=Object.assign({},process.env),Object.assign(process.env,s);return}}catch{}globalThis.process={env:s}})();const S={},v=s=>{S[s]=s},P=s=>v(s),U=()=>S,R=(s,e=null)=>{const t=e||S;return s?Object.keys(t).reduce((r,o)=>({...r,[o]:`${s}.${t[o]}`}),{}):t},m={__IS_FAKE_LOCAL_STORAGE__:!0},_={getItem:s=>m[s],setItem:(s,e)=>{m[s]=e},removeItem:s=>{delete m[s]},...m};let I=!1,O=!1;typeof window<"u"&&(I=!0,document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{O=!0}):O=!0);const h=()=>I,b=()=>O,p=()=>{if(!I)return!1;try{const s="__rwp_test__";return window.localStorage.setItem(s,"test"),window.localStorage.removeItem(s),!0}catch{return!1}},T=s=>{const e=typeof s;return s===null?!0:Array.isArray(s)||e==="object"?!1:e!=="function"},L=Object.freeze(Object.defineProperty({__proto__:null,addKey:v,fakeLocalStorage:_,getHasHydrated:b,getIsClient:h,getKeys:U,getPrefixedKeys:R,isLocalStorageAvailable:p,isPrimitive:T,key:P},Symbol.toStringTag,{value:"Module"}));class w{constructor(e,t){if(new.target===w)throw TypeError("StorageProvider is abstract. Extend this class to implement it");this.namespace=e||null,this.registry=t||{}}setNamespace(e){}register(e,t){this.registry[e]=t}getItem(e){}setItem(e,t){}removeItem(e,t=!1){}getAll(){}_resetAll(e=!0,t=[],r=!1){}resetAll(e=[]){}removeAll(e=[]){}}class F extends w{constructor(e=null,t={}){super(e,t),this.storage=this.getStorage(),this._isUsingFakeStorage=!p()}getStorage(){return p()?window.localStorage:_}setNamespace(e){if(!this.namespace){this.namespace=e;return}if(this.namespace===e)return;const t=JSON.parse(JSON.stringify(this.getAll()));this.removeAll();for(const[r,o]of Object.entries(t)){const i=r.replace(this.namespace,e);this.setItem(i,o)}this.namespace=e}getItem(e){const t=this.storage.getItem(e);if(t==null)return null;try{return JSON.parse(t)}catch{return t}}setItem(e,t){let r=t;return r!=null&&(r=T(t)?t:JSON.stringify(t)),this.storage.setItem(e,r)}removeItem(e,t=!1){return t&&delete this.registry[e],this.storage.removeItem(e)}getAll(){const e=`${this.namespace}.`;return Object.keys(this.storage).reduce((t,r)=>((!this.namespace||r.startsWith(e))&&(t[r]=this.storage.getItem(r)),t),{})}_resetAll(e=!0,t=[],r=!1){const o=`${this.namespace}.`;Object.keys(this.storage).forEach(i=>{const c=this.namespace?i.startsWith(o):!0,g=t?.includes(i)||!1;!c||g||(e?Object.prototype.hasOwnProperty.call(this.registry,i)?this.storage.setItem(i,this.registry[i]):this.storage.removeItem(i):(this.storage.removeItem(i),r&&delete this.registry[i]))})}resetAll(e=[],t=!1){this._resetAll(!0,e||[],t)}removeAll(e=[],t=!1){this._resetAll(!1,e||[],t)}upgradeToRealStorage(){if(!this._isUsingFakeStorage||!p())return!1;const e={...this.storage};return this.storage=window.localStorage,this._isUsingFakeStorage=!1,Object.keys(e).forEach(t=>{if(t!=="__IS_FAKE_LOCAL_STORAGE__"&&e[t]!=null)try{this.storage.setItem(t,e[t])}catch{return this.storage=_,this._isUsingFakeStorage=!0,!1}}),!0}isUsingFakeStorage(){return this._isUsingFakeStorage}}const j={logging:{enabled:!1}};let E=F,a=new E,u={...j},y=[];const K=()=>a.namespace,k=()=>a,H=()=>u,W=s=>{a.setNamespace(s),a=new E(s||K())},V=s=>{if(u={...u,...s},u.logging.enabled)for(console.info("Flushing",y.length,"pending logs");y.length;)console.log(...y.shift())},A=()=>{if(!h())return!1;const s=a.upgradeToRealStorage();return s&&N("react-wire-persisted: Upgraded to real localStorage after hydration"),s},N=(...s)=>{u.logging.enabled?console.log(...s):y.push(s)},$=(s,e=null)=>{if(!s&&typeof s!="number")throw new Error(`createPersistedWire: Key cannot be a falsey value (${s}}`);a.register(s,e);const t=l.createWire(e),r=()=>t.getValue(),o=d=>(a.setItem(s,d),t.setValue(d)),i=d=>{t.subscribe(d)},c=a.getItem(s),g=c===null?e:c;return N("react-wire-persisted: create",s,{value:e,storedValue:c,initialValue:g}),g!==e&&o(g),{...t,getValue:r,setValue:o,subscribe:i}},C=(s={})=>{const{autoUpgrade:e=!0,onUpgrade:t}=s,r=f.useRef(!1);return f.useEffect(()=>{if(!e||r.current||!h())return;const o=()=>{b()&&!r.current&&A()&&(r.current=!0,t?.())};o();const i=setTimeout(o,0);return()=>clearTimeout(i)},[e,t]),{hasUpgraded:r.current}};function J({children:s,onUpgrade:e,autoUpgrade:t=!0}){const r=f.useRef(!1);return f.useEffect(()=>{if(!t||r.current||!h())return;const o=()=>{b()&&!r.current&&A()&&(r.current=!0,e?.())};o();const i=setTimeout(o,0);return()=>clearTimeout(i)},[t,e]),s}n.HydrationProvider=J,n.createPersistedWire=$,n.defaultOptions=j,n.getNamespace=K,n.getOptions=H,n.getStorage=k,n.setNamespace=W,n.setOptions=V,n.upgradeStorage=A,n.useHydration=C,n.utils=L,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`react`),require(`@forminator/react-wire`)):typeof define==`function`&&define.amd?define([`exports`,`react`,`@forminator/react-wire`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e[`react-wire-persisted`]={},e.React,e.reactWire))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=Object.defineProperty,i=(e,t)=>{let n={};for(var i in e)r(n,i,{get:e[i],enumerable:!0});return t||r(n,Symbol.toStringTag,{value:`Module`}),n};(function(){let e={};try{if(process){process.env=Object.assign({},process.env),Object.assign(process.env,e);return}}catch{}globalThis.process={env:e}})();var a={__IS_FAKE_LOCAL_STORAGE__:`true`},o={getItem:e=>a[e],setItem:(e,t)=>{a[e]=t},removeItem:e=>{delete a[e]},...a},s=!1,c=!1,l=!1;typeof window<`u`&&(s=!0,document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>{c=!0}):c=!0);var u=()=>s,d=()=>c,f=()=>l,p=()=>{l=!0},m=()=>{if(!s)return!1;try{let e=`__rwp_test__`;return window.localStorage.setItem(e,`test`),window.localStorage.removeItem(e),!0}catch{return!1}},h={},g=e=>{h[e]=e},_=e=>g(e),v=()=>h,y=(e,t=null)=>{let n=t||h;return e?Object.keys(n).reduce((t,r)=>(t[r]=`${e}.${n[r]}`,t),{}):n},b=i({addKey:()=>g,fakeLocalStorage:()=>o,getHasHydrated:()=>d,getHasHydratedStorage:()=>f,getIsClient:()=>u,getKeys:()=>v,getPrefixedKeys:()=>y,isLocalStorageAvailable:()=>m,isPrimitive:()=>x,key:()=>_,markStorageAsHydrated:()=>p}),x=e=>{let t=typeof e;return e===null?!0:Array.isArray(e)||t===`object`?!1:t!==`function`},S=class e{constructor(t,n){if(new.target===e)throw TypeError(`StorageProvider is abstract. Extend this class to implement it`);this.namespace=t||null,this.registry=n||{}}register(e,t){this.registry[e]=t}upgradeToRealStorage(){return!1}isUsingFakeStorage(){return!1}},C=class extends S{constructor(e,t={}){super(e,t),this.storage=o,this._isUsingFakeStorage=!0}getStorage(){return m()?window.localStorage:o}setNamespace(e){if(!this.namespace){this.namespace=e;return}if(this.namespace===e)return;let t=JSON.parse(JSON.stringify(this.getAll()));this.removeAll();for(let[n,r]of Object.entries(t)){let t=n.replace(this.namespace,e);this.setItem(t,r)}this.namespace=e}getItem(e){let t=this.storage.getItem(e);if(t==null)return null;try{return JSON.parse(t)}catch{return t}}setItem(e,t){return t==null?this.removeItem(e):this.storage.setItem(e,JSON.stringify(t))}removeItem(e,t=!1){return t&&delete this.registry[e],this.storage.removeItem(e)}getAll(){let e=`${this.namespace}.`;return Object.keys(this.storage).reduce((t,n)=>((!this.namespace||n.startsWith(e))&&(t[n]=this.storage.getItem(n)),t),{})}_resetAll(e=!0,t=[],n=!1){let r=`${this.namespace}.`;Object.keys(this.storage).forEach(i=>{let a=this.namespace?i.startsWith(r):!0,o=t?.includes(i)||!1;!a||o||(e?Object.hasOwn(this.registry,i)?this.registry[i]===void 0||this.registry[i]===null?this.storage.removeItem(i):this.storage.setItem(i,JSON.stringify(this.registry[i])):this.storage.removeItem(i):(this.storage.removeItem(i),n&&delete this.registry[i]))})}resetAll(e=[],t=!1){this._resetAll(!0,e||[],t)}removeAll(e=[],t=!1){this._resetAll(!1,e||[],t)}upgradeToRealStorage(){return!this._isUsingFakeStorage||!m()?!1:(this.storage=window.localStorage,this._isUsingFakeStorage=!1,!0)}isUsingFakeStorage(){return this._isUsingFakeStorage}},w=Math.random().toString(36).substring(7),T=(...e)=>{typeof globalThis<`u`&&globalThis.__RWP_LOGGING_ENABLED__!==!1&&console.log(...e)},E={logging:{enabled:!1},storageProvider:C},D={...E},O,k=[];typeof globalThis<`u`&&globalThis.__RWP_LOGGING_ENABLED__===void 0&&(globalThis.__RWP_LOGGING_ENABLED__=E.logging.enabled),T(`[RWP] Module initialized, instance ID:`,w),T(`[RWP] About to check global storage, instanceId:`,w);try{globalThis.__RWP_STORAGE__?T(`[RWP] Using existing global storage in instance:`,w):(T(`[RWP] Creating global storage in instance:`,w),globalThis.__RWP_STORAGE__=new C(`__internal_rwp_storage__`)),O=globalThis.__RWP_STORAGE__,T(`[RWP] InternalStorage assigned successfully`)}catch(e){globalThis.__RWP_LOGGING_ENABLED__&&console.error(`[RWP] Error setting up global storage:`,e),O=new C(`__internal_rwp_storage__`)}typeof globalThis<`u`&&(globalThis.__RWP_REGISTERED_WIRES__?T(`[RWP] Using existing global registeredWires in instance:`,w):(T(`[RWP] Creating global registeredWires in instance:`,w),globalThis.__RWP_REGISTERED_WIRES__=new Map));var A=globalThis.__RWP_REGISTERED_WIRES__||new Map;T(`[RWP] registeredWires Map reference in instance:`,w,`size:`,A.size);var j=()=>O.namespace,M=()=>O,N=e=>{T(`[RWP] setNamespace() called with:`,e,`registered wires before:`,A.size);let t=e||j();if(!t)throw Error(`react-wire-persisted: Cannot set namespace to null or undefined`);O.setNamespace(e),O=new C(t),T(`[RWP] setNamespace() done, registered wires after:`,A.size)},P=()=>D,F=e=>{if(D={...D,...e},typeof globalThis<`u`&&(globalThis.__RWP_LOGGING_ENABLED__=D.logging.enabled),D.logging.enabled)for(console.info(`Flushing`,k.length,`pending logs`);k.length;)console.log(...k.shift()||[])},I=()=>{T(`[RWP] refreshAllWires() called in instance:`,w,`registered wires:`,A.size),R(`react-wire-persisted: refreshAllWires() called, registered wires:`,A.size),A.forEach((e,t)=>{let n=O.getItem(t),r=e.getValue();T(`[RWP] Checking wire`,t,{storedValue:n,currentValue:r,willUpdate:n!==null&&n!==r}),R(`react-wire-persisted: Checking wire`,t,{storedValue:n,currentValue:r,willUpdate:n!==null&&n!==r}),n!==null&&n!==r&&(T(`[RWP] Refreshing wire`,t,`with stored value`,n),R(`react-wire-persisted: Refreshing wire`,t,`with stored value`,n),e.setValue(n))})},L=()=>{if(T(`[RWP] upgradeStorage() called in instance:`,w,{isClient:u(),isUsingFakeStorage:O.isUsingFakeStorage()}),R(`react-wire-persisted: upgradeStorage() called`,{isClient:u(),isUsingFakeStorage:O.isUsingFakeStorage()}),!u())return!1;let e=O.upgradeToRealStorage();return T(`[RWP] upgradeToRealStorage() returned`,e),R(`react-wire-persisted: upgradeToRealStorage() returned`,e),e&&(p(),T(`[RWP] Upgraded to real localStorage, calling refreshAllWires()`),R(`react-wire-persisted: Upgraded to real localStorage after hydration`),I()),e},R=(...e)=>{D.logging.enabled?console.log(...e):k.push(e)},z=(e,t=null)=>{if(T(`[RWP] createPersistedWire() called in instance:`,w,`key:`,e,`value:`,t),!e)throw Error(`createPersistedWire: Key cannot be a falsey value (${e}}`);O.register(e,t);let r=(0,n.createWire)(t),i=()=>r.getValue(),a=t=>(T(`[RWP] setValue called in instance:`,w,`key:`,e,`isUsingFakeStorage:`,O.isUsingFakeStorage()),O.setItem(e,t),r.setValue(t)),o=e=>r.subscribe(e),s=t,c=f()||!O.isUsingFakeStorage();if(c&&u()){let t=O.getItem(e);t!==null&&(s=t)}return R(`react-wire-persisted: create`,e,{value:t,initialValue:s,hasHydratedStorage:f(),isUsingFakeStorage:O.isUsingFakeStorage(),canReadStorage:c}),s!==t&&s!==void 0&&a(s),A.set(e,{getValue:i,setValue:a,subscribe:o}),T(`[RWP] Wire registered, total wires:`,A.size,`keys:`,Array.from(A.keys())),{...r,getValue:i,setValue:a,subscribe:o}},B=(e={})=>{let{autoUpgrade:n=!0,onUpgrade:r}=e,i=(0,t.useRef)(!1);return(0,t.useEffect)(()=>{if(!n||i.current||!u())return;let e=()=>{d()&&!i.current&&L()&&(i.current=!0,r?.())};e();let t=setTimeout(e,0);return()=>clearTimeout(t)},[n,r]),{hasUpgraded:i.current}},V=({children:e,onUpgrade:t,autoUpgrade:n=!0})=>(B({onUpgrade:t,autoUpgrade:n}),e),H=class extends C{constructor(e,t={}){super(e,t)}getStorage(){return o}};e.HydrationProvider=V,e.LocalStorageProvider=C,e.MemoryStorageProvider=H,e.RWPStorageProvider=S,e.createPersistedWire=z,e.defaultOptions=E,e.getNamespace=j,e.getOptions=P,e.getStorage=M,e.setNamespace=N,e.setOptions=F,e.upgradeStorage=L,e.useHydration=B,Object.defineProperty(e,`utils`,{enumerable:!0,get:function(){return b}})});
2
+ //# sourceMappingURL=react-wire-persisted.umd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-wire-persisted.umd.cjs","names":[],"sources":["../src/utils/fakeLocalStorage.ts","../src/utils/isomorphic.ts","../src/utils/keys.ts","../src/utils/index.ts","../src/providers/RWPStorageProvider.ts","../src/providers/LocalStorageProvider.ts","../src/react-wire-persisted.ts","../src/hooks/useHydration.ts","../src/components/HydrationProvider.tsx","../src/providers/MemoryStorageProvider.ts"],"sourcesContent":["import type { InternalStorage } from '@/types'\n\nconst storage: Record<string, string> = {\n __IS_FAKE_LOCAL_STORAGE__: 'true',\n}\n\nexport const fakeLocalStorage: InternalStorage = {\n getItem: (key: string): string | null => storage[key],\n setItem: (key: string, value: string): void => {\n storage[key] = value\n },\n removeItem: (key: string): void => {\n delete storage[key]\n },\n // Make Object.keys() work properly for _resetAll method\n ...storage,\n}\n","/**\n * Utilities for handling server-side rendering and client-side hydration\n */\n\nlet isClient: boolean = false\nlet hasHydrated: boolean = false\nlet hasHydratedStorage: boolean = false\n\n// Detect if we're running in a browser environment\nif (typeof window !== 'undefined') {\n isClient = true\n\n // Mark as hydrated when the DOM is ready\n if (document.readyState === 'loading')\n document.addEventListener('DOMContentLoaded', () => {\n hasHydrated = true\n })\n else hasHydrated = true\n}\n\n/**\n * Check if we're running in a browser environment\n */\nexport const getIsClient = (): boolean => isClient\n\n/**\n * Check if the client has finished hydrating\n */\nexport const getHasHydrated = (): boolean => hasHydrated\n\n/**\n * Check if storage has been hydrated (safe to read from real localStorage)\n */\nexport const getHasHydratedStorage = (): boolean => hasHydratedStorage\n\n/**\n * Mark storage as hydrated (called after upgradeStorage)\n */\nexport const markStorageAsHydrated = (): void => {\n hasHydratedStorage = true\n}\n\n/**\n * Check if localStorage is available and safe to use\n */\nexport const isLocalStorageAvailable = (): boolean => {\n if (!isClient) return false\n\n try {\n const testKey = '__rwp_test__'\n\n window.localStorage.setItem(testKey, 'test')\n window.localStorage.removeItem(testKey)\n\n return true\n } catch (_) {\n return false\n }\n}\n","/**\n * Convenience map of keys\n */\nconst storageKeys: Record<string, string> = {}\n\n/**\n * Adds a key to the keys map\n *\n * @param {String} value Key name\n */\nexport const addKey = (value: string): void => {\n storageKeys[value] = value\n}\n\n/**\n * Adds a key to the keys map\n * (Alias for `addKey`)\n *\n * @param {String} value Key name\n */\nexport const key = (value: string) => addKey(value)\n\n/**\n * Convenience method to get internally managed storage keys\n *\n * @returns {Object} InternalStorage keys map\n */\nexport const getKeys = (): Record<string, string> => storageKeys\n\n/**\n * Helper utility to prefix all keys in a map to use a namespace\n *\n * @param {String} namespace InternalStorage namespace prefix\n * @param {Object} keys (Optional) InternalStorage key/values. Defaults to the internally managed keys map\n */\nexport const getPrefixedKeys = (namespace: string, keys: Record<string, string> | null = null) => {\n const items = keys || storageKeys\n\n if (!namespace) return items\n\n return Object.keys(items).reduce(\n (acc, it) => {\n acc[it] = `${namespace}.${items[it]}`\n\n return acc\n },\n {} as Record<string, string>,\n )\n}\n","export * from './fakeLocalStorage'\nexport * from './isomorphic'\nexport * from './keys'\n\n/**\n * Checks if a value is a primitive type\n *\n * @param {*} val Value to check\n * @returns {Boolean} True if value is a primitive type\n */\nexport const isPrimitive = (val: unknown): boolean => {\n const type = typeof val\n\n if (val === null) return true\n if (Array.isArray(val)) return false\n if (type === 'object') return false\n\n return type !== 'function'\n}\n","/**\n * Base class to allow storage access\n * @see `LocalStorageProvider.ts` for an example implementation\n */\n/** biome-ignore-all lint/correctness/noUnusedFunctionParameters: WIP next PR will switch to TypeScript */\nexport abstract class RWPStorageProvider {\n namespace: string | null\n registry: Record<string, unknown>\n\n /**\n * Initializes the class\n * @param {String} namespace Namespace to prefix all keys with. Mostly used for the logging and reset functions\n * @param {Object} registry (Optional) Initialize the storage provider with an existing registry\n */\n protected constructor(namespace: string, registry: Record<string, unknown>) {\n // Simulate being an abstract class\n if (new.target === RWPStorageProvider)\n throw TypeError(`StorageProvider is abstract. Extend this class to implement it`)\n\n this.namespace = namespace || null\n this.registry = registry || /* istanbul ignore next */ {}\n }\n\n /**\n * Sets the namespace for this storage provider, and migrates\n * all stored values to the new namespace\n * @param {String} namespace New namespace for this storage provider\n */\n /* istanbul ignore next */\n abstract setNamespace(namespace: string | null): void\n\n /**\n * Registers an item with its initial value. This is used for logging, resetting, etc.\n * @param {String} key InternalStorage item's key\n * @param {*} initialValue InternalStorage item's initial value\n */\n register<T>(key: string, initialValue: T | null) {\n this.registry[key] = initialValue\n }\n\n /**\n * Reads an item from storage\n * @param {String} key Key for the item to retrieve\n */\n /* istanbul ignore next */\n abstract getItem<T>(key: string): T | null\n\n /**\n * Stores a value\n * @param {String} key Item's storage key\n * @param {String} value Item's value to store\n */\n /* istanbul ignore next */\n abstract setItem<T>(key: string, value: T | null): void\n\n /**\n * Removes an item from storage\n * @param {String} key Item's storage key\n * @param {Boolean} fromRegistry (Optional) If the item should also be removed from the registry\n */\n /* istanbul ignore next */\n abstract removeItem(key: string, fromRegistry: boolean): void\n\n /**\n * Gets all stored keys and values\n * If a `namespace` was set, only keys prefixed with the namespace will be returned\n */\n /* istanbul ignore next */\n abstract getAll(): Record<string, unknown>\n\n /**\n *\n * @param {Boolean} useInitialValues If values should be replaced with their initial values. If false, keys are removed\n * @param {String[]} excludedKeys (Optional) List of keys to exclude\n * @param {Boolean} clearRegistry (Optional) If the registry should also be cleared\n */\n /* istanbul ignore next */\n abstract _resetAll(useInitialValues: boolean, excludedKeys: string[], clearRegistry: boolean): void\n\n /**\n * Resets all values to their initial values\n * If a `namespace` is set, only keys prefixed with the namespace will be reset\n * @param {String[]} excludedKeys (Optional) List of keys to exclude\n */\n /* istanbul ignore next */\n abstract resetAll(excludedKeys: string[]): void\n\n /**\n * Removes all items from local storage.\n * If a `namespace` is set, only keys prefixed with the namespace will be removed\n * @param {String[]} excludedKeys (Optional) List of keys to exclude\n */\n /* istanbul ignore next */\n abstract removeAll(excludedKeys: string[]): void\n\n upgradeToRealStorage(): boolean {\n return false\n }\n\n isUsingFakeStorage(): boolean {\n return false\n }\n}\n\nexport default RWPStorageProvider\n","import RWPStorageProvider from '@/providers/RWPStorageProvider'\nimport type { AnyStorage } from '@/types'\nimport { fakeLocalStorage, isLocalStorageAvailable } from '@/utils'\n\n/**\n * A storage provider for `localStorage`\n * @see `RWPStorageProvider.ts` for documentation\n */\nexport class LocalStorageProvider extends RWPStorageProvider {\n public storage: AnyStorage\n private _isUsingFakeStorage: boolean\n\n constructor(namespace: string, registry: Record<string, unknown> = {}) {\n super(namespace, registry)\n\n // Always start with fake storage to prevent hydration mismatches\n // Will be upgraded to real storage after hydration via upgradeToRealStorage()\n this.storage = fakeLocalStorage\n this._isUsingFakeStorage = true\n }\n\n getStorage(): AnyStorage {\n // Use the isomorphic utility to check localStorage availability\n if (isLocalStorageAvailable()) return window.localStorage\n\n // Fallback to fake localStorage for SSR or when localStorage is disabled\n return fakeLocalStorage\n }\n\n setNamespace(namespace: string) {\n if (!this.namespace) {\n this.namespace = namespace\n return\n }\n\n if (this.namespace === namespace) return\n\n const items = JSON.parse(JSON.stringify(this.getAll()))\n\n this.removeAll()\n\n for (const [key, value] of Object.entries(items)) {\n const newKey = key.replace(this.namespace, namespace)\n this.setItem(newKey, value)\n }\n\n this.namespace = namespace\n }\n\n getItem(key: string) {\n const val = this.storage.getItem(key)\n\n if (val === undefined || val === null) return null\n\n try {\n return JSON.parse(val)\n } catch (_) {\n return val\n }\n }\n\n setItem(key: string, value: unknown) {\n // Don't allow \"null\" & similar values to be stringified\n if (value === undefined || value === null) return this.removeItem(key)\n\n return this.storage.setItem(key, JSON.stringify(value))\n }\n\n removeItem(key: string, fromRegistry: boolean = false) {\n if (fromRegistry) delete this.registry[key]\n\n return this.storage.removeItem(key)\n }\n\n getAll(): Record<string, unknown> {\n const prefixNs = `${this.namespace}.`\n\n return Object.keys(this.storage).reduce(\n (acc, it) => {\n if (this.namespace ? it.startsWith(prefixNs) : true) acc[it] = this.storage.getItem(it)\n\n return acc\n },\n {} as Record<string, unknown>,\n )\n }\n\n _resetAll(useInitialValues: boolean = true, excludedKeys: string[] = [], clearRegistry: boolean = false) {\n const prefixNs = `${this.namespace}.`\n\n Object.keys(this.storage).forEach((it) => {\n const isAppKey = this.namespace ? it.startsWith(prefixNs) : true\n const isExcluded = excludedKeys?.includes(it) || false\n\n if (!isAppKey || isExcluded) return\n\n if (useInitialValues) {\n const isRegistered = Object.hasOwn(this.registry, it)\n\n if (isRegistered)\n if (this.registry[it] === undefined || this.registry[it] === null) this.storage.removeItem(it)\n else this.storage.setItem(it, JSON.stringify(this.registry[it]))\n else this.storage.removeItem(it)\n } else {\n this.storage.removeItem(it)\n\n if (clearRegistry) delete this.registry[it]\n }\n })\n }\n\n resetAll(excludedKeys: string[] = [], clearRegistry: boolean = false) {\n this._resetAll(true, excludedKeys || [], clearRegistry)\n }\n\n removeAll(excludedKeys: string[] = [], clearRegistry: boolean = false) {\n this._resetAll(false, excludedKeys || [], clearRegistry)\n }\n\n /**\n * Attempt to upgrade from fake storage to real localStorage\n * This is useful for hydration scenarios\n */\n upgradeToRealStorage(): boolean {\n if (!this._isUsingFakeStorage) return false // Already using real storage\n\n if (!isLocalStorageAvailable()) return false // Real storage still not available\n\n // Simply switch to real storage - don't migrate fake data\n // The existing persisted data in localStorage should be preserved\n this.storage = window.localStorage\n this._isUsingFakeStorage = false\n\n return true\n }\n\n /**\n * Check if currently using fake storage\n */\n isUsingFakeStorage(): boolean {\n return this._isUsingFakeStorage\n }\n}\n\nexport default LocalStorageProvider\n","import { createWire, type Defined, type Wire } from '@forminator/react-wire'\nimport LocalStorageProvider from '@/providers/LocalStorageProvider'\nimport type RWPStorageProvider from '@/providers/RWPStorageProvider'\nimport type { PersistedWire, RWPOptions, WireLikeObject } from '@/types'\nimport { getHasHydratedStorage, getIsClient, markStorageAsHydrated } from '@/utils'\n\n// Generate unique instance ID\nconst instanceId = Math.random().toString(36).substring(7)\nconst rwpLog = (...args: unknown[]) => {\n if (typeof globalThis !== 'undefined' && globalThis.__RWP_LOGGING_ENABLED__ !== false) {\n console.log(...args)\n }\n}\n\nexport const defaultOptions: RWPOptions = {\n logging: {\n enabled: false,\n },\n storageProvider: LocalStorageProvider,\n}\n\nlet options: RWPOptions = { ...defaultOptions }\nlet storage: RWPStorageProvider\nconst pendingLogs: unknown[][] = []\n\n// Set global logging flag on startup\nif (typeof globalThis !== 'undefined' && globalThis.__RWP_LOGGING_ENABLED__ === undefined) {\n globalThis.__RWP_LOGGING_ENABLED__ = defaultOptions.logging.enabled\n}\n\nrwpLog('[RWP] Module initialized, instance ID:', instanceId)\n\n// Make storage global so all instances share the same storage after upgrade\nrwpLog('[RWP] About to check global storage, instanceId:', instanceId)\ntry {\n if (!globalThis.__RWP_STORAGE__) {\n rwpLog('[RWP] Creating global storage in instance:', instanceId)\n globalThis.__RWP_STORAGE__ = new LocalStorageProvider('__internal_rwp_storage__')\n } else {\n rwpLog('[RWP] Using existing global storage in instance:', instanceId)\n }\n storage = globalThis.__RWP_STORAGE__\n rwpLog('[RWP] InternalStorage assigned successfully')\n} catch (error) {\n if (globalThis.__RWP_LOGGING_ENABLED__) console.error('[RWP] Error setting up global storage:', error)\n storage = new LocalStorageProvider('__internal_rwp_storage__')\n}\n\n// Use a global registry to handle multiple module instances\n// This ensures all instances share the same wire registry\nif (typeof globalThis !== 'undefined') {\n if (!globalThis.__RWP_REGISTERED_WIRES__) {\n rwpLog('[RWP] Creating global registeredWires in instance:', instanceId)\n globalThis.__RWP_REGISTERED_WIRES__ = new Map()\n } else {\n rwpLog('[RWP] Using existing global registeredWires in instance:', instanceId)\n }\n}\n\n// Registry to track wire instances for hydration refresh\nconst registeredWires = globalThis.__RWP_REGISTERED_WIRES__ || new Map<string, WireLikeObject>()\nrwpLog('[RWP] registeredWires Map reference in instance:', instanceId, 'size:', registeredWires.size)\n\nexport const getNamespace = (): string | null => storage.namespace\n\nexport const getStorage = (): RWPStorageProvider => storage\n\n/**\n * Sets the namespace for the storage provider\n *\n * @param {String} namespace The namespace for the storage provider\n */\nexport const setNamespace = (namespace: string) => {\n rwpLog('[RWP] setNamespace() called with:', namespace, 'registered wires before:', registeredWires.size)\n const currentNamespace = namespace || getNamespace()\n\n if (!currentNamespace) throw new Error('react-wire-persisted: Cannot set namespace to null or undefined')\n\n storage.setNamespace(namespace)\n storage = new LocalStorageProvider(currentNamespace)\n rwpLog(`[RWP] setNamespace() done, registered wires after:`, registeredWires.size)\n}\n\nexport const getOptions = (): RWPOptions => options\n\nexport const setOptions = (value: Partial<RWPOptions>) => {\n options = {\n ...options,\n ...value,\n }\n // Update global logging flag\n if (typeof globalThis !== 'undefined') {\n globalThis.__RWP_LOGGING_ENABLED__ = options.logging.enabled\n }\n /* istanbul ignore next */\n if (options.logging.enabled) {\n console.info('Flushing', pendingLogs.length, 'pending logs')\n while (pendingLogs.length)\n /* istanbul ignore next */\n console.log(...(pendingLogs.shift() || []))\n }\n}\n\n/**\n * Refresh all registered wires by reading from storage\n * Called after storage upgrade to sync wires with persisted values\n */\nconst refreshAllWires = () => {\n rwpLog('[RWP] refreshAllWires() called in instance:', instanceId, 'registered wires:', registeredWires.size)\n log('react-wire-persisted: refreshAllWires() called, registered wires:', registeredWires.size)\n\n registeredWires.forEach((wire: Wire<unknown> | WireLikeObject, key: string) => {\n const storedValue = storage.getItem(key)\n const currentValue = wire.getValue()\n\n rwpLog('[RWP] Checking wire', key, {\n storedValue,\n currentValue,\n willUpdate: storedValue !== null && storedValue !== currentValue,\n })\n\n log('react-wire-persisted: Checking wire', key, {\n storedValue,\n currentValue,\n willUpdate: storedValue !== null && storedValue !== currentValue,\n })\n\n if (storedValue !== null && storedValue !== currentValue) {\n rwpLog('[RWP] Refreshing wire', key, 'with stored value', storedValue)\n log('react-wire-persisted: Refreshing wire', key, 'with stored value', storedValue)\n wire.setValue(storedValue)\n }\n })\n}\n\n/**\n * Attempts to upgrade the storage provider from fake storage to real localStorage\n * This should be called on the client side after hydration\n *\n * @returns true if upgrade was successful\n */\nexport const upgradeStorage = (): boolean => {\n rwpLog('[RWP] upgradeStorage() called in instance:', instanceId, {\n isClient: getIsClient(),\n isUsingFakeStorage: storage.isUsingFakeStorage(),\n })\n\n log('react-wire-persisted: upgradeStorage() called', {\n isClient: getIsClient(),\n isUsingFakeStorage: storage.isUsingFakeStorage(),\n })\n\n if (!getIsClient()) return false\n\n const upgraded = storage.upgradeToRealStorage()\n\n rwpLog('[RWP] upgradeToRealStorage() returned', upgraded)\n log('react-wire-persisted: upgradeToRealStorage() returned', upgraded)\n\n if (upgraded) {\n markStorageAsHydrated()\n rwpLog('[RWP] Upgraded to real localStorage, calling refreshAllWires()')\n log('react-wire-persisted: Upgraded to real localStorage after hydration')\n\n // Refresh all wires with stored values\n refreshAllWires()\n }\n\n return upgraded\n}\n\nconst log = (...args: unknown[]) => {\n /* istanbul ignore next */\n if (options.logging.enabled)\n /* istanbul ignore next */\n console.log(...args)\n else pendingLogs.push(args)\n}\n\n/**\n * Creates a persisted Wire using the `RWPStorageProvider` that is currently set\n * Defaults to `localStorage` via `LocalStorageProvider`\n *\n * @param {String} key Unique key for storing this value\n * @param {*} value Initial value of this Wire\n * @returns A new Wire decorated with localStorage functionality\n */\nexport const createPersistedWire = <T = null>(key: string, value: T = null as T): PersistedWire<T> => {\n rwpLog('[RWP] createPersistedWire() called in instance:', instanceId, 'key:', key, 'value:', value)\n\n // This check helps ensure no accidental key typos occur\n if (!key) throw new Error(`createPersistedWire: Key cannot be a falsey value (${key}}`)\n\n // Track this writable entry so we can easily clear all\n storage.register(key, value)\n\n // The actual Wire backing object\n const wire = createWire<T>(value)\n\n const getValue = () => wire.getValue()\n\n const setValue = (newValue: Defined<T>) => {\n rwpLog(\n '[RWP] setValue called in instance:',\n instanceId,\n 'key:',\n key,\n 'isUsingFakeStorage:',\n storage.isUsingFakeStorage(),\n )\n storage.setItem(key, newValue)\n return wire.setValue(newValue)\n }\n\n const subscribe = (callback: (value: Defined<T>) => void) => {\n return wire.subscribe(callback)\n }\n\n // Always start with default value to ensure SSR consistency\n let initialValue: T = value\n\n // Only read from storage if we've hydrated OR if storage is already using real localStorage\n // (prevents hydration mismatch in SSR, but allows normal behavior in client-only apps)\n const canReadStorage = getHasHydratedStorage() || !storage.isUsingFakeStorage()\n\n if (canReadStorage && getIsClient()) {\n const storedValue = storage.getItem<T>(key)\n\n if (storedValue !== null) initialValue = storedValue\n }\n\n log('react-wire-persisted: create', key, {\n value,\n initialValue,\n hasHydratedStorage: getHasHydratedStorage(),\n isUsingFakeStorage: storage.isUsingFakeStorage(),\n canReadStorage,\n })\n\n if (initialValue !== value && initialValue !== undefined) setValue(initialValue as Defined<T>)\n\n // Register wire for post-hydration refresh\n registeredWires.set(key, {\n getValue,\n setValue,\n subscribe,\n })\n\n rwpLog('[RWP] Wire registered, total wires:', registeredWires.size, 'keys:', Array.from(registeredWires.keys()))\n\n return {\n ...wire,\n getValue,\n setValue,\n subscribe,\n }\n}\n","'use client'\n\nimport { useEffect, useRef } from 'react'\nimport { upgradeStorage } from '@/react-wire-persisted'\nimport { getHasHydrated, getIsClient } from '@/utils'\n\nexport type UseHydrationOptions = {\n autoUpgrade?: boolean\n onUpgrade?: () => void\n}\n\n/**\n * React hook that handles automatic storage upgrade after hydration\n * This should be used in the root component of your application\n *\n * @param {Object} options Configuration options\n * @param {Boolean} options.autoUpgrade Whether to automatically upgrade storage (default: true)\n * @param {Function} options.onUpgrade Callback called when storage is upgraded\n */\nexport const useHydration = (options: UseHydrationOptions = {}) => {\n const { autoUpgrade = true, onUpgrade } = options\n\n const hasUpgraded = useRef(false)\n\n useEffect(() => {\n if (!autoUpgrade || hasUpgraded.current || !getIsClient()) return\n\n const attemptUpgrade = () => {\n if (getHasHydrated() && !hasUpgraded.current) {\n const upgraded = upgradeStorage()\n\n if (upgraded) {\n hasUpgraded.current = true\n onUpgrade?.()\n }\n }\n }\n\n // Try to upgrade immediately if already hydrated\n attemptUpgrade()\n\n // Also try after a short delay to ensure DOM is ready\n const timeoutId = setTimeout(attemptUpgrade, 0)\n\n return () => clearTimeout(timeoutId)\n }, [autoUpgrade, onUpgrade])\n\n return {\n hasUpgraded: hasUpgraded.current,\n }\n}\n","'use client'\n\nimport type { ReactNode } from 'react'\nimport { useHydration } from '@/hooks/useHydration'\n\n/**\n * A Next.js App Router compatible component that handles automatic storage upgrade\n * after hydration. Use this in your root layout.\n *\n * @param {Object} props Component props\n * @param {React.ReactNode} props.children Child components to render\n * @param {Function} props.onUpgrade Callback called when storage is upgraded\n * @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)\n */\nexport type HydrationProviderProps = {\n children: ReactNode\n onUpgrade?: () => void\n autoUpgrade?: boolean\n}\n\nconst HydrationProvider = ({ children, onUpgrade, autoUpgrade = true }: HydrationProviderProps) => {\n useHydration({ onUpgrade, autoUpgrade })\n\n return children\n}\n\nexport default HydrationProvider\n","import { fakeLocalStorage } from '@/utils'\nimport LocalStorageProvider from './LocalStorageProvider'\n\nexport class MemoryStorageProvider extends LocalStorageProvider {\n constructor(namespace: string, registry: Record<string, unknown> = {}) {\n super(namespace, registry)\n }\n\n getStorage() {\n return fakeLocalStorage\n }\n}\n\nexport default MemoryStorageProvider\n"],"mappings":"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"}
package/nextjs.js CHANGED
@@ -1,9 +1,7 @@
1
1
  'use client'
2
2
 
3
- import { useEffect, useState } from 'react'
4
-
5
3
  /**
6
- * Simple Next.js App Router compatible component for react-wire-persisted
4
+ * Next.js App Router compatible component for react-wire-persisted
7
5
  * This component handles the localStorage hydration issue in Next.js
8
6
  *
9
7
  * Usage:
@@ -13,28 +11,7 @@ import { useEffect, useState } from 'react'
13
11
  * <YourApp />
14
12
  * </NextJSHydrationProvider>
15
13
  */
16
- export function NextJSHydrationProvider({ children }) {
17
- const [isHydrated, setIsHydrated] = useState(false)
18
-
19
- useEffect(() => {
20
- // Mark as hydrated and try to upgrade storage
21
- setIsHydrated(true)
22
-
23
- // Dynamically import and upgrade storage after hydration
24
- if (typeof window !== 'undefined') {
25
- import('./dist/react-wire-persisted.js')
26
- .then(module => {
27
- // Attempt to upgrade storage
28
- if (module.upgradeStorage) {
29
- module.upgradeStorage()
30
- }
31
- })
32
- .catch(err => {
33
- console.warn('Failed to upgrade react-wire-persisted storage:', err)
34
- })
35
- }
36
- }, [])
37
14
 
38
- // Render children immediately, but storage upgrade happens after hydration
39
- return children
40
- }
15
+ // Re-export HydrationProvider as NextJSHydrationProvider for backwards compatibility
16
+ // Also export the main HydrationProvider
17
+ export { HydrationProvider as NextJSHydrationProvider, HydrationProvider } from './src/components/HydrationProvider.js'
package/package.json CHANGED
@@ -1,80 +1,96 @@
1
1
  {
2
- "name": "react-wire-persisted",
3
- "version": "2.0.1",
4
- "author": "Wesley Bliss <wesley.bliss@gmail.com>",
5
- "license": "MIT",
6
- "type": "module",
7
- "main": "./dist/react-wire-persisted.umd.cjs",
8
- "module": "./dist/index.js",
9
- "files": [
10
- "src",
11
- "dist",
12
- "client.js",
13
- "client.mjs",
14
- "nextjs.js"
15
- ],
16
- "exports": {
17
- ".": {
18
- "import": "./dist/react-wire-persisted.js",
19
- "require": "./dist/react-wire-persisted.umd.cjs"
2
+ "name": "react-wire-persisted",
3
+ "version": "3.0.0",
4
+ "author": "Wesley Bliss <wesley.bliss@gmail.com>",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/react-wire-persisted.umd.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "src",
12
+ "dist",
13
+ "client.js",
14
+ "client.mjs",
15
+ "nextjs.js"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/react-wire-persisted.js",
21
+ "require": "./dist/react-wire-persisted.umd.cjs"
22
+ },
23
+ "./client": {
24
+ "import": "./client.mjs",
25
+ "require": "./client.js"
26
+ },
27
+ "./nextjs": {
28
+ "import": "./nextjs.js",
29
+ "require": "./nextjs.js"
30
+ }
31
+ },
32
+ "scripts": {
33
+ "dev": "vite --host --config config/vite.config.development.ts",
34
+ "clean": "rm -rf dist",
35
+ "build": "pnpm clean && pnpm build:js",
36
+ "build:js": "vite build --config config/vite.config.production.ts && cp dist/react-wire-persisted.js dist/index.js",
37
+ "prepublishOnly": "pnpm build",
38
+ "check": "pnpm tsc --noEmit --skipLibCheck",
39
+ "lint": "biome check --write",
40
+ "lint:format": "biome format --write",
41
+ "lint:lint": "biome lint --write",
42
+ "test": "pnpm test:unit",
43
+ "test:unit": "NODE_ENV=test vitest run",
44
+ "test:unit:only": "NODE_ENV=test vitest run -t ",
45
+ "test:unit:coverage": "NODE_ENV=test vitest run --no-color --reporter=junit --coverage --outputFile=coverage/report.xml",
46
+ "yalc": "pnpm build && yalc push"
47
+ },
48
+ "devDependencies": {
49
+ "@biomejs/biome": "^2.4.8",
50
+ "@forminator/react-wire": "^0.7.0",
51
+ "@testing-library/dom": "^10.4.1",
52
+ "@testing-library/jest-dom": "^6.9.1",
53
+ "@testing-library/react": "^16.3.2",
54
+ "@testing-library/user-event": "^14.6.1",
55
+ "@types/react": "^19.2.14",
56
+ "@types/react-dom": "^19.2.3",
57
+ "@vitejs/plugin-react": "^6.0.1",
58
+ "@vitest/coverage-v8": "^4.1.0",
59
+ "browserslist": "^4.28.1",
60
+ "dotenv": "^17.3.1",
61
+ "esbuild": "^0.27.4",
62
+ "jest": "^30.3.0",
63
+ "jest-environment-jsdom": "^30.3.0",
64
+ "np": "^11.0.2",
65
+ "react": "^19.2.4",
66
+ "react-dom": "^19.2.4",
67
+ "rollup-plugin-inject-process-env": "^1.3.1",
68
+ "snapshot-diff": "^0.10.0",
69
+ "typescript": "^5.9.3",
70
+ "vite": "8.0.1",
71
+ "vite-jest": "^0.1.4",
72
+ "vite-plugin-dts": "^4.5.4",
73
+ "vite-tsconfig-paths": "^6.1.1",
74
+ "vitest": "^4.1.0"
20
75
  },
21
- "./client": {
22
- "import": "./client.mjs",
23
- "require": "./client.js"
76
+ "peerDependencies": {
77
+ "@forminator/react-wire": "^0.7.0",
78
+ "react": "^19.0.0",
79
+ "react-dom": "^19.0.0"
24
80
  },
25
- "./nextjs": {
26
- "import": "./nextjs.js",
27
- "require": "./nextjs.js"
81
+ "browserslist": {
82
+ "production": [
83
+ ">0.2%",
84
+ "not dead",
85
+ "not op_mini all"
86
+ ],
87
+ "development": [
88
+ "last 1 chrome version",
89
+ "last 1 firefox version",
90
+ "last 1 safari version, not dead"
91
+ ]
92
+ },
93
+ "pnpm": {
94
+ "neverBuiltDependencies": []
28
95
  }
29
- },
30
- "scripts": {
31
- "dev": "vite --host --config config/vite.config.development.js",
32
- "build": "vite build --config config/vite.config.production.js && cp dist/react-wire-persisted.js dist/index.js",
33
- "test:unit": "NODE_ENV=test vitest run",
34
- "test:unit:only": "NODE_ENV=test vitest run -t ",
35
- "test:unit:coverage": "NODE_ENV=test vitest run --no-color --reporter=junit --coverage --outputFile=coverage/report.xml",
36
- "yalc": "pnpm build && yalc push"
37
- },
38
- "devDependencies": {
39
- "@forminator/react-wire": "^0.7.0",
40
- "@testing-library/dom": "^10.4.1",
41
- "@testing-library/jest-dom": "^6.8.0",
42
- "@testing-library/react": "^16.3.0",
43
- "@testing-library/user-event": "^14.6.1",
44
- "@vitejs/plugin-react": "^5.0.3",
45
- "@vitest/coverage-v8": "^4.0.18",
46
- "browserslist": "^4.26.2",
47
- "dotenv": "^17.2.2",
48
- "esbuild": "^0.25.10",
49
- "jest": "^30.1.3",
50
- "jest-environment-jsdom": "^30.1.2",
51
- "np": "^10.2.0",
52
- "react": "^19.1.1",
53
- "react-dom": "^19.1.1",
54
- "rollup-plugin-inject-process-env": "^1.3.1",
55
- "snapshot-diff": "^0.10.0",
56
- "vite": "^7.1.7",
57
- "vite-jest": "^0.1.4",
58
- "vitest": "^4.0.18"
59
- },
60
- "peerDependencies": {
61
- "@forminator/react-wire": "^0.7.0",
62
- "react": "^18.3.1 || ^19.0.0",
63
- "react-dom": "^18.3.1 || ^19.0.0"
64
- },
65
- "browserslist": {
66
- "production": [
67
- ">0.2%",
68
- "not dead",
69
- "not op_mini all"
70
- ],
71
- "development": [
72
- "last 1 chrome version",
73
- "last 1 firefox version",
74
- "last 1 safari version"
75
- ]
76
- },
77
- "pnpm": {
78
- "neverBuiltDependencies": []
79
- }
80
96
  }
@@ -1,6 +1,7 @@
1
1
  'use client'
2
2
 
3
- import { useHydration } from '../hooks/useHydration'
3
+ import type { ReactNode } from 'react'
4
+ import { useHydration } from '@/hooks/useHydration'
4
5
 
5
6
  /**
6
7
  * A Next.js App Router compatible component that handles automatic storage upgrade
@@ -11,7 +12,16 @@ import { useHydration } from '../hooks/useHydration'
11
12
  * @param {Function} props.onUpgrade Callback called when storage is upgraded
12
13
  * @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)
13
14
  */
14
- export function HydrationProvider({ children, onUpgrade, autoUpgrade = true }) {
15
+ export type HydrationProviderProps = {
16
+ children: ReactNode
17
+ onUpgrade?: () => void
18
+ autoUpgrade?: boolean
19
+ }
20
+
21
+ const HydrationProvider = ({ children, onUpgrade, autoUpgrade = true }: HydrationProviderProps) => {
15
22
  useHydration({ onUpgrade, autoUpgrade })
23
+
16
24
  return children
17
- }
25
+ }
26
+
27
+ export default HydrationProvider
@@ -0,0 +1 @@
1
+ export { default as HydrationProvider } from './HydrationProvider'
@@ -0,0 +1,16 @@
1
+ // noinspection ES6ConvertVarToLetConst
2
+
3
+ import type { Wire } from '@forminator/react-wire'
4
+ import type RWPStorageProvider from '@/providers/RWPStorageProvider'
5
+
6
+ declare global {
7
+ interface Window {
8
+ app?: Record<string, unknown>
9
+ }
10
+
11
+ var __RWP_LOGGING_ENABLED__: boolean | undefined
12
+ var __RWP_STORAGE__: RWPStorageProvider | undefined
13
+ var __RWP_REGISTERED_WIRES__:
14
+ | Map<string, Wire<unknown> | Pick<Wire<unknown>, 'getValue' | 'setValue' | 'subscribe'>>
15
+ | undefined
16
+ }
@@ -1,8 +1,13 @@
1
1
  'use client'
2
2
 
3
3
  import { useEffect, useRef } from 'react'
4
- import { upgradeStorage } from '../react-wire-persisted'
5
- import { getIsClient, getHasHydrated } from '../utils'
4
+ import { upgradeStorage } from '@/react-wire-persisted'
5
+ import { getHasHydrated, getIsClient } from '@/utils'
6
+
7
+ export type UseHydrationOptions = {
8
+ autoUpgrade?: boolean
9
+ onUpgrade?: () => void
10
+ }
6
11
 
7
12
  /**
8
13
  * React hook that handles automatic storage upgrade after hydration
@@ -12,47 +17,35 @@ import { getIsClient, getHasHydrated } from '../utils'
12
17
  * @param {Boolean} options.autoUpgrade Whether to automatically upgrade storage (default: true)
13
18
  * @param {Function} options.onUpgrade Callback called when storage is upgraded
14
19
  */
15
- export const useHydration = (options = {}) => {
16
-
17
- const {
18
- autoUpgrade = true,
19
- onUpgrade
20
- } = options
21
-
20
+ export const useHydration = (options: UseHydrationOptions = {}) => {
21
+ const { autoUpgrade = true, onUpgrade } = options
22
+
22
23
  const hasUpgraded = useRef(false)
23
-
24
+
24
25
  useEffect(() => {
25
-
26
- if (!autoUpgrade || hasUpgraded.current || !getIsClient())
27
- return
28
-
26
+ if (!autoUpgrade || hasUpgraded.current || !getIsClient()) return
27
+
29
28
  const attemptUpgrade = () => {
30
-
31
29
  if (getHasHydrated() && !hasUpgraded.current) {
32
-
33
30
  const upgraded = upgradeStorage()
34
-
31
+
35
32
  if (upgraded) {
36
33
  hasUpgraded.current = true
37
34
  onUpgrade?.()
38
35
  }
39
-
40
36
  }
41
-
42
37
  }
43
-
38
+
44
39
  // Try to upgrade immediately if already hydrated
45
40
  attemptUpgrade()
46
-
41
+
47
42
  // Also try after a short delay to ensure DOM is ready
48
43
  const timeoutId = setTimeout(attemptUpgrade, 0)
49
-
44
+
50
45
  return () => clearTimeout(timeoutId)
51
-
52
46
  }, [autoUpgrade, onUpgrade])
53
-
47
+
54
48
  return {
55
- hasUpgraded: hasUpgraded.current
49
+ hasUpgraded: hasUpgraded.current,
56
50
  }
57
-
58
51
  }
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ import * as utils from '@/utils'
2
+
3
+ export * from '@/components'
4
+ export * from '@/hooks/useHydration'
5
+ export * from '@/providers/LocalStorageProvider'
6
+ export * from '@/providers/MemoryStorageProvider'
7
+ export * from '@/providers/RWPStorageProvider'
8
+ export * from '@/react-wire-persisted'
9
+ export { utils }
@@ -0,0 +1,145 @@
1
+ import RWPStorageProvider from '@/providers/RWPStorageProvider'
2
+ import type { AnyStorage } from '@/types'
3
+ import { fakeLocalStorage, isLocalStorageAvailable } from '@/utils'
4
+
5
+ /**
6
+ * A storage provider for `localStorage`
7
+ * @see `RWPStorageProvider.ts` for documentation
8
+ */
9
+ export class LocalStorageProvider extends RWPStorageProvider {
10
+ public storage: AnyStorage
11
+ private _isUsingFakeStorage: boolean
12
+
13
+ constructor(namespace: string, registry: Record<string, unknown> = {}) {
14
+ super(namespace, registry)
15
+
16
+ // Always start with fake storage to prevent hydration mismatches
17
+ // Will be upgraded to real storage after hydration via upgradeToRealStorage()
18
+ this.storage = fakeLocalStorage
19
+ this._isUsingFakeStorage = true
20
+ }
21
+
22
+ getStorage(): AnyStorage {
23
+ // Use the isomorphic utility to check localStorage availability
24
+ if (isLocalStorageAvailable()) return window.localStorage
25
+
26
+ // Fallback to fake localStorage for SSR or when localStorage is disabled
27
+ return fakeLocalStorage
28
+ }
29
+
30
+ setNamespace(namespace: string) {
31
+ if (!this.namespace) {
32
+ this.namespace = namespace
33
+ return
34
+ }
35
+
36
+ if (this.namespace === namespace) return
37
+
38
+ const items = JSON.parse(JSON.stringify(this.getAll()))
39
+
40
+ this.removeAll()
41
+
42
+ for (const [key, value] of Object.entries(items)) {
43
+ const newKey = key.replace(this.namespace, namespace)
44
+ this.setItem(newKey, value)
45
+ }
46
+
47
+ this.namespace = namespace
48
+ }
49
+
50
+ getItem(key: string) {
51
+ const val = this.storage.getItem(key)
52
+
53
+ if (val === undefined || val === null) return null
54
+
55
+ try {
56
+ return JSON.parse(val)
57
+ } catch (_) {
58
+ return val
59
+ }
60
+ }
61
+
62
+ setItem(key: string, value: unknown) {
63
+ // Don't allow "null" & similar values to be stringified
64
+ if (value === undefined || value === null) return this.removeItem(key)
65
+
66
+ return this.storage.setItem(key, JSON.stringify(value))
67
+ }
68
+
69
+ removeItem(key: string, fromRegistry: boolean = false) {
70
+ if (fromRegistry) delete this.registry[key]
71
+
72
+ return this.storage.removeItem(key)
73
+ }
74
+
75
+ getAll(): Record<string, unknown> {
76
+ const prefixNs = `${this.namespace}.`
77
+
78
+ return Object.keys(this.storage).reduce(
79
+ (acc, it) => {
80
+ if (this.namespace ? it.startsWith(prefixNs) : true) acc[it] = this.storage.getItem(it)
81
+
82
+ return acc
83
+ },
84
+ {} as Record<string, unknown>,
85
+ )
86
+ }
87
+
88
+ _resetAll(useInitialValues: boolean = true, excludedKeys: string[] = [], clearRegistry: boolean = false) {
89
+ const prefixNs = `${this.namespace}.`
90
+
91
+ Object.keys(this.storage).forEach((it) => {
92
+ const isAppKey = this.namespace ? it.startsWith(prefixNs) : true
93
+ const isExcluded = excludedKeys?.includes(it) || false
94
+
95
+ if (!isAppKey || isExcluded) return
96
+
97
+ if (useInitialValues) {
98
+ const isRegistered = Object.hasOwn(this.registry, it)
99
+
100
+ if (isRegistered)
101
+ if (this.registry[it] === undefined || this.registry[it] === null) this.storage.removeItem(it)
102
+ else this.storage.setItem(it, JSON.stringify(this.registry[it]))
103
+ else this.storage.removeItem(it)
104
+ } else {
105
+ this.storage.removeItem(it)
106
+
107
+ if (clearRegistry) delete this.registry[it]
108
+ }
109
+ })
110
+ }
111
+
112
+ resetAll(excludedKeys: string[] = [], clearRegistry: boolean = false) {
113
+ this._resetAll(true, excludedKeys || [], clearRegistry)
114
+ }
115
+
116
+ removeAll(excludedKeys: string[] = [], clearRegistry: boolean = false) {
117
+ this._resetAll(false, excludedKeys || [], clearRegistry)
118
+ }
119
+
120
+ /**
121
+ * Attempt to upgrade from fake storage to real localStorage
122
+ * This is useful for hydration scenarios
123
+ */
124
+ upgradeToRealStorage(): boolean {
125
+ if (!this._isUsingFakeStorage) return false // Already using real storage
126
+
127
+ if (!isLocalStorageAvailable()) return false // Real storage still not available
128
+
129
+ // Simply switch to real storage - don't migrate fake data
130
+ // The existing persisted data in localStorage should be preserved
131
+ this.storage = window.localStorage
132
+ this._isUsingFakeStorage = false
133
+
134
+ return true
135
+ }
136
+
137
+ /**
138
+ * Check if currently using fake storage
139
+ */
140
+ isUsingFakeStorage(): boolean {
141
+ return this._isUsingFakeStorage
142
+ }
143
+ }
144
+
145
+ export default LocalStorageProvider
@@ -0,0 +1,14 @@
1
+ import { fakeLocalStorage } from '@/utils'
2
+ import LocalStorageProvider from './LocalStorageProvider'
3
+
4
+ export class MemoryStorageProvider extends LocalStorageProvider {
5
+ constructor(namespace: string, registry: Record<string, unknown> = {}) {
6
+ super(namespace, registry)
7
+ }
8
+
9
+ getStorage() {
10
+ return fakeLocalStorage
11
+ }
12
+ }
13
+
14
+ export default MemoryStorageProvider