@pyreon/storage 0.10.0 → 0.11.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/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/custom.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from
|
|
2
|
-
import { _resetRegistry, createStorage, useMemoryStorage } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest"
|
|
2
|
+
import { _resetRegistry, createStorage, useMemoryStorage } from "../index"
|
|
3
3
|
|
|
4
|
-
describe(
|
|
4
|
+
describe("createStorage", () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
_resetRegistry()
|
|
7
7
|
})
|
|
@@ -10,7 +10,7 @@ describe('createStorage', () => {
|
|
|
10
10
|
_resetRegistry()
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
it(
|
|
13
|
+
it("creates a working storage hook from a custom backend", () => {
|
|
14
14
|
const store = new Map<string, string>()
|
|
15
15
|
const useCustom = createStorage({
|
|
16
16
|
get: (k) => store.get(k) ?? null,
|
|
@@ -18,17 +18,17 @@ describe('createStorage', () => {
|
|
|
18
18
|
remove: (k) => store.delete(k),
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
const sig = useCustom(
|
|
22
|
-
expect(sig()).toBe(
|
|
21
|
+
const sig = useCustom("key", "default")
|
|
22
|
+
expect(sig()).toBe("default")
|
|
23
23
|
|
|
24
|
-
sig.set(
|
|
25
|
-
expect(sig()).toBe(
|
|
26
|
-
expect(store.get(
|
|
24
|
+
sig.set("updated")
|
|
25
|
+
expect(sig()).toBe("updated")
|
|
26
|
+
expect(store.get("key")).toBe(JSON.stringify("updated"))
|
|
27
27
|
})
|
|
28
28
|
|
|
29
|
-
it(
|
|
29
|
+
it("reads existing values from the backend", () => {
|
|
30
30
|
const store = new Map<string, string>()
|
|
31
|
-
store.set(
|
|
31
|
+
store.set("key", JSON.stringify("existing"))
|
|
32
32
|
|
|
33
33
|
const useCustom = createStorage({
|
|
34
34
|
get: (k) => store.get(k) ?? null,
|
|
@@ -36,11 +36,11 @@ describe('createStorage', () => {
|
|
|
36
36
|
remove: (k) => store.delete(k),
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
const sig = useCustom(
|
|
40
|
-
expect(sig()).toBe(
|
|
39
|
+
const sig = useCustom("key", "default")
|
|
40
|
+
expect(sig()).toBe("existing")
|
|
41
41
|
})
|
|
42
42
|
|
|
43
|
-
it(
|
|
43
|
+
it(".remove() clears from backend and resets signal", () => {
|
|
44
44
|
const store = new Map<string, string>()
|
|
45
45
|
const useCustom = createStorage({
|
|
46
46
|
get: (k) => store.get(k) ?? null,
|
|
@@ -48,15 +48,15 @@ describe('createStorage', () => {
|
|
|
48
48
|
remove: (k) => store.delete(k),
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
const sig = useCustom(
|
|
52
|
-
sig.set(
|
|
51
|
+
const sig = useCustom("key", "default")
|
|
52
|
+
sig.set("updated")
|
|
53
53
|
sig.remove()
|
|
54
54
|
|
|
55
|
-
expect(sig()).toBe(
|
|
56
|
-
expect(store.has(
|
|
55
|
+
expect(sig()).toBe("default")
|
|
56
|
+
expect(store.has("key")).toBe(false)
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
it(
|
|
59
|
+
it("deduplicates signals for same backend + key", () => {
|
|
60
60
|
const store = new Map<string, string>()
|
|
61
61
|
const useCustom = createStorage(
|
|
62
62
|
{
|
|
@@ -64,18 +64,18 @@ describe('createStorage', () => {
|
|
|
64
64
|
set: (k, v) => store.set(k, v),
|
|
65
65
|
remove: (k) => store.delete(k),
|
|
66
66
|
},
|
|
67
|
-
|
|
67
|
+
"test-backend",
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
const a = useCustom(
|
|
71
|
-
const b = useCustom(
|
|
70
|
+
const a = useCustom("key", "default")
|
|
71
|
+
const b = useCustom("key", "default")
|
|
72
72
|
expect(a).toBe(b)
|
|
73
73
|
})
|
|
74
74
|
|
|
75
|
-
it(
|
|
75
|
+
it("handles backend read errors gracefully", () => {
|
|
76
76
|
const useCustom = createStorage({
|
|
77
77
|
get: () => {
|
|
78
|
-
throw new Error(
|
|
78
|
+
throw new Error("read failed")
|
|
79
79
|
},
|
|
80
80
|
set: () => {
|
|
81
81
|
// intentional no-op for error test
|
|
@@ -85,28 +85,28 @@ describe('createStorage', () => {
|
|
|
85
85
|
},
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
-
const sig = useCustom(
|
|
89
|
-
expect(sig()).toBe(
|
|
88
|
+
const sig = useCustom("key", "fallback")
|
|
89
|
+
expect(sig()).toBe("fallback")
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
-
it(
|
|
92
|
+
it("handles backend write errors gracefully", () => {
|
|
93
93
|
const useCustom = createStorage({
|
|
94
94
|
get: () => null,
|
|
95
95
|
set: () => {
|
|
96
|
-
throw new Error(
|
|
96
|
+
throw new Error("write failed")
|
|
97
97
|
},
|
|
98
98
|
remove: () => {
|
|
99
99
|
// intentional no-op for error test
|
|
100
100
|
},
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
-
const sig = useCustom(
|
|
103
|
+
const sig = useCustom("key", "default")
|
|
104
104
|
// Should not throw — signal still updates
|
|
105
|
-
sig.set(
|
|
106
|
-
expect(sig()).toBe(
|
|
105
|
+
sig.set("new-value")
|
|
106
|
+
expect(sig()).toBe("new-value")
|
|
107
107
|
})
|
|
108
108
|
|
|
109
|
-
it(
|
|
109
|
+
it(".subscribe() delegates to underlying signal", () => {
|
|
110
110
|
const store = new Map<string, string>()
|
|
111
111
|
const useCustom = createStorage({
|
|
112
112
|
get: (k) => store.get(k) ?? null,
|
|
@@ -114,17 +114,17 @@ describe('createStorage', () => {
|
|
|
114
114
|
remove: (k) => store.delete(k),
|
|
115
115
|
})
|
|
116
116
|
|
|
117
|
-
const sig = useCustom(
|
|
117
|
+
const sig = useCustom("sub-key", "a")
|
|
118
118
|
let called = false
|
|
119
119
|
const unsub = sig.subscribe(() => {
|
|
120
120
|
called = true
|
|
121
121
|
})
|
|
122
|
-
sig.set(
|
|
122
|
+
sig.set("b")
|
|
123
123
|
expect(called).toBe(true)
|
|
124
124
|
unsub()
|
|
125
125
|
})
|
|
126
126
|
|
|
127
|
-
it(
|
|
127
|
+
it(".direct() delegates to underlying signal", () => {
|
|
128
128
|
const store = new Map<string, string>()
|
|
129
129
|
const useCustom = createStorage({
|
|
130
130
|
get: (k) => store.get(k) ?? null,
|
|
@@ -132,17 +132,17 @@ describe('createStorage', () => {
|
|
|
132
132
|
remove: (k) => store.delete(k),
|
|
133
133
|
})
|
|
134
134
|
|
|
135
|
-
const sig = useCustom(
|
|
135
|
+
const sig = useCustom("dir-key", "a")
|
|
136
136
|
let called = false
|
|
137
137
|
const unsub = sig.direct(() => {
|
|
138
138
|
called = true
|
|
139
139
|
})
|
|
140
|
-
sig.set(
|
|
140
|
+
sig.set("b")
|
|
141
141
|
expect(called).toBe(true)
|
|
142
142
|
unsub()
|
|
143
143
|
})
|
|
144
144
|
|
|
145
|
-
it(
|
|
145
|
+
it(".debug() and .label work", () => {
|
|
146
146
|
const store = new Map<string, string>()
|
|
147
147
|
const useCustom = createStorage({
|
|
148
148
|
get: (k) => store.get(k) ?? null,
|
|
@@ -150,13 +150,13 @@ describe('createStorage', () => {
|
|
|
150
150
|
remove: (k) => store.delete(k),
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
-
const sig = useCustom(
|
|
154
|
-
sig.label =
|
|
155
|
-
expect(sig.label).toBe(
|
|
156
|
-
expect(sig.debug().value).toBe(
|
|
153
|
+
const sig = useCustom("debug-key", "test")
|
|
154
|
+
sig.label = "my-signal"
|
|
155
|
+
expect(sig.label).toBe("my-signal")
|
|
156
|
+
expect(sig.debug().value).toBe("test")
|
|
157
157
|
})
|
|
158
158
|
|
|
159
|
-
it(
|
|
159
|
+
it("supports custom serializer/deserializer", () => {
|
|
160
160
|
const store = new Map<string, string>()
|
|
161
161
|
const useCustom = createStorage({
|
|
162
162
|
get: (k) => store.get(k) ?? null,
|
|
@@ -164,18 +164,18 @@ describe('createStorage', () => {
|
|
|
164
164
|
remove: (k) => store.delete(k),
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
-
const date = useCustom(
|
|
167
|
+
const date = useCustom("date", new Date("2025-01-01"), {
|
|
168
168
|
serializer: (d) => d.toISOString(),
|
|
169
169
|
deserializer: (s) => new Date(s),
|
|
170
170
|
})
|
|
171
171
|
|
|
172
|
-
const newDate = new Date(
|
|
172
|
+
const newDate = new Date("2025-06-15")
|
|
173
173
|
date.set(newDate)
|
|
174
|
-
expect(date().toISOString()).toBe(
|
|
174
|
+
expect(date().toISOString()).toBe("2025-06-15T00:00:00.000Z")
|
|
175
175
|
})
|
|
176
176
|
})
|
|
177
177
|
|
|
178
|
-
describe(
|
|
178
|
+
describe("useMemoryStorage", () => {
|
|
179
179
|
beforeEach(() => {
|
|
180
180
|
_resetRegistry()
|
|
181
181
|
})
|
|
@@ -184,24 +184,24 @@ describe('useMemoryStorage', () => {
|
|
|
184
184
|
_resetRegistry()
|
|
185
185
|
})
|
|
186
186
|
|
|
187
|
-
it(
|
|
188
|
-
const sig = useMemoryStorage(
|
|
189
|
-
expect(sig()).toBe(
|
|
187
|
+
it("works as an in-memory storage", () => {
|
|
188
|
+
const sig = useMemoryStorage("key", "default")
|
|
189
|
+
expect(sig()).toBe("default")
|
|
190
190
|
|
|
191
|
-
sig.set(
|
|
192
|
-
expect(sig()).toBe(
|
|
191
|
+
sig.set("updated")
|
|
192
|
+
expect(sig()).toBe("updated")
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
-
it(
|
|
196
|
-
const a = useMemoryStorage(
|
|
197
|
-
const b = useMemoryStorage(
|
|
195
|
+
it("deduplicates signals", () => {
|
|
196
|
+
const a = useMemoryStorage("key", "default")
|
|
197
|
+
const b = useMemoryStorage("key", "default")
|
|
198
198
|
expect(a).toBe(b)
|
|
199
199
|
})
|
|
200
200
|
|
|
201
|
-
it(
|
|
202
|
-
const sig = useMemoryStorage(
|
|
203
|
-
sig.set(
|
|
201
|
+
it(".remove() resets to default", () => {
|
|
202
|
+
const sig = useMemoryStorage("temp", "initial")
|
|
203
|
+
sig.set("changed")
|
|
204
204
|
sig.remove()
|
|
205
|
-
expect(sig()).toBe(
|
|
205
|
+
expect(sig()).toBe("initial")
|
|
206
206
|
})
|
|
207
207
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from
|
|
2
|
-
import { _resetDBCache, _resetRegistry, useIndexedDB } from
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest"
|
|
2
|
+
import { _resetDBCache, _resetRegistry, useIndexedDB } from "../index"
|
|
3
3
|
|
|
4
|
-
describe(
|
|
4
|
+
describe("useIndexedDB", () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
6
|
_resetRegistry()
|
|
7
7
|
_resetDBCache()
|
|
@@ -12,63 +12,63 @@ describe('useIndexedDB', () => {
|
|
|
12
12
|
_resetDBCache()
|
|
13
13
|
})
|
|
14
14
|
|
|
15
|
-
it(
|
|
16
|
-
const draft = useIndexedDB(
|
|
15
|
+
it("returns default value initially", () => {
|
|
16
|
+
const draft = useIndexedDB("draft", { title: "", body: "" })
|
|
17
17
|
// Initially returns default (IDB load is async)
|
|
18
|
-
expect(draft()).toEqual({ title:
|
|
18
|
+
expect(draft()).toEqual({ title: "", body: "" })
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
it(
|
|
22
|
-
const draft = useIndexedDB(
|
|
23
|
-
draft.set({ title:
|
|
24
|
-
expect(draft()).toEqual({ title:
|
|
21
|
+
it(".set() updates signal immediately", () => {
|
|
22
|
+
const draft = useIndexedDB("draft", { title: "", body: "" })
|
|
23
|
+
draft.set({ title: "Hello", body: "World" })
|
|
24
|
+
expect(draft()).toEqual({ title: "Hello", body: "World" })
|
|
25
25
|
})
|
|
26
26
|
|
|
27
|
-
it(
|
|
28
|
-
const count = useIndexedDB(
|
|
27
|
+
it(".update() updates signal", () => {
|
|
28
|
+
const count = useIndexedDB("count", 0)
|
|
29
29
|
count.update((n) => n + 1)
|
|
30
30
|
expect(count()).toBe(1)
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
const draft = useIndexedDB(
|
|
35
|
-
expect(draft.peek()).toBe(
|
|
33
|
+
it(".peek() reads without subscribing", () => {
|
|
34
|
+
const draft = useIndexedDB("draft", "default")
|
|
35
|
+
expect(draft.peek()).toBe("default")
|
|
36
36
|
})
|
|
37
37
|
|
|
38
|
-
it(
|
|
39
|
-
const draft = useIndexedDB(
|
|
40
|
-
draft.set(
|
|
38
|
+
it(".remove() resets to default", () => {
|
|
39
|
+
const draft = useIndexedDB("draft", "default")
|
|
40
|
+
draft.set("modified")
|
|
41
41
|
draft.remove()
|
|
42
|
-
expect(draft()).toBe(
|
|
42
|
+
expect(draft()).toBe("default")
|
|
43
43
|
})
|
|
44
44
|
|
|
45
|
-
it(
|
|
46
|
-
const a = useIndexedDB(
|
|
47
|
-
const b = useIndexedDB(
|
|
45
|
+
it("returns same signal for same key", () => {
|
|
46
|
+
const a = useIndexedDB("key", "value")
|
|
47
|
+
const b = useIndexedDB("key", "value")
|
|
48
48
|
expect(a).toBe(b)
|
|
49
49
|
})
|
|
50
50
|
|
|
51
|
-
it(
|
|
52
|
-
const a = useIndexedDB(
|
|
53
|
-
const b = useIndexedDB(
|
|
51
|
+
it("returns different signals for different keys", () => {
|
|
52
|
+
const a = useIndexedDB("key1", "a")
|
|
53
|
+
const b = useIndexedDB("key2", "b")
|
|
54
54
|
expect(a).not.toBe(b)
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
-
it(
|
|
58
|
-
const draft = useIndexedDB(
|
|
59
|
-
expect(draft.debug().value).toBe(
|
|
57
|
+
it(".debug() returns debug info", () => {
|
|
58
|
+
const draft = useIndexedDB("draft", "test")
|
|
59
|
+
expect(draft.debug().value).toBe("test")
|
|
60
60
|
})
|
|
61
61
|
|
|
62
|
-
it(
|
|
63
|
-
const draft = useIndexedDB(
|
|
64
|
-
draft.label =
|
|
65
|
-
expect(draft.label).toBe(
|
|
62
|
+
it(".label can be set", () => {
|
|
63
|
+
const draft = useIndexedDB("draft", "")
|
|
64
|
+
draft.label = "draft-signal"
|
|
65
|
+
expect(draft.label).toBe("draft-signal")
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
it(
|
|
69
|
-
const draft = useIndexedDB(
|
|
70
|
-
draft.set(
|
|
68
|
+
it("set updates signal synchronously even though IDB write is async", () => {
|
|
69
|
+
const draft = useIndexedDB("sync-test", "default", { debounceMs: 10 })
|
|
70
|
+
draft.set("immediate")
|
|
71
71
|
// Signal updates immediately — no need to wait for IDB
|
|
72
|
-
expect(draft()).toBe(
|
|
72
|
+
expect(draft()).toBe("immediate")
|
|
73
73
|
})
|
|
74
74
|
})
|