@pyreon/storage 0.10.0 → 0.11.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.
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +3 -3
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +12 -6
- package/src/clear.ts +13 -13
- package/src/cookie.ts +16 -20
- package/src/custom.ts +9 -18
- package/src/index.ts +9 -9
- package/src/indexed-db.ts +21 -41
- package/src/local.ts +15 -28
- package/src/registry.ts +2 -5
- package/src/session.ts +10 -21
- package/src/tests/clear.test.ts +43 -43
- package/src/tests/cookie.test.ts +66 -66
- package/src/tests/custom.test.ts +59 -59
- package/src/tests/indexed-db.test.ts +36 -36
- package/src/tests/local.test.ts +98 -98
- package/src/tests/session.test.ts +31 -31
- package/src/types.ts +2 -2
- package/src/utils.ts +9 -13
package/src/tests/local.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { effect } from
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it } from
|
|
3
|
-
import { _resetRegistry, useStorage } from
|
|
1
|
+
import { effect } from "@pyreon/reactivity"
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest"
|
|
3
|
+
import { _resetRegistry, useStorage } from "../index"
|
|
4
4
|
|
|
5
|
-
describe(
|
|
5
|
+
describe("useStorage (localStorage)", () => {
|
|
6
6
|
beforeEach(() => {
|
|
7
7
|
localStorage.clear()
|
|
8
8
|
_resetRegistry()
|
|
@@ -13,203 +13,203 @@ describe('useStorage (localStorage)', () => {
|
|
|
13
13
|
_resetRegistry()
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
-
it(
|
|
17
|
-
const theme = useStorage(
|
|
18
|
-
expect(theme()).toBe(
|
|
16
|
+
it("returns default value when key is not in storage", () => {
|
|
17
|
+
const theme = useStorage("theme", "light")
|
|
18
|
+
expect(theme()).toBe("light")
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
it(
|
|
22
|
-
localStorage.setItem(
|
|
23
|
-
const theme = useStorage(
|
|
24
|
-
expect(theme()).toBe(
|
|
21
|
+
it("reads existing value from localStorage", () => {
|
|
22
|
+
localStorage.setItem("theme", JSON.stringify("dark"))
|
|
23
|
+
const theme = useStorage("theme", "light")
|
|
24
|
+
expect(theme()).toBe("dark")
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
it(
|
|
28
|
-
const theme = useStorage(
|
|
29
|
-
theme.set(
|
|
30
|
-
expect(theme()).toBe(
|
|
31
|
-
expect(JSON.parse(localStorage.getItem(
|
|
27
|
+
it(".set() updates signal and localStorage", () => {
|
|
28
|
+
const theme = useStorage("theme", "light")
|
|
29
|
+
theme.set("dark")
|
|
30
|
+
expect(theme()).toBe("dark")
|
|
31
|
+
expect(JSON.parse(localStorage.getItem("theme")!)).toBe("dark")
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
-
it(
|
|
35
|
-
const count = useStorage(
|
|
34
|
+
it(".update() updates signal and localStorage", () => {
|
|
35
|
+
const count = useStorage("count", 0)
|
|
36
36
|
count.update((n) => n + 1)
|
|
37
37
|
expect(count()).toBe(1)
|
|
38
|
-
expect(JSON.parse(localStorage.getItem(
|
|
38
|
+
expect(JSON.parse(localStorage.getItem("count")!)).toBe(1)
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it(
|
|
42
|
-
const theme = useStorage(
|
|
43
|
-
expect(theme.peek()).toBe(
|
|
41
|
+
it(".peek() reads without subscribing", () => {
|
|
42
|
+
const theme = useStorage("theme", "light")
|
|
43
|
+
expect(theme.peek()).toBe("light")
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
it(
|
|
47
|
-
const theme = useStorage(
|
|
48
|
-
theme.set(
|
|
49
|
-
expect(theme()).toBe(
|
|
46
|
+
it(".remove() clears from storage and resets to default", () => {
|
|
47
|
+
const theme = useStorage("theme", "light")
|
|
48
|
+
theme.set("dark")
|
|
49
|
+
expect(theme()).toBe("dark")
|
|
50
50
|
|
|
51
51
|
theme.remove()
|
|
52
|
-
expect(theme()).toBe(
|
|
53
|
-
expect(localStorage.getItem(
|
|
52
|
+
expect(theme()).toBe("light")
|
|
53
|
+
expect(localStorage.getItem("theme")).toBeNull()
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
it(
|
|
57
|
-
const a = useStorage(
|
|
58
|
-
const b = useStorage(
|
|
56
|
+
it("returns same signal instance for same key (deduplication)", () => {
|
|
57
|
+
const a = useStorage("theme", "light")
|
|
58
|
+
const b = useStorage("theme", "light")
|
|
59
59
|
expect(a).toBe(b)
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
it(
|
|
63
|
-
const a = useStorage(
|
|
64
|
-
const b = useStorage(
|
|
62
|
+
it("returns different signals for different keys", () => {
|
|
63
|
+
const a = useStorage("theme", "light")
|
|
64
|
+
const b = useStorage("lang", "en")
|
|
65
65
|
expect(a).not.toBe(b)
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
it(
|
|
69
|
-
const prefs = useStorage(
|
|
70
|
-
expect(prefs()).toEqual({ sidebar: true, density:
|
|
68
|
+
it("works with objects", () => {
|
|
69
|
+
const prefs = useStorage("prefs", { sidebar: true, density: "comfortable" })
|
|
70
|
+
expect(prefs()).toEqual({ sidebar: true, density: "comfortable" })
|
|
71
71
|
|
|
72
|
-
prefs.set({ sidebar: false, density:
|
|
73
|
-
expect(prefs()).toEqual({ sidebar: false, density:
|
|
74
|
-
expect(JSON.parse(localStorage.getItem(
|
|
72
|
+
prefs.set({ sidebar: false, density: "compact" })
|
|
73
|
+
expect(prefs()).toEqual({ sidebar: false, density: "compact" })
|
|
74
|
+
expect(JSON.parse(localStorage.getItem("prefs")!)).toEqual({
|
|
75
75
|
sidebar: false,
|
|
76
|
-
density:
|
|
76
|
+
density: "compact",
|
|
77
77
|
})
|
|
78
78
|
})
|
|
79
79
|
|
|
80
|
-
it(
|
|
81
|
-
const items = useStorage(
|
|
80
|
+
it("works with arrays", () => {
|
|
81
|
+
const items = useStorage("items", [1, 2, 3])
|
|
82
82
|
expect(items()).toEqual([1, 2, 3])
|
|
83
83
|
|
|
84
84
|
items.set([4, 5])
|
|
85
85
|
expect(items()).toEqual([4, 5])
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
it(
|
|
89
|
-
const flag = useStorage(
|
|
88
|
+
it("works with booleans", () => {
|
|
89
|
+
const flag = useStorage("flag", false)
|
|
90
90
|
flag.set(true)
|
|
91
91
|
expect(flag()).toBe(true)
|
|
92
|
-
expect(JSON.parse(localStorage.getItem(
|
|
92
|
+
expect(JSON.parse(localStorage.getItem("flag")!)).toBe(true)
|
|
93
93
|
})
|
|
94
94
|
|
|
95
|
-
it(
|
|
96
|
-
const count = useStorage(
|
|
95
|
+
it("works with numbers", () => {
|
|
96
|
+
const count = useStorage("count", 42)
|
|
97
97
|
expect(count()).toBe(42)
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
-
it(
|
|
101
|
-
localStorage.setItem(
|
|
102
|
-
const value = useStorage(
|
|
103
|
-
expect(value()).toBe(
|
|
100
|
+
it("handles corrupt storage values gracefully", () => {
|
|
101
|
+
localStorage.setItem("broken", "not valid json{{{")
|
|
102
|
+
const value = useStorage("broken", "fallback")
|
|
103
|
+
expect(value()).toBe("fallback")
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
-
it(
|
|
107
|
-
localStorage.setItem(
|
|
106
|
+
it("calls onError when deserialization fails", () => {
|
|
107
|
+
localStorage.setItem("broken", "{invalid")
|
|
108
108
|
const errors: Error[] = []
|
|
109
|
-
const value = useStorage(
|
|
109
|
+
const value = useStorage("broken", "default", {
|
|
110
110
|
onError: (e) => {
|
|
111
111
|
errors.push(e)
|
|
112
112
|
return undefined
|
|
113
113
|
},
|
|
114
114
|
})
|
|
115
|
-
expect(value()).toBe(
|
|
115
|
+
expect(value()).toBe("default")
|
|
116
116
|
expect(errors).toHaveLength(1)
|
|
117
117
|
})
|
|
118
118
|
|
|
119
|
-
it(
|
|
120
|
-
localStorage.setItem(
|
|
121
|
-
const value = useStorage(
|
|
122
|
-
onError: () =>
|
|
119
|
+
it("onError can return a custom fallback", () => {
|
|
120
|
+
localStorage.setItem("broken", "{invalid")
|
|
121
|
+
const value = useStorage("broken", "default", {
|
|
122
|
+
onError: () => "custom-fallback",
|
|
123
123
|
})
|
|
124
|
-
expect(value()).toBe(
|
|
124
|
+
expect(value()).toBe("custom-fallback")
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
it(
|
|
128
|
-
const date = useStorage(
|
|
127
|
+
it("custom serializer/deserializer work", () => {
|
|
128
|
+
const date = useStorage("date", new Date("2025-01-01"), {
|
|
129
129
|
serializer: (d) => d.toISOString(),
|
|
130
130
|
deserializer: (s) => new Date(s),
|
|
131
131
|
})
|
|
132
132
|
|
|
133
|
-
expect(date()).toEqual(new Date(
|
|
133
|
+
expect(date()).toEqual(new Date("2025-01-01"))
|
|
134
134
|
|
|
135
|
-
const newDate = new Date(
|
|
135
|
+
const newDate = new Date("2025-06-15")
|
|
136
136
|
date.set(newDate)
|
|
137
|
-
expect(localStorage.getItem(
|
|
137
|
+
expect(localStorage.getItem("date")).toBe("2025-06-15T00:00:00.000Z")
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
it(
|
|
141
|
-
const theme = useStorage(
|
|
140
|
+
it("is reactive — works in effects", () => {
|
|
141
|
+
const theme = useStorage("theme", "light")
|
|
142
142
|
const values: string[] = []
|
|
143
143
|
|
|
144
144
|
effect(() => {
|
|
145
145
|
values.push(theme())
|
|
146
146
|
})
|
|
147
147
|
|
|
148
|
-
expect(values).toEqual([
|
|
148
|
+
expect(values).toEqual(["light"])
|
|
149
149
|
|
|
150
|
-
theme.set(
|
|
151
|
-
expect(values).toEqual([
|
|
150
|
+
theme.set("dark")
|
|
151
|
+
expect(values).toEqual(["light", "dark"])
|
|
152
152
|
})
|
|
153
153
|
|
|
154
|
-
it(
|
|
155
|
-
const theme = useStorage(
|
|
154
|
+
it(".subscribe() works", () => {
|
|
155
|
+
const theme = useStorage("theme", "light")
|
|
156
156
|
let callCount = 0
|
|
157
157
|
const unsub = theme.subscribe(() => {
|
|
158
158
|
callCount++
|
|
159
159
|
})
|
|
160
160
|
|
|
161
|
-
theme.set(
|
|
161
|
+
theme.set("dark")
|
|
162
162
|
expect(callCount).toBeGreaterThanOrEqual(1)
|
|
163
163
|
unsub()
|
|
164
164
|
})
|
|
165
165
|
|
|
166
|
-
it(
|
|
167
|
-
const theme = useStorage(
|
|
166
|
+
it(".debug() returns debug info", () => {
|
|
167
|
+
const theme = useStorage("theme", "light")
|
|
168
168
|
const info = theme.debug()
|
|
169
|
-
expect(info.value).toBe(
|
|
169
|
+
expect(info.value).toBe("light")
|
|
170
170
|
})
|
|
171
171
|
|
|
172
|
-
it(
|
|
173
|
-
const theme = useStorage(
|
|
174
|
-
theme.label =
|
|
175
|
-
expect(theme.label).toBe(
|
|
172
|
+
it(".label can be set and read", () => {
|
|
173
|
+
const theme = useStorage("theme", "light")
|
|
174
|
+
theme.label = "theme-signal"
|
|
175
|
+
expect(theme.label).toBe("theme-signal")
|
|
176
176
|
})
|
|
177
177
|
|
|
178
|
-
it(
|
|
179
|
-
const theme = useStorage(
|
|
178
|
+
it("cross-tab sync via storage event", () => {
|
|
179
|
+
const theme = useStorage("theme", "light")
|
|
180
180
|
|
|
181
181
|
// Simulate storage event from another tab
|
|
182
|
-
const event = Object.assign(new Event(
|
|
183
|
-
key:
|
|
184
|
-
newValue: JSON.stringify(
|
|
182
|
+
const event = Object.assign(new Event("storage"), {
|
|
183
|
+
key: "theme",
|
|
184
|
+
newValue: JSON.stringify("dark"),
|
|
185
185
|
storageArea: localStorage,
|
|
186
186
|
})
|
|
187
187
|
window.dispatchEvent(event)
|
|
188
188
|
|
|
189
|
-
expect(theme()).toBe(
|
|
189
|
+
expect(theme()).toBe("dark")
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
-
it(
|
|
193
|
-
const theme = useStorage(
|
|
194
|
-
theme.set(
|
|
192
|
+
it("cross-tab sync with null newValue resets to default", () => {
|
|
193
|
+
const theme = useStorage("theme", "light")
|
|
194
|
+
theme.set("dark")
|
|
195
195
|
|
|
196
|
-
const event = Object.assign(new Event(
|
|
197
|
-
key:
|
|
196
|
+
const event = Object.assign(new Event("storage"), {
|
|
197
|
+
key: "theme",
|
|
198
198
|
newValue: null,
|
|
199
199
|
storageArea: localStorage,
|
|
200
200
|
})
|
|
201
201
|
window.dispatchEvent(event)
|
|
202
202
|
|
|
203
|
-
expect(theme()).toBe(
|
|
203
|
+
expect(theme()).toBe("light")
|
|
204
204
|
})
|
|
205
205
|
|
|
206
|
-
it(
|
|
207
|
-
const a = useStorage(
|
|
208
|
-
a.set(
|
|
206
|
+
it("after remove(), a new useStorage call creates a fresh signal", () => {
|
|
207
|
+
const a = useStorage("temp", "first")
|
|
208
|
+
a.set("modified")
|
|
209
209
|
a.remove()
|
|
210
210
|
|
|
211
|
-
const b = useStorage(
|
|
212
|
-
expect(b()).toBe(
|
|
211
|
+
const b = useStorage("temp", "second")
|
|
212
|
+
expect(b()).toBe("second")
|
|
213
213
|
expect(a).not.toBe(b)
|
|
214
214
|
})
|
|
215
215
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from
|
|
2
|
-
import { _resetRegistry, useSessionStorage } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest"
|
|
2
|
+
import { _resetRegistry, useSessionStorage } from "../index"
|
|
3
3
|
|
|
4
|
-
describe(
|
|
4
|
+
describe("useSessionStorage", () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
sessionStorage.clear()
|
|
7
7
|
_resetRegistry()
|
|
@@ -12,56 +12,56 @@ describe('useSessionStorage', () => {
|
|
|
12
12
|
_resetRegistry()
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
it(
|
|
16
|
-
const step = useSessionStorage(
|
|
15
|
+
it("returns default value when key is not in storage", () => {
|
|
16
|
+
const step = useSessionStorage("step", 0)
|
|
17
17
|
expect(step()).toBe(0)
|
|
18
18
|
})
|
|
19
19
|
|
|
20
|
-
it(
|
|
21
|
-
sessionStorage.setItem(
|
|
22
|
-
const step = useSessionStorage(
|
|
20
|
+
it("reads existing value from sessionStorage", () => {
|
|
21
|
+
sessionStorage.setItem("step", JSON.stringify(3))
|
|
22
|
+
const step = useSessionStorage("step", 0)
|
|
23
23
|
expect(step()).toBe(3)
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
it(
|
|
27
|
-
const step = useSessionStorage(
|
|
26
|
+
it(".set() updates signal and sessionStorage", () => {
|
|
27
|
+
const step = useSessionStorage("step", 0)
|
|
28
28
|
step.set(5)
|
|
29
29
|
expect(step()).toBe(5)
|
|
30
|
-
expect(JSON.parse(sessionStorage.getItem(
|
|
30
|
+
expect(JSON.parse(sessionStorage.getItem("step")!)).toBe(5)
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
const step = useSessionStorage(
|
|
33
|
+
it(".remove() clears from storage and resets to default", () => {
|
|
34
|
+
const step = useSessionStorage("step", 0)
|
|
35
35
|
step.set(5)
|
|
36
36
|
step.remove()
|
|
37
37
|
expect(step()).toBe(0)
|
|
38
|
-
expect(sessionStorage.getItem(
|
|
38
|
+
expect(sessionStorage.getItem("step")).toBeNull()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it(
|
|
42
|
-
const a = useSessionStorage(
|
|
43
|
-
const b = useSessionStorage(
|
|
41
|
+
it("returns same signal instance for same key", () => {
|
|
42
|
+
const a = useSessionStorage("step", 0)
|
|
43
|
+
const b = useSessionStorage("step", 0)
|
|
44
44
|
expect(a).toBe(b)
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
it(
|
|
48
|
-
const form = useSessionStorage(
|
|
49
|
-
form.set({ name:
|
|
50
|
-
expect(form()).toEqual({ name:
|
|
47
|
+
it("works with objects", () => {
|
|
48
|
+
const form = useSessionStorage("form-draft", { name: "", email: "" })
|
|
49
|
+
form.set({ name: "Alice", email: "alice@example.com" })
|
|
50
|
+
expect(form()).toEqual({ name: "Alice", email: "alice@example.com" })
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
it(
|
|
54
|
-
sessionStorage.setItem(
|
|
55
|
-
const value = useSessionStorage(
|
|
56
|
-
expect(value()).toBe(
|
|
53
|
+
it("handles corrupt storage values gracefully", () => {
|
|
54
|
+
sessionStorage.setItem("broken", "{{invalid")
|
|
55
|
+
const value = useSessionStorage("broken", "default")
|
|
56
|
+
expect(value()).toBe("default")
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
it(
|
|
60
|
-
const { useStorage } = await import(
|
|
61
|
-
const local = useStorage(
|
|
62
|
-
const session = useSessionStorage(
|
|
59
|
+
it("does not share signals with localStorage", async () => {
|
|
60
|
+
const { useStorage } = await import("../local")
|
|
61
|
+
const local = useStorage("key", "local-default")
|
|
62
|
+
const session = useSessionStorage("key", "session-default")
|
|
63
63
|
expect(local).not.toBe(session)
|
|
64
|
-
expect(local()).toBe(
|
|
65
|
-
expect(session()).toBe(
|
|
64
|
+
expect(local()).toBe("local-default")
|
|
65
|
+
expect(session()).toBe("session-default")
|
|
66
66
|
})
|
|
67
67
|
})
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Signal } from
|
|
1
|
+
import type { Signal } from "@pyreon/reactivity"
|
|
2
2
|
|
|
3
3
|
// ─── Storage Signal ──────────────────────────────────────────────────────────
|
|
4
4
|
|
|
@@ -42,7 +42,7 @@ export interface CookieOptions<T> extends StorageOptions<T> {
|
|
|
42
42
|
/** HTTPS only — default: false */
|
|
43
43
|
secure?: boolean
|
|
44
44
|
/** SameSite policy — default: 'lax' */
|
|
45
|
-
sameSite?:
|
|
45
|
+
sameSite?: "strict" | "lax" | "none"
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// ─── IndexedDB Options ───────────────────────────────────────────────────────
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StorageOptions } from
|
|
1
|
+
import type { StorageOptions } from "./types"
|
|
2
2
|
|
|
3
3
|
// ─── SSR Detection ───────────────────────────────────────────────────────────
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import type { StorageOptions } from './types'
|
|
|
6
6
|
* Check if we're running in a browser environment.
|
|
7
7
|
*/
|
|
8
8
|
export function isBrowser(): boolean {
|
|
9
|
-
return typeof window !==
|
|
9
|
+
return typeof window !== "undefined" && typeof document !== "undefined"
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// ─── Serialization ───────────────────────────────────────────────────────────
|
|
@@ -14,10 +14,7 @@ export function isBrowser(): boolean {
|
|
|
14
14
|
/**
|
|
15
15
|
* Serialize a value to a string for storage.
|
|
16
16
|
*/
|
|
17
|
-
export function serialize<T>(
|
|
18
|
-
value: T,
|
|
19
|
-
serializer?: StorageOptions<T>['serializer'],
|
|
20
|
-
): string {
|
|
17
|
+
export function serialize<T>(value: T, serializer?: StorageOptions<T>["serializer"]): string {
|
|
21
18
|
if (serializer) return serializer(value)
|
|
22
19
|
return JSON.stringify(value)
|
|
23
20
|
}
|
|
@@ -29,8 +26,8 @@ export function serialize<T>(
|
|
|
29
26
|
export function deserialize<T>(
|
|
30
27
|
raw: string,
|
|
31
28
|
defaultValue: T,
|
|
32
|
-
deserializer?: StorageOptions<T>[
|
|
33
|
-
onError?: StorageOptions<T>[
|
|
29
|
+
deserializer?: StorageOptions<T>["deserializer"],
|
|
30
|
+
onError?: StorageOptions<T>["onError"],
|
|
34
31
|
): T {
|
|
35
32
|
try {
|
|
36
33
|
if (deserializer) return deserializer(raw)
|
|
@@ -50,14 +47,13 @@ export function deserialize<T>(
|
|
|
50
47
|
* Safely get a Web Storage instance (localStorage or sessionStorage).
|
|
51
48
|
* Returns null if not available (SSR, security restrictions, etc.).
|
|
52
49
|
*/
|
|
53
|
-
export function getWebStorage(type:
|
|
50
|
+
export function getWebStorage(type: "local" | "session"): Storage | null {
|
|
54
51
|
if (!isBrowser()) return null
|
|
55
52
|
try {
|
|
56
|
-
const storage =
|
|
57
|
-
type === 'local' ? window.localStorage : window.sessionStorage
|
|
53
|
+
const storage = type === "local" ? window.localStorage : window.sessionStorage
|
|
58
54
|
// Test that it actually works (can throw in private browsing)
|
|
59
|
-
const testKey =
|
|
60
|
-
storage.setItem(testKey,
|
|
55
|
+
const testKey = "__pyreon_storage_test__"
|
|
56
|
+
storage.setItem(testKey, "1")
|
|
61
57
|
storage.removeItem(testKey)
|
|
62
58
|
return storage
|
|
63
59
|
} catch {
|