react-wire-persisted 1.5.0 → 2.0.1
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.
- package/client.js +58 -0
- package/client.mjs +108 -0
- package/dist/{react-wire-persisted.mjs → index.js} +133 -55
- package/dist/react-wire-persisted.js +309 -0
- package/dist/react-wire-persisted.umd.cjs +1 -0
- package/nextjs.js +40 -0
- package/package.json +32 -20
- package/src/components/HydrationProvider.js +45 -0
- package/src/components/HydrationProvider.jsx +17 -0
- package/src/components/index.js +1 -0
- package/src/hooks/useHydration.js +58 -0
- package/src/index.js +2 -0
- package/src/providers/LocalStorageProvider.js +69 -27
- package/src/react-wire-persisted.js +21 -0
- package/src/utils/fakeLocalStorage.js +6 -4
- package/src/utils/index.js +1 -0
- package/src/utils/isomorphic.js +46 -0
- package/dist/react-wire-persisted.umd.js +0 -1
package/client.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// This file is specifically for Next.js App Router client components
|
|
4
|
+
// Import this directly: import { HydrationProvider } from 'react-wire-persisted/client'
|
|
5
|
+
|
|
6
|
+
import { useEffect, useRef } from 'react'
|
|
7
|
+
import { upgradeStorage } from './dist/react-wire-persisted.js'
|
|
8
|
+
|
|
9
|
+
// Inline the hydration detection logic to avoid import issues
|
|
10
|
+
const getIsClient = () => typeof window !== 'undefined'
|
|
11
|
+
|
|
12
|
+
const getHasHydrated = () => {
|
|
13
|
+
if (!getIsClient()) return false
|
|
14
|
+
return document.readyState !== 'loading'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A Next.js App Router compatible client component that handles automatic storage upgrade
|
|
19
|
+
* after hydration. Import this from 'react-wire-persisted/client' to ensure proper client-side operation.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} props Component props
|
|
22
|
+
* @param {React.ReactNode} props.children Child components to render
|
|
23
|
+
* @param {Function} props.onUpgrade Callback called when storage is upgraded
|
|
24
|
+
* @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)
|
|
25
|
+
*/
|
|
26
|
+
export function HydrationProvider({ children, onUpgrade, autoUpgrade = true }) {
|
|
27
|
+
const hasUpgraded = useRef(false)
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!autoUpgrade || hasUpgraded.current || !getIsClient()) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const attemptUpgrade = () => {
|
|
35
|
+
if (getHasHydrated() && !hasUpgraded.current) {
|
|
36
|
+
const upgraded = upgradeStorage()
|
|
37
|
+
|
|
38
|
+
if (upgraded) {
|
|
39
|
+
hasUpgraded.current = true
|
|
40
|
+
onUpgrade?.()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Try to upgrade immediately if already hydrated
|
|
46
|
+
attemptUpgrade()
|
|
47
|
+
|
|
48
|
+
// Also try after a short delay to ensure DOM is ready
|
|
49
|
+
const timeoutId = setTimeout(attemptUpgrade, 0)
|
|
50
|
+
|
|
51
|
+
return () => clearTimeout(timeoutId)
|
|
52
|
+
}, [autoUpgrade, onUpgrade])
|
|
53
|
+
|
|
54
|
+
return children
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Re-export the hook from the main package for convenience
|
|
58
|
+
export { useHydration } from './dist/react-wire-persisted.js'
|
package/client.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// This file is specifically for Next.js App Router client components
|
|
4
|
+
// Import this directly: import { HydrationProvider } from 'react-wire-persisted/client.mjs'
|
|
5
|
+
|
|
6
|
+
import { useEffect, useRef } from 'react'
|
|
7
|
+
|
|
8
|
+
// Inline all the necessary utilities to avoid any import issues
|
|
9
|
+
const isClient = typeof window !== 'undefined'
|
|
10
|
+
|
|
11
|
+
const isLocalStorageAvailable = () => {
|
|
12
|
+
if (!isClient) return false
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const testKey = '__rwp_test__'
|
|
16
|
+
window.localStorage.setItem(testKey, 'test')
|
|
17
|
+
window.localStorage.removeItem(testKey)
|
|
18
|
+
return true
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Simple fake storage for SSR
|
|
25
|
+
const fakeStorage = {
|
|
26
|
+
data: {},
|
|
27
|
+
getItem: (key) => fakeStorage.data[key] || null,
|
|
28
|
+
setItem: (key, value) => {
|
|
29
|
+
fakeStorage.data[key] = value
|
|
30
|
+
},
|
|
31
|
+
removeItem: (key) => {
|
|
32
|
+
delete fakeStorage.data[key]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Get current storage (real or fake)
|
|
37
|
+
const getCurrentStorage = () => {
|
|
38
|
+
return isLocalStorageAvailable() ? window.localStorage : fakeStorage
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Simple storage upgrade function
|
|
42
|
+
const upgradeStorageToReal = () => {
|
|
43
|
+
if (!isClient || !isLocalStorageAvailable()) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// If we're already using real localStorage, nothing to do
|
|
48
|
+
if (fakeStorage.data && Object.keys(fakeStorage.data).length === 0) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Move data from fake to real storage
|
|
54
|
+
const fakeData = { ...fakeStorage.data }
|
|
55
|
+
Object.keys(fakeData).forEach(key => {
|
|
56
|
+
if (fakeData[key] != null) {
|
|
57
|
+
window.localStorage.setItem(key, fakeData[key])
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Clear fake storage
|
|
62
|
+
fakeStorage.data = {}
|
|
63
|
+
return true
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A Next.js App Router compatible client component that handles automatic storage upgrade
|
|
71
|
+
* after hydration. Import this from 'react-wire-persisted/client.mjs' to ensure proper client-side operation.
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} props Component props
|
|
74
|
+
* @param {React.ReactNode} props.children Child components to render
|
|
75
|
+
* @param {Function} props.onUpgrade Callback called when storage is upgraded
|
|
76
|
+
* @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)
|
|
77
|
+
*/
|
|
78
|
+
export function HydrationProvider({ children, onUpgrade, autoUpgrade = true }) {
|
|
79
|
+
const hasUpgraded = useRef(false)
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!autoUpgrade || hasUpgraded.current || !isClient) {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const attemptUpgrade = () => {
|
|
87
|
+
// Wait for DOM to be ready
|
|
88
|
+
if (document.readyState !== 'loading' && !hasUpgraded.current) {
|
|
89
|
+
const upgraded = upgradeStorageToReal()
|
|
90
|
+
|
|
91
|
+
if (upgraded) {
|
|
92
|
+
hasUpgraded.current = true
|
|
93
|
+
onUpgrade?.()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Try to upgrade immediately if DOM is already ready
|
|
99
|
+
attemptUpgrade()
|
|
100
|
+
|
|
101
|
+
// Also try after a short delay to ensure everything is ready
|
|
102
|
+
const timeoutId = setTimeout(attemptUpgrade, 100)
|
|
103
|
+
|
|
104
|
+
return () => clearTimeout(timeoutId)
|
|
105
|
+
}, [autoUpgrade, onUpgrade])
|
|
106
|
+
|
|
107
|
+
return children
|
|
108
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createWire as
|
|
1
|
+
import { createWire as U } from "@forminator/react-wire";
|
|
2
|
+
import { useRef as b, useEffect as O } from "react";
|
|
2
3
|
(function() {
|
|
3
4
|
const s = {};
|
|
4
5
|
try {
|
|
@@ -10,42 +11,62 @@ import { createWire as b } from "@forminator/react-wire";
|
|
|
10
11
|
}
|
|
11
12
|
globalThis.process = { env: s };
|
|
12
13
|
})();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
const t = e ||
|
|
17
|
-
return s ? Object.keys(t).reduce((r,
|
|
14
|
+
const y = {}, w = (s) => {
|
|
15
|
+
y[s] = s;
|
|
16
|
+
}, j = (s) => w(s), x = () => y, N = (s, e = null) => {
|
|
17
|
+
const t = e || y;
|
|
18
|
+
return s ? Object.keys(t).reduce((r, o) => ({
|
|
18
19
|
...r,
|
|
19
|
-
[
|
|
20
|
+
[o]: `${s}.${t[o]}`
|
|
20
21
|
}), {}) : t;
|
|
21
22
|
}, g = {
|
|
23
|
+
__IS_FAKE_LOCAL_STORAGE__: !0
|
|
24
|
+
}, h = {
|
|
22
25
|
getItem: (s) => g[s],
|
|
23
26
|
setItem: (s, e) => {
|
|
24
27
|
g[s] = e;
|
|
25
28
|
},
|
|
26
29
|
removeItem: (s) => {
|
|
27
30
|
delete g[s];
|
|
31
|
+
},
|
|
32
|
+
// Make Object.keys() work properly for _resetAll method
|
|
33
|
+
...g
|
|
34
|
+
};
|
|
35
|
+
let S = !1, p = !1;
|
|
36
|
+
typeof window < "u" && (S = !0, document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", () => {
|
|
37
|
+
p = !0;
|
|
38
|
+
}) : p = !0);
|
|
39
|
+
const m = () => S, _ = () => p, d = () => {
|
|
40
|
+
if (!S) return !1;
|
|
41
|
+
try {
|
|
42
|
+
const s = "__rwp_test__";
|
|
43
|
+
return window.localStorage.setItem(s, "test"), window.localStorage.removeItem(s), !0;
|
|
44
|
+
} catch {
|
|
45
|
+
return !1;
|
|
28
46
|
}
|
|
29
|
-
},
|
|
47
|
+
}, A = (s) => {
|
|
30
48
|
const e = typeof s;
|
|
31
49
|
return s === null ? !0 : Array.isArray(s) || e === "object" ? !1 : e !== "function";
|
|
32
|
-
},
|
|
50
|
+
}, k = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
33
51
|
__proto__: null,
|
|
34
|
-
addKey:
|
|
35
|
-
fakeLocalStorage:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
addKey: w,
|
|
53
|
+
fakeLocalStorage: h,
|
|
54
|
+
getHasHydrated: _,
|
|
55
|
+
getIsClient: m,
|
|
56
|
+
getKeys: x,
|
|
57
|
+
getPrefixedKeys: N,
|
|
58
|
+
isLocalStorageAvailable: d,
|
|
59
|
+
isPrimitive: A,
|
|
60
|
+
key: j
|
|
40
61
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
41
|
-
class
|
|
62
|
+
class I {
|
|
42
63
|
/**
|
|
43
64
|
* Initializes the class
|
|
44
65
|
* @param {String} namespace Namespace to prefix all keys with. Mostly used for the logging & reset functions
|
|
45
66
|
* @param {Object} registry (Optional) Initialize the storage provider with an existing registry
|
|
46
67
|
*/
|
|
47
68
|
constructor(e, t) {
|
|
48
|
-
if (new.target ===
|
|
69
|
+
if (new.target === I)
|
|
49
70
|
throw TypeError("StorageProvider is abstract. Extend this class to implement it");
|
|
50
71
|
this.namespace = e || null, this.registry = t || /* istanbul ignore next */
|
|
51
72
|
{};
|
|
@@ -122,16 +143,12 @@ class h {
|
|
|
122
143
|
removeAll(e = []) {
|
|
123
144
|
}
|
|
124
145
|
}
|
|
125
|
-
class
|
|
146
|
+
class T extends I {
|
|
126
147
|
constructor(e = null, t = {}) {
|
|
127
|
-
super(e, t), this.storage = this.getStorage();
|
|
148
|
+
super(e, t), this.storage = this.getStorage(), this._isUsingFakeStorage = !d();
|
|
128
149
|
}
|
|
129
150
|
getStorage() {
|
|
130
|
-
|
|
131
|
-
return window.localStorage;
|
|
132
|
-
} catch {
|
|
133
|
-
return console.warn(new Error("LocalStorageProvider: localStorage not supported")), g;
|
|
134
|
-
}
|
|
151
|
+
return d() ? window.localStorage : h;
|
|
135
152
|
}
|
|
136
153
|
setNamespace(e) {
|
|
137
154
|
if (!this.namespace) {
|
|
@@ -142,9 +159,9 @@ class v extends h {
|
|
|
142
159
|
return;
|
|
143
160
|
const t = JSON.parse(JSON.stringify(this.getAll()));
|
|
144
161
|
this.removeAll();
|
|
145
|
-
for (const [r,
|
|
146
|
-
const
|
|
147
|
-
this.setItem(
|
|
162
|
+
for (const [r, o] of Object.entries(t)) {
|
|
163
|
+
const n = r.replace(this.namespace, e);
|
|
164
|
+
this.setItem(n, o);
|
|
148
165
|
}
|
|
149
166
|
this.namespace = e;
|
|
150
167
|
}
|
|
@@ -160,7 +177,7 @@ class v extends h {
|
|
|
160
177
|
}
|
|
161
178
|
setItem(e, t) {
|
|
162
179
|
let r = t;
|
|
163
|
-
return r != null && (r =
|
|
180
|
+
return r != null && (r = A(t) ? t : JSON.stringify(t)), this.storage.setItem(e, r);
|
|
164
181
|
}
|
|
165
182
|
removeItem(e, t = !1) {
|
|
166
183
|
return t && delete this.registry[e], this.storage.removeItem(e);
|
|
@@ -170,10 +187,10 @@ class v extends h {
|
|
|
170
187
|
return Object.keys(this.storage).reduce((t, r) => ((!this.namespace || r.startsWith(e)) && (t[r] = this.storage.getItem(r)), t), {});
|
|
171
188
|
}
|
|
172
189
|
_resetAll(e = !0, t = [], r = !1) {
|
|
173
|
-
const
|
|
174
|
-
Object.keys(
|
|
175
|
-
const
|
|
176
|
-
!
|
|
190
|
+
const o = `${this.namespace}.`;
|
|
191
|
+
Object.keys(this.storage).forEach((n) => {
|
|
192
|
+
const a = this.namespace ? n.startsWith(o) : !0, l = t?.includes(n) || !1;
|
|
193
|
+
!a || l || (e ? Object.prototype.hasOwnProperty.call(this.registry, n) ? this.storage.setItem(n, this.registry[n]) : this.storage.removeItem(n) : (this.storage.removeItem(n), r && delete this.registry[n]));
|
|
177
194
|
});
|
|
178
195
|
}
|
|
179
196
|
resetAll(e = [], t = !1) {
|
|
@@ -182,50 +199,111 @@ class v extends h {
|
|
|
182
199
|
removeAll(e = [], t = !1) {
|
|
183
200
|
this._resetAll(!1, e || [], t);
|
|
184
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Attempt to upgrade from fake storage to real localStorage
|
|
204
|
+
* This is useful for hydration scenarios
|
|
205
|
+
*/
|
|
206
|
+
upgradeToRealStorage() {
|
|
207
|
+
if (!this._isUsingFakeStorage || !d())
|
|
208
|
+
return !1;
|
|
209
|
+
const e = { ...this.storage };
|
|
210
|
+
return this.storage = window.localStorage, this._isUsingFakeStorage = !1, Object.keys(e).forEach((t) => {
|
|
211
|
+
if (t !== "__IS_FAKE_LOCAL_STORAGE__" && e[t] != null)
|
|
212
|
+
try {
|
|
213
|
+
this.storage.setItem(t, e[t]);
|
|
214
|
+
} catch {
|
|
215
|
+
return this.storage = h, this._isUsingFakeStorage = !0, !1;
|
|
216
|
+
}
|
|
217
|
+
}), !0;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if currently using fake storage
|
|
221
|
+
*/
|
|
222
|
+
isUsingFakeStorage() {
|
|
223
|
+
return this._isUsingFakeStorage;
|
|
224
|
+
}
|
|
185
225
|
}
|
|
186
|
-
const
|
|
226
|
+
const L = {
|
|
187
227
|
logging: {
|
|
188
228
|
enabled: !1
|
|
189
229
|
}
|
|
190
230
|
};
|
|
191
|
-
let
|
|
192
|
-
const
|
|
193
|
-
i.setNamespace(s), i = new
|
|
194
|
-
},
|
|
231
|
+
let v = T, i = new v(), u = { ...L }, f = [];
|
|
232
|
+
const P = () => i.namespace, V = () => i, $ = () => u, C = (s) => {
|
|
233
|
+
i.setNamespace(s), i = new v(s || P());
|
|
234
|
+
}, H = (s) => {
|
|
195
235
|
if (u = {
|
|
196
236
|
...u,
|
|
197
237
|
...s
|
|
198
238
|
}, u.logging.enabled)
|
|
199
239
|
for (console.info("Flushing", f.length, "pending logs"); f.length; )
|
|
200
240
|
console.log(...f.shift());
|
|
201
|
-
},
|
|
241
|
+
}, K = () => {
|
|
242
|
+
if (!m())
|
|
243
|
+
return !1;
|
|
244
|
+
const s = i.upgradeToRealStorage();
|
|
245
|
+
return s && E("react-wire-persisted: Upgraded to real localStorage after hydration"), s;
|
|
246
|
+
}, E = (...s) => {
|
|
202
247
|
u.logging.enabled ? console.log(...s) : f.push(s);
|
|
203
|
-
},
|
|
248
|
+
}, W = (s, e = null) => {
|
|
204
249
|
if (!s && typeof s != "number") throw new Error(
|
|
205
250
|
`createPersistedWire: Key cannot be a falsey value (${s}}`
|
|
206
251
|
);
|
|
207
252
|
i.register(s, e);
|
|
208
|
-
const t =
|
|
253
|
+
const t = U(e), r = () => t.getValue(), o = (c) => (i.setItem(s, c), t.setValue(c)), n = (c) => {
|
|
209
254
|
t.subscribe(c);
|
|
210
|
-
},
|
|
211
|
-
return
|
|
255
|
+
}, a = i.getItem(s), l = a === null ? e : a;
|
|
256
|
+
return E("react-wire-persisted: create", s, {
|
|
212
257
|
value: e,
|
|
213
|
-
storedValue:
|
|
214
|
-
initialValue:
|
|
215
|
-
}),
|
|
258
|
+
storedValue: a,
|
|
259
|
+
initialValue: l
|
|
260
|
+
}), l !== e && o(l), {
|
|
216
261
|
...t,
|
|
217
262
|
getValue: r,
|
|
218
|
-
setValue:
|
|
219
|
-
subscribe:
|
|
263
|
+
setValue: o,
|
|
264
|
+
subscribe: n
|
|
265
|
+
};
|
|
266
|
+
}, J = (s = {}) => {
|
|
267
|
+
const {
|
|
268
|
+
autoUpgrade: e = !0,
|
|
269
|
+
onUpgrade: t
|
|
270
|
+
} = s, r = b(!1);
|
|
271
|
+
return O(() => {
|
|
272
|
+
if (!e || r.current || !m())
|
|
273
|
+
return;
|
|
274
|
+
const o = () => {
|
|
275
|
+
_() && !r.current && K() && (r.current = !0, t?.());
|
|
276
|
+
};
|
|
277
|
+
o();
|
|
278
|
+
const n = setTimeout(o, 0);
|
|
279
|
+
return () => clearTimeout(n);
|
|
280
|
+
}, [e, t]), {
|
|
281
|
+
hasUpgraded: r.current
|
|
220
282
|
};
|
|
221
283
|
};
|
|
284
|
+
function D({ children: s, onUpgrade: e, autoUpgrade: t = !0 }) {
|
|
285
|
+
const r = b(!1);
|
|
286
|
+
return O(() => {
|
|
287
|
+
if (!t || r.current || !m())
|
|
288
|
+
return;
|
|
289
|
+
const o = () => {
|
|
290
|
+
_() && !r.current && K() && (r.current = !0, e?.());
|
|
291
|
+
};
|
|
292
|
+
o();
|
|
293
|
+
const n = setTimeout(o, 0);
|
|
294
|
+
return () => clearTimeout(n);
|
|
295
|
+
}, [t, e]), s;
|
|
296
|
+
}
|
|
222
297
|
export {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
P as
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
298
|
+
D as HydrationProvider,
|
|
299
|
+
W as createPersistedWire,
|
|
300
|
+
L as defaultOptions,
|
|
301
|
+
P as getNamespace,
|
|
302
|
+
$ as getOptions,
|
|
303
|
+
V as getStorage,
|
|
304
|
+
C as setNamespace,
|
|
305
|
+
H as setOptions,
|
|
306
|
+
K as upgradeStorage,
|
|
307
|
+
J as useHydration,
|
|
308
|
+
k as utils
|
|
231
309
|
};
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { createWire as U } from "@forminator/react-wire";
|
|
2
|
+
import { useRef as b, useEffect as O } from "react";
|
|
3
|
+
(function() {
|
|
4
|
+
const s = {};
|
|
5
|
+
try {
|
|
6
|
+
if (process) {
|
|
7
|
+
process.env = Object.assign({}, process.env), Object.assign(process.env, s);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
} catch {
|
|
11
|
+
}
|
|
12
|
+
globalThis.process = { env: s };
|
|
13
|
+
})();
|
|
14
|
+
const y = {}, w = (s) => {
|
|
15
|
+
y[s] = s;
|
|
16
|
+
}, j = (s) => w(s), x = () => y, N = (s, e = null) => {
|
|
17
|
+
const t = e || y;
|
|
18
|
+
return s ? Object.keys(t).reduce((r, o) => ({
|
|
19
|
+
...r,
|
|
20
|
+
[o]: `${s}.${t[o]}`
|
|
21
|
+
}), {}) : t;
|
|
22
|
+
}, g = {
|
|
23
|
+
__IS_FAKE_LOCAL_STORAGE__: !0
|
|
24
|
+
}, h = {
|
|
25
|
+
getItem: (s) => g[s],
|
|
26
|
+
setItem: (s, e) => {
|
|
27
|
+
g[s] = e;
|
|
28
|
+
},
|
|
29
|
+
removeItem: (s) => {
|
|
30
|
+
delete g[s];
|
|
31
|
+
},
|
|
32
|
+
// Make Object.keys() work properly for _resetAll method
|
|
33
|
+
...g
|
|
34
|
+
};
|
|
35
|
+
let S = !1, p = !1;
|
|
36
|
+
typeof window < "u" && (S = !0, document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", () => {
|
|
37
|
+
p = !0;
|
|
38
|
+
}) : p = !0);
|
|
39
|
+
const m = () => S, _ = () => p, d = () => {
|
|
40
|
+
if (!S) return !1;
|
|
41
|
+
try {
|
|
42
|
+
const s = "__rwp_test__";
|
|
43
|
+
return window.localStorage.setItem(s, "test"), window.localStorage.removeItem(s), !0;
|
|
44
|
+
} catch {
|
|
45
|
+
return !1;
|
|
46
|
+
}
|
|
47
|
+
}, A = (s) => {
|
|
48
|
+
const e = typeof s;
|
|
49
|
+
return s === null ? !0 : Array.isArray(s) || e === "object" ? !1 : e !== "function";
|
|
50
|
+
}, k = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
51
|
+
__proto__: null,
|
|
52
|
+
addKey: w,
|
|
53
|
+
fakeLocalStorage: h,
|
|
54
|
+
getHasHydrated: _,
|
|
55
|
+
getIsClient: m,
|
|
56
|
+
getKeys: x,
|
|
57
|
+
getPrefixedKeys: N,
|
|
58
|
+
isLocalStorageAvailable: d,
|
|
59
|
+
isPrimitive: A,
|
|
60
|
+
key: j
|
|
61
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
62
|
+
class I {
|
|
63
|
+
/**
|
|
64
|
+
* Initializes the class
|
|
65
|
+
* @param {String} namespace Namespace to prefix all keys with. Mostly used for the logging & reset functions
|
|
66
|
+
* @param {Object} registry (Optional) Initialize the storage provider with an existing registry
|
|
67
|
+
*/
|
|
68
|
+
constructor(e, t) {
|
|
69
|
+
if (new.target === I)
|
|
70
|
+
throw TypeError("StorageProvider is abstract. Extend this class to implement it");
|
|
71
|
+
this.namespace = e || null, this.registry = t || /* istanbul ignore next */
|
|
72
|
+
{};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sets the namespace for this storage provider, and migrates
|
|
76
|
+
* all stored values to the new namespace
|
|
77
|
+
* @param {String} namespace New namespace for this storage provider
|
|
78
|
+
*/
|
|
79
|
+
/* istanbul ignore next */
|
|
80
|
+
setNamespace(e) {
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Registers an item with it's initial value. This is used for logging, resetting, etc.
|
|
84
|
+
* @param {String} key Storage item's key
|
|
85
|
+
* @param {*} initialValue Storage item's initial value
|
|
86
|
+
*/
|
|
87
|
+
register(e, t) {
|
|
88
|
+
this.registry[e] = t;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Reads an item from storage
|
|
92
|
+
* @param {String} key Key for the item to retrieve
|
|
93
|
+
*/
|
|
94
|
+
/* istanbul ignore next */
|
|
95
|
+
getItem(e) {
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Stores a value
|
|
99
|
+
* @param {String} key Item's storage key
|
|
100
|
+
* @param {String} value Item's value to store
|
|
101
|
+
*/
|
|
102
|
+
/* istanbul ignore next */
|
|
103
|
+
setItem(e, t) {
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Removes an item from storage
|
|
107
|
+
* @param {String} key Item's storage key
|
|
108
|
+
* @param {Boolean} fromRegistry (Optional) If the item should also be removed from the registry
|
|
109
|
+
*/
|
|
110
|
+
/* istanbul ignore next */
|
|
111
|
+
removeItem(e, t = !1) {
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Gets all stored keys & values
|
|
115
|
+
* If a `namespace` was set, only keys prefixed with the namespace will be returned
|
|
116
|
+
*/
|
|
117
|
+
/* istanbul ignore next */
|
|
118
|
+
getAll() {
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
* @param {Boolean} useInitialValues If values should be replaced with their initial values. If false, keys are removed
|
|
123
|
+
* @param {String[]} excludedKeys (Optional) List of keys to exclude
|
|
124
|
+
* @param {Boolean} clearRegistry (Optional) If the registry should also be cleared
|
|
125
|
+
*/
|
|
126
|
+
/* istanbul ignore next */
|
|
127
|
+
_resetAll(e = !0, t = [], r = !1) {
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Resets all values to their initial values
|
|
131
|
+
* If a `namespace` is set, only keys prefixed with the namespace will be reset
|
|
132
|
+
* @param {String[]} excludedKeys (Optional) List of keys to exclude
|
|
133
|
+
*/
|
|
134
|
+
/* istanbul ignore next */
|
|
135
|
+
resetAll(e = []) {
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Removes all items from local storage.
|
|
139
|
+
* If a `namespace` is set, only keys prefixed with the namespace will be removed
|
|
140
|
+
* @param {String[]} excludedKeys (Optional) List of keys to exclude
|
|
141
|
+
*/
|
|
142
|
+
/* istanbul ignore next */
|
|
143
|
+
removeAll(e = []) {
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
class T extends I {
|
|
147
|
+
constructor(e = null, t = {}) {
|
|
148
|
+
super(e, t), this.storage = this.getStorage(), this._isUsingFakeStorage = !d();
|
|
149
|
+
}
|
|
150
|
+
getStorage() {
|
|
151
|
+
return d() ? window.localStorage : h;
|
|
152
|
+
}
|
|
153
|
+
setNamespace(e) {
|
|
154
|
+
if (!this.namespace) {
|
|
155
|
+
this.namespace = e;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (this.namespace === e)
|
|
159
|
+
return;
|
|
160
|
+
const t = JSON.parse(JSON.stringify(this.getAll()));
|
|
161
|
+
this.removeAll();
|
|
162
|
+
for (const [r, o] of Object.entries(t)) {
|
|
163
|
+
const n = r.replace(this.namespace, e);
|
|
164
|
+
this.setItem(n, o);
|
|
165
|
+
}
|
|
166
|
+
this.namespace = e;
|
|
167
|
+
}
|
|
168
|
+
getItem(e) {
|
|
169
|
+
const t = this.storage.getItem(e);
|
|
170
|
+
if (t == null)
|
|
171
|
+
return null;
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(t);
|
|
174
|
+
} catch {
|
|
175
|
+
return t;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
setItem(e, t) {
|
|
179
|
+
let r = t;
|
|
180
|
+
return r != null && (r = A(t) ? t : JSON.stringify(t)), this.storage.setItem(e, r);
|
|
181
|
+
}
|
|
182
|
+
removeItem(e, t = !1) {
|
|
183
|
+
return t && delete this.registry[e], this.storage.removeItem(e);
|
|
184
|
+
}
|
|
185
|
+
getAll() {
|
|
186
|
+
const e = `${this.namespace}.`;
|
|
187
|
+
return Object.keys(this.storage).reduce((t, r) => ((!this.namespace || r.startsWith(e)) && (t[r] = this.storage.getItem(r)), t), {});
|
|
188
|
+
}
|
|
189
|
+
_resetAll(e = !0, t = [], r = !1) {
|
|
190
|
+
const o = `${this.namespace}.`;
|
|
191
|
+
Object.keys(this.storage).forEach((n) => {
|
|
192
|
+
const a = this.namespace ? n.startsWith(o) : !0, l = t?.includes(n) || !1;
|
|
193
|
+
!a || l || (e ? Object.prototype.hasOwnProperty.call(this.registry, n) ? this.storage.setItem(n, this.registry[n]) : this.storage.removeItem(n) : (this.storage.removeItem(n), r && delete this.registry[n]));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
resetAll(e = [], t = !1) {
|
|
197
|
+
this._resetAll(!0, e || [], t);
|
|
198
|
+
}
|
|
199
|
+
removeAll(e = [], t = !1) {
|
|
200
|
+
this._resetAll(!1, e || [], t);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Attempt to upgrade from fake storage to real localStorage
|
|
204
|
+
* This is useful for hydration scenarios
|
|
205
|
+
*/
|
|
206
|
+
upgradeToRealStorage() {
|
|
207
|
+
if (!this._isUsingFakeStorage || !d())
|
|
208
|
+
return !1;
|
|
209
|
+
const e = { ...this.storage };
|
|
210
|
+
return this.storage = window.localStorage, this._isUsingFakeStorage = !1, Object.keys(e).forEach((t) => {
|
|
211
|
+
if (t !== "__IS_FAKE_LOCAL_STORAGE__" && e[t] != null)
|
|
212
|
+
try {
|
|
213
|
+
this.storage.setItem(t, e[t]);
|
|
214
|
+
} catch {
|
|
215
|
+
return this.storage = h, this._isUsingFakeStorage = !0, !1;
|
|
216
|
+
}
|
|
217
|
+
}), !0;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if currently using fake storage
|
|
221
|
+
*/
|
|
222
|
+
isUsingFakeStorage() {
|
|
223
|
+
return this._isUsingFakeStorage;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const L = {
|
|
227
|
+
logging: {
|
|
228
|
+
enabled: !1
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
let v = T, i = new v(), u = { ...L }, f = [];
|
|
232
|
+
const P = () => i.namespace, V = () => i, $ = () => u, C = (s) => {
|
|
233
|
+
i.setNamespace(s), i = new v(s || P());
|
|
234
|
+
}, H = (s) => {
|
|
235
|
+
if (u = {
|
|
236
|
+
...u,
|
|
237
|
+
...s
|
|
238
|
+
}, u.logging.enabled)
|
|
239
|
+
for (console.info("Flushing", f.length, "pending logs"); f.length; )
|
|
240
|
+
console.log(...f.shift());
|
|
241
|
+
}, K = () => {
|
|
242
|
+
if (!m())
|
|
243
|
+
return !1;
|
|
244
|
+
const s = i.upgradeToRealStorage();
|
|
245
|
+
return s && E("react-wire-persisted: Upgraded to real localStorage after hydration"), s;
|
|
246
|
+
}, E = (...s) => {
|
|
247
|
+
u.logging.enabled ? console.log(...s) : f.push(s);
|
|
248
|
+
}, W = (s, e = null) => {
|
|
249
|
+
if (!s && typeof s != "number") throw new Error(
|
|
250
|
+
`createPersistedWire: Key cannot be a falsey value (${s}}`
|
|
251
|
+
);
|
|
252
|
+
i.register(s, e);
|
|
253
|
+
const t = U(e), r = () => t.getValue(), o = (c) => (i.setItem(s, c), t.setValue(c)), n = (c) => {
|
|
254
|
+
t.subscribe(c);
|
|
255
|
+
}, a = i.getItem(s), l = a === null ? e : a;
|
|
256
|
+
return E("react-wire-persisted: create", s, {
|
|
257
|
+
value: e,
|
|
258
|
+
storedValue: a,
|
|
259
|
+
initialValue: l
|
|
260
|
+
}), l !== e && o(l), {
|
|
261
|
+
...t,
|
|
262
|
+
getValue: r,
|
|
263
|
+
setValue: o,
|
|
264
|
+
subscribe: n
|
|
265
|
+
};
|
|
266
|
+
}, J = (s = {}) => {
|
|
267
|
+
const {
|
|
268
|
+
autoUpgrade: e = !0,
|
|
269
|
+
onUpgrade: t
|
|
270
|
+
} = s, r = b(!1);
|
|
271
|
+
return O(() => {
|
|
272
|
+
if (!e || r.current || !m())
|
|
273
|
+
return;
|
|
274
|
+
const o = () => {
|
|
275
|
+
_() && !r.current && K() && (r.current = !0, t?.());
|
|
276
|
+
};
|
|
277
|
+
o();
|
|
278
|
+
const n = setTimeout(o, 0);
|
|
279
|
+
return () => clearTimeout(n);
|
|
280
|
+
}, [e, t]), {
|
|
281
|
+
hasUpgraded: r.current
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
function D({ children: s, onUpgrade: e, autoUpgrade: t = !0 }) {
|
|
285
|
+
const r = b(!1);
|
|
286
|
+
return O(() => {
|
|
287
|
+
if (!t || r.current || !m())
|
|
288
|
+
return;
|
|
289
|
+
const o = () => {
|
|
290
|
+
_() && !r.current && K() && (r.current = !0, e?.());
|
|
291
|
+
};
|
|
292
|
+
o();
|
|
293
|
+
const n = setTimeout(o, 0);
|
|
294
|
+
return () => clearTimeout(n);
|
|
295
|
+
}, [t, e]), s;
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
D as HydrationProvider,
|
|
299
|
+
W as createPersistedWire,
|
|
300
|
+
L as defaultOptions,
|
|
301
|
+
P as getNamespace,
|
|
302
|
+
$ as getOptions,
|
|
303
|
+
V as getStorage,
|
|
304
|
+
C as setNamespace,
|
|
305
|
+
H as setOptions,
|
|
306
|
+
K as upgradeStorage,
|
|
307
|
+
J as useHydration,
|
|
308
|
+
k as utils
|
|
309
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
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"})}));
|
package/nextjs.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Simple Next.js App Router compatible component for react-wire-persisted
|
|
7
|
+
* This component handles the localStorage hydration issue in Next.js
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { NextJSHydrationProvider } from 'react-wire-persisted/nextjs'
|
|
11
|
+
*
|
|
12
|
+
* <NextJSHydrationProvider>
|
|
13
|
+
* <YourApp />
|
|
14
|
+
* </NextJSHydrationProvider>
|
|
15
|
+
*/
|
|
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
|
+
|
|
38
|
+
// Render children immediately, but storage upgrade happens after hydration
|
|
39
|
+
return children
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,54 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-wire-persisted",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"author": "Wesley Bliss <wesley.bliss@gmail.com>",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/react-wire-persisted.umd.cjs",
|
|
8
|
-
"module": "./dist/
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
9
|
"files": [
|
|
10
10
|
"src",
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"client.js",
|
|
13
|
+
"client.mjs",
|
|
14
|
+
"nextjs.js"
|
|
12
15
|
],
|
|
13
16
|
"exports": {
|
|
14
17
|
".": {
|
|
15
|
-
"import": "./dist/react-wire-persisted.
|
|
18
|
+
"import": "./dist/react-wire-persisted.js",
|
|
16
19
|
"require": "./dist/react-wire-persisted.umd.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"import": "./client.mjs",
|
|
23
|
+
"require": "./client.js"
|
|
24
|
+
},
|
|
25
|
+
"./nextjs": {
|
|
26
|
+
"import": "./nextjs.js",
|
|
27
|
+
"require": "./nextjs.js"
|
|
17
28
|
}
|
|
18
29
|
},
|
|
19
30
|
"scripts": {
|
|
20
31
|
"dev": "vite --host --config config/vite.config.development.js",
|
|
21
|
-
"build": "vite build --config config/vite.config.production.js",
|
|
32
|
+
"build": "vite build --config config/vite.config.production.js && cp dist/react-wire-persisted.js dist/index.js",
|
|
22
33
|
"test:unit": "NODE_ENV=test vitest run",
|
|
23
34
|
"test:unit:only": "NODE_ENV=test vitest run -t ",
|
|
24
|
-
"test:unit:coverage": "NODE_ENV=test vitest run --no-color --reporter=junit --coverage --outputFile=coverage/report.xml"
|
|
35
|
+
"test:unit:coverage": "NODE_ENV=test vitest run --no-color --reporter=junit --coverage --outputFile=coverage/report.xml",
|
|
36
|
+
"yalc": "pnpm build && yalc push"
|
|
25
37
|
},
|
|
26
38
|
"devDependencies": {
|
|
27
39
|
"@forminator/react-wire": "^0.7.0",
|
|
28
|
-
"@testing-library/
|
|
40
|
+
"@testing-library/dom": "^10.4.1",
|
|
41
|
+
"@testing-library/jest-dom": "^6.8.0",
|
|
29
42
|
"@testing-library/react": "^16.3.0",
|
|
30
43
|
"@testing-library/user-event": "^14.6.1",
|
|
31
|
-
"@vitejs/plugin-react": "^
|
|
32
|
-
"@vitest/coverage-
|
|
33
|
-
"browserslist": "^4.
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"jest": "^30.
|
|
38
|
-
"jest-environment-jsdom": "^30.0.5",
|
|
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",
|
|
39
51
|
"np": "^10.2.0",
|
|
40
52
|
"react": "^19.1.1",
|
|
41
53
|
"react-dom": "^19.1.1",
|
|
42
54
|
"rollup-plugin-inject-process-env": "^1.3.1",
|
|
43
55
|
"snapshot-diff": "^0.10.0",
|
|
44
|
-
"vite": "^7.
|
|
56
|
+
"vite": "^7.1.7",
|
|
45
57
|
"vite-jest": "^0.1.4",
|
|
46
|
-
"vitest": "^
|
|
58
|
+
"vitest": "^4.0.18"
|
|
47
59
|
},
|
|
48
60
|
"peerDependencies": {
|
|
49
|
-
"@forminator/react-wire": "^0.
|
|
50
|
-
"react": "^
|
|
51
|
-
"react-dom": "^
|
|
61
|
+
"@forminator/react-wire": "^0.7.0",
|
|
62
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
63
|
+
"react-dom": "^18.3.1 || ^19.0.0"
|
|
52
64
|
},
|
|
53
65
|
"browserslist": {
|
|
54
66
|
"production": [
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
import { upgradeStorage } from '../react-wire-persisted'
|
|
5
|
+
import { getIsClient, getHasHydrated } from '../utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A Next.js App Router compatible component that handles automatic storage upgrade
|
|
9
|
+
* after hydration. Use this in your root layout.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} props Component props
|
|
12
|
+
* @param {React.ReactNode} props.children Child components to render
|
|
13
|
+
* @param {Function} props.onUpgrade Callback called when storage is upgraded
|
|
14
|
+
* @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)
|
|
15
|
+
*/
|
|
16
|
+
export function HydrationProvider({ children, onUpgrade, autoUpgrade = true }) {
|
|
17
|
+
const hasUpgraded = useRef(false)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!autoUpgrade || hasUpgraded.current || !getIsClient()) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const attemptUpgrade = () => {
|
|
25
|
+
if (getHasHydrated() && !hasUpgraded.current) {
|
|
26
|
+
const upgraded = upgradeStorage()
|
|
27
|
+
|
|
28
|
+
if (upgraded) {
|
|
29
|
+
hasUpgraded.current = true
|
|
30
|
+
onUpgrade?.()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Try to upgrade immediately if already hydrated
|
|
36
|
+
attemptUpgrade()
|
|
37
|
+
|
|
38
|
+
// Also try after a short delay to ensure DOM is ready
|
|
39
|
+
const timeoutId = setTimeout(attemptUpgrade, 0)
|
|
40
|
+
|
|
41
|
+
return () => clearTimeout(timeoutId)
|
|
42
|
+
}, [autoUpgrade, onUpgrade])
|
|
43
|
+
|
|
44
|
+
return children
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useHydration } from '../hooks/useHydration'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A Next.js App Router compatible component that handles automatic storage upgrade
|
|
7
|
+
* after hydration. Use this in your root layout.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} props Component props
|
|
10
|
+
* @param {React.ReactNode} props.children Child components to render
|
|
11
|
+
* @param {Function} props.onUpgrade Callback called when storage is upgraded
|
|
12
|
+
* @param {Boolean} props.autoUpgrade Whether to automatically upgrade storage (default: true)
|
|
13
|
+
*/
|
|
14
|
+
export function HydrationProvider({ children, onUpgrade, autoUpgrade = true }) {
|
|
15
|
+
useHydration({ onUpgrade, autoUpgrade })
|
|
16
|
+
return children
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HydrationProvider } from './HydrationProvider.js'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react'
|
|
4
|
+
import { upgradeStorage } from '../react-wire-persisted'
|
|
5
|
+
import { getIsClient, getHasHydrated } from '../utils'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* React hook that handles automatic storage upgrade after hydration
|
|
9
|
+
* This should be used in the root component of your application
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} options Configuration options
|
|
12
|
+
* @param {Boolean} options.autoUpgrade Whether to automatically upgrade storage (default: true)
|
|
13
|
+
* @param {Function} options.onUpgrade Callback called when storage is upgraded
|
|
14
|
+
*/
|
|
15
|
+
export const useHydration = (options = {}) => {
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
autoUpgrade = true,
|
|
19
|
+
onUpgrade
|
|
20
|
+
} = options
|
|
21
|
+
|
|
22
|
+
const hasUpgraded = useRef(false)
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
|
|
26
|
+
if (!autoUpgrade || hasUpgraded.current || !getIsClient())
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
const attemptUpgrade = () => {
|
|
30
|
+
|
|
31
|
+
if (getHasHydrated() && !hasUpgraded.current) {
|
|
32
|
+
|
|
33
|
+
const upgraded = upgradeStorage()
|
|
34
|
+
|
|
35
|
+
if (upgraded) {
|
|
36
|
+
hasUpgraded.current = true
|
|
37
|
+
onUpgrade?.()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Try to upgrade immediately if already hydrated
|
|
45
|
+
attemptUpgrade()
|
|
46
|
+
|
|
47
|
+
// Also try after a short delay to ensure DOM is ready
|
|
48
|
+
const timeoutId = setTimeout(attemptUpgrade, 0)
|
|
49
|
+
|
|
50
|
+
return () => clearTimeout(timeoutId)
|
|
51
|
+
|
|
52
|
+
}, [autoUpgrade, onUpgrade])
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
hasUpgraded: hasUpgraded.current
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import StorageProvider from './StorageProvider'
|
|
2
|
-
import { fakeLocalStorage, isPrimitive } from '../utils'
|
|
2
|
+
import { fakeLocalStorage, isPrimitive, isLocalStorageAvailable } from '../utils'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A storage provider for `localStorage`
|
|
@@ -8,24 +8,24 @@ import { fakeLocalStorage, isPrimitive } from '../utils'
|
|
|
8
8
|
class LocalStorageProvider extends StorageProvider {
|
|
9
9
|
|
|
10
10
|
constructor(namespace = null, registry = {}) {
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
super(namespace, registry)
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
this.storage = this.getStorage()
|
|
15
|
-
|
|
15
|
+
this._isUsingFakeStorage = !isLocalStorageAvailable()
|
|
16
|
+
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
getStorage() {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
// Use the isomorphic utility to check localStorage availability
|
|
22
|
+
if (isLocalStorageAvailable()) {
|
|
21
23
|
return window.localStorage
|
|
22
|
-
} catch (e) {
|
|
23
|
-
/* istanbul ignore next */
|
|
24
|
-
console.warn(new Error('LocalStorageProvider: localStorage not supported'))
|
|
25
|
-
/* istanbul ignore next */
|
|
26
|
-
return fakeLocalStorage
|
|
27
24
|
}
|
|
28
|
-
|
|
25
|
+
|
|
26
|
+
// Fallback to fake localStorage for SSR or when localStorage is disabled
|
|
27
|
+
return fakeLocalStorage
|
|
28
|
+
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
setNamespace(namespace) {
|
|
@@ -107,36 +107,36 @@ class LocalStorageProvider extends StorageProvider {
|
|
|
107
107
|
excludedKeys = [],
|
|
108
108
|
clearRegistry = false
|
|
109
109
|
) {
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
const prefixNs = `${this.namespace}.`
|
|
112
|
-
|
|
113
|
-
Object.keys(
|
|
114
|
-
|
|
112
|
+
|
|
113
|
+
Object.keys(this.storage).forEach(it => {
|
|
114
|
+
|
|
115
115
|
const isAppKey = this.namespace ? it.startsWith(prefixNs) : true
|
|
116
116
|
const isExcluded = excludedKeys?.includes(it) || false
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
if (!isAppKey || isExcluded) return
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
if (useInitialValues) {
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
const isRegistered = Object.prototype.hasOwnProperty.call(this.registry, it)
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
if (isRegistered)
|
|
125
125
|
this.storage.setItem(it, this.registry[it])
|
|
126
126
|
else
|
|
127
127
|
this.storage.removeItem(it)
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
} else {
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
this.storage.removeItem(it)
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
if (clearRegistry)
|
|
134
134
|
delete this.registry[it]
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
|
|
138
138
|
})
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
resetAll(excludedKeys = [], clearRegistry = false) {
|
|
@@ -146,7 +146,49 @@ class LocalStorageProvider extends StorageProvider {
|
|
|
146
146
|
removeAll(excludedKeys = [], clearRegistry = false) {
|
|
147
147
|
this._resetAll(false, excludedKeys || [], clearRegistry)
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Attempt to upgrade from fake storage to real localStorage
|
|
152
|
+
* This is useful for hydration scenarios
|
|
153
|
+
*/
|
|
154
|
+
upgradeToRealStorage() {
|
|
155
|
+
|
|
156
|
+
if (!this._isUsingFakeStorage) {
|
|
157
|
+
return false // Already using real storage
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!isLocalStorageAvailable()) {
|
|
161
|
+
return false // Real storage still not available
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const fakeData = { ...this.storage }
|
|
165
|
+
this.storage = window.localStorage
|
|
166
|
+
this._isUsingFakeStorage = false
|
|
167
|
+
|
|
168
|
+
// Migrate data from fake storage to real storage
|
|
169
|
+
Object.keys(fakeData).forEach(key => {
|
|
170
|
+
if (key !== '__IS_FAKE_LOCAL_STORAGE__' && fakeData[key] != null) {
|
|
171
|
+
try {
|
|
172
|
+
this.storage.setItem(key, fakeData[key])
|
|
173
|
+
} catch (e) {
|
|
174
|
+
// If we can't write to localStorage, revert to fake storage
|
|
175
|
+
this.storage = fakeLocalStorage
|
|
176
|
+
this._isUsingFakeStorage = true
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
return true
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Check if currently using fake storage
|
|
187
|
+
*/
|
|
188
|
+
isUsingFakeStorage() {
|
|
189
|
+
return this._isUsingFakeStorage
|
|
190
|
+
}
|
|
191
|
+
|
|
150
192
|
}
|
|
151
193
|
|
|
152
194
|
export default LocalStorageProvider
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createWire } from '@forminator/react-wire'
|
|
2
2
|
import LocalStorageProvider from './providers/LocalStorageProvider'
|
|
3
|
+
import { getIsClient, getHasHydrated } from './utils'
|
|
3
4
|
|
|
4
5
|
export const defaultOptions = {
|
|
5
6
|
logging: {
|
|
@@ -52,6 +53,26 @@ export const setOptions = value => {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Attempts to upgrade the storage provider from fake storage to real localStorage
|
|
58
|
+
* This should be called on the client side after hydration
|
|
59
|
+
*
|
|
60
|
+
* @returns {Boolean} True if upgrade was successful
|
|
61
|
+
*/
|
|
62
|
+
export const upgradeStorage = () => {
|
|
63
|
+
if (!getIsClient()) {
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const upgraded = storage.upgradeToRealStorage()
|
|
68
|
+
|
|
69
|
+
if (upgraded) {
|
|
70
|
+
log('react-wire-persisted: Upgraded to real localStorage after hydration')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return upgraded
|
|
74
|
+
}
|
|
75
|
+
|
|
55
76
|
const log = (...args) => {
|
|
56
77
|
/* istanbul ignore next */
|
|
57
78
|
if (options.logging.enabled)
|
|
@@ -4,11 +4,13 @@ const storage = {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export const fakeLocalStorage = {
|
|
7
|
-
getItem: key =>
|
|
7
|
+
getItem: key => storage[key],
|
|
8
8
|
setItem: (key, value) => {
|
|
9
|
-
|
|
9
|
+
storage[key] = value
|
|
10
10
|
},
|
|
11
11
|
removeItem: key => {
|
|
12
|
-
delete
|
|
13
|
-
}
|
|
12
|
+
delete storage[key]
|
|
13
|
+
},
|
|
14
|
+
// Make Object.keys() work properly for _resetAll method
|
|
15
|
+
...storage
|
|
14
16
|
}
|
package/src/utils/index.js
CHANGED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for handling server-side rendering and client-side hydration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let isClient = false
|
|
6
|
+
let hasHydrated = false
|
|
7
|
+
|
|
8
|
+
// Detect if we're running in a browser environment
|
|
9
|
+
if (typeof window !== 'undefined') {
|
|
10
|
+
isClient = true
|
|
11
|
+
|
|
12
|
+
// Mark as hydrated when the DOM is ready
|
|
13
|
+
if (document.readyState === 'loading') {
|
|
14
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
15
|
+
hasHydrated = true
|
|
16
|
+
})
|
|
17
|
+
} else {
|
|
18
|
+
hasHydrated = true
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if we're running in a browser environment
|
|
24
|
+
*/
|
|
25
|
+
export const getIsClient = () => isClient
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if the client has finished hydrating
|
|
29
|
+
*/
|
|
30
|
+
export const getHasHydrated = () => hasHydrated
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Check if localStorage is available and safe to use
|
|
34
|
+
*/
|
|
35
|
+
export const isLocalStorageAvailable = () => {
|
|
36
|
+
if (!isClient) return false
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const testKey = '__rwp_test__'
|
|
40
|
+
window.localStorage.setItem(testKey, 'test')
|
|
41
|
+
window.localStorage.removeItem(testKey)
|
|
42
|
+
return true
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(n,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("@forminator/react-wire")):typeof define=="function"&&define.amd?define(["exports","@forminator/react-wire"],a):(n=typeof globalThis<"u"?globalThis:n||self,a(n["react-wire-persisted"]={},n.reactWire))})(this,function(n,a){"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 h={},y=s=>{h[s]=s},w=s=>y(s),v=()=>h,A=(s,e=null)=>{const t=e||h;return s?Object.keys(t).reduce((r,o)=>({...r,[o]:`${s}.${t[o]}`}),{}):t},c={getItem:s=>c[s],setItem:(s,e)=>{c[s]=e},removeItem:s=>{delete c[s]}},b=s=>{const e=typeof s;return s===null?!0:Array.isArray(s)||e==="object"?!1:e!=="function"},N=Object.freeze(Object.defineProperty({__proto__:null,addKey:y,fakeLocalStorage:c,getKeys:v,getPrefixedKeys:A,isPrimitive:b,key:w},Symbol.toStringTag,{value:"Module"}));class p{constructor(e,t){if(new.target===p)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 j extends p{constructor(e=null,t={}){super(e,t),this.storage=this.getStorage()}getStorage(){try{return window.localStorage}catch{return console.warn(new Error("LocalStorageProvider: localStorage not supported")),c}}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=b(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(localStorage).forEach(i=>{const u=this.namespace?i.startsWith(o):!0,f=t?.includes(i)||!1;!u||f||(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)}}const O={logging:{enabled:!1}};let I=j,l=new I,g={...O},m=[];const S=()=>l.namespace,P=()=>l,K=()=>g,W=s=>{l.setNamespace(s),l=new I(s||S())},_=s=>{if(g={...g,...s},g.logging.enabled)for(console.info("Flushing",m.length,"pending logs");m.length;)console.log(...m.shift())},E=(...s)=>{g.logging.enabled?console.log(...s):m.push(s)},T=(s,e=null)=>{if(!s&&typeof s!="number")throw new Error(`createPersistedWire: Key cannot be a falsey value (${s}}`);l.register(s,e);const t=a.createWire(e),r=()=>t.getValue(),o=d=>(l.setItem(s,d),t.setValue(d)),i=d=>{t.subscribe(d)},u=l.getItem(s),f=u===null?e:u;return E("react-wire-persisted: create",s,{value:e,storedValue:u,initialValue:f}),f!==e&&o(f),{...t,getValue:r,setValue:o,subscribe:i}};n.createPersistedWire=T,n.defaultOptions=O,n.getNamespace=S,n.getOptions=K,n.getStorage=P,n.setNamespace=W,n.setOptions=_,n.utils=N,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})});
|