@choksheak/react-utils 0.3.6 → 0.3.8
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/NoSSR.cjs +37 -0
- package/NoSSR.d.mts +24 -0
- package/NoSSR.d.ts +20 -0
- package/NoSSR.min.cjs +2 -0
- package/NoSSR.min.cjs.map +1 -0
- package/NoSSR.min.mjs +2 -0
- package/NoSSR.min.mjs.map +1 -0
- package/NoSSR.mjs +12 -0
- package/README.md +10 -1
- package/fetcher.cjs +3 -3
- package/fetcher.d.mts +5 -4
- package/fetcher.d.ts +4 -3
- package/fetcher.min.cjs +1 -1
- package/fetcher.min.cjs.map +1 -1
- package/fetcher.min.mjs +1 -1
- package/fetcher.min.mjs.map +1 -1
- package/fetcher.mjs +2 -2
- package/package.json +21 -3
- package/sharedQuery.cjs +486 -486
- package/sharedQuery.d.mts +64 -61
- package/sharedQuery.d.ts +62 -59
- package/sharedQuery.min.cjs +1 -1
- package/sharedQuery.min.cjs.map +1 -1
- package/sharedQuery.min.mjs +1 -1
- package/sharedQuery.min.mjs.map +1 -1
- package/sharedQuery.mjs +487 -486
- package/sharedState.cjs +201 -199
- package/sharedState.d.mts +48 -38
- package/sharedState.d.ts +45 -36
- package/sharedState.min.cjs +1 -1
- package/sharedState.min.cjs.map +1 -1
- package/sharedState.min.mjs +1 -1
- package/sharedState.min.mjs.map +1 -1
- package/sharedState.mjs +202 -199
- package/storage.cjs +79 -65
- package/storage.d.mts +39 -2
- package/storage.d.ts +37 -1
- package/storage.min.cjs +1 -1
- package/storage.min.cjs.map +1 -1
- package/storage.min.mjs +1 -1
- package/storage.min.mjs.map +1 -1
- package/storage.mjs +77 -65
- package/stringify.d.mts +9 -0
- package/stringify.d.ts +9 -0
- package/stringify.min.cjs.map +1 -1
- package/stringify.min.mjs.map +1 -1
- package/useDeepMemo.cjs +1 -1
- package/useDeepMemo.d.mts +11 -0
- package/useDeepMemo.d.ts +11 -0
- package/useDeepMemo.min.cjs +1 -1
- package/useDeepMemo.min.cjs.map +1 -1
- package/useDeepMemo.min.mjs +1 -1
- package/useDeepMemo.min.mjs.map +1 -1
- package/useDeepMemo.mjs +1 -1
package/sharedState.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { useSyncExternalStore } from "react";
|
|
|
4
4
|
|
|
5
5
|
// src/storage.ts
|
|
6
6
|
import { kvStore } from "@choksheak/ts-utils/kvStore";
|
|
7
|
-
import {
|
|
7
|
+
import { createLocalStore } from "@choksheak/ts-utils/localStore";
|
|
8
8
|
var IS_NOT_BROWSER = typeof window === "undefined";
|
|
9
9
|
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
10
10
|
const { store } = options ?? {};
|
|
@@ -13,70 +13,12 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
13
13
|
}
|
|
14
14
|
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
15
15
|
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
16
|
-
const
|
|
16
|
+
const config = { ...store, expiryMs };
|
|
17
17
|
switch (store.persistTo) {
|
|
18
|
-
case "localStorage":
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
set: (key, value) => {
|
|
24
|
-
if (IS_NOT_BROWSER) return;
|
|
25
|
-
adapter.set(key, value);
|
|
26
|
-
},
|
|
27
|
-
get: (key) => {
|
|
28
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
29
|
-
const value = adapter.get(key);
|
|
30
|
-
if (isValid && !isValid(value)) {
|
|
31
|
-
console.warn(
|
|
32
|
-
`localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
33
|
-
);
|
|
34
|
-
adapter.delete(key);
|
|
35
|
-
return void 0;
|
|
36
|
-
}
|
|
37
|
-
return value;
|
|
38
|
-
},
|
|
39
|
-
delete: (key) => {
|
|
40
|
-
if (IS_NOT_BROWSER) return;
|
|
41
|
-
adapter.delete(key);
|
|
42
|
-
},
|
|
43
|
-
clear: () => {
|
|
44
|
-
if (IS_NOT_BROWSER) return;
|
|
45
|
-
adapter.clear();
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
case "indexedDb": {
|
|
50
|
-
const keyPrefix = store.key + ":";
|
|
51
|
-
const isValid2 = store.isValid;
|
|
52
|
-
return {
|
|
53
|
-
set: async (key, value) => {
|
|
54
|
-
if (IS_NOT_BROWSER) return;
|
|
55
|
-
await kvStore.set(keyPrefix + key, value, expiryMs);
|
|
56
|
-
},
|
|
57
|
-
get: async (key) => {
|
|
58
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
59
|
-
const fullKey = keyPrefix + key;
|
|
60
|
-
const value = await kvStore.get(fullKey);
|
|
61
|
-
if (isValid2 && !isValid2(value)) {
|
|
62
|
-
console.warn(
|
|
63
|
-
`indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
64
|
-
);
|
|
65
|
-
await kvStore.delete(fullKey);
|
|
66
|
-
return void 0;
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
},
|
|
70
|
-
delete: async (key) => {
|
|
71
|
-
if (IS_NOT_BROWSER) return;
|
|
72
|
-
await kvStore.delete(keyPrefix + key);
|
|
73
|
-
},
|
|
74
|
-
clear: async () => {
|
|
75
|
-
if (IS_NOT_BROWSER) return;
|
|
76
|
-
await kvStore.clear();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}
|
|
18
|
+
case "localStorage":
|
|
19
|
+
return localStoreAdapter(config);
|
|
20
|
+
case "indexedDb":
|
|
21
|
+
return kvStoreAdapter(config);
|
|
80
22
|
default:
|
|
81
23
|
store.persistTo;
|
|
82
24
|
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
@@ -84,6 +26,74 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
84
26
|
}
|
|
85
27
|
return store;
|
|
86
28
|
}
|
|
29
|
+
function localStoreAdapter({
|
|
30
|
+
key,
|
|
31
|
+
expiryMs,
|
|
32
|
+
isValid
|
|
33
|
+
}) {
|
|
34
|
+
const adapter = createLocalStore(key, { defaultExpiryMs: expiryMs });
|
|
35
|
+
return {
|
|
36
|
+
set: (key2, value) => {
|
|
37
|
+
if (IS_NOT_BROWSER) return;
|
|
38
|
+
adapter.set(key2, value);
|
|
39
|
+
},
|
|
40
|
+
get: (key2) => {
|
|
41
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
42
|
+
const value = adapter.get(key2);
|
|
43
|
+
if (isValid && !isValid(value)) {
|
|
44
|
+
console.warn(
|
|
45
|
+
`localStorage adapter: Auto-discard invalid value for ${key2}: ${JSON.stringify(value)}`
|
|
46
|
+
);
|
|
47
|
+
adapter.delete(key2);
|
|
48
|
+
return void 0;
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
},
|
|
52
|
+
delete: (key2) => {
|
|
53
|
+
if (IS_NOT_BROWSER) return;
|
|
54
|
+
adapter.delete(key2);
|
|
55
|
+
},
|
|
56
|
+
clear: () => {
|
|
57
|
+
if (IS_NOT_BROWSER) return;
|
|
58
|
+
adapter.clear();
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function kvStoreAdapter({
|
|
63
|
+
key,
|
|
64
|
+
expiryMs,
|
|
65
|
+
isValid
|
|
66
|
+
}) {
|
|
67
|
+
const keyPrefix = key + ":";
|
|
68
|
+
return {
|
|
69
|
+
set: async (key2, value) => {
|
|
70
|
+
if (IS_NOT_BROWSER) return;
|
|
71
|
+
await kvStore.set(keyPrefix + key2, value, expiryMs);
|
|
72
|
+
},
|
|
73
|
+
get: async (key2) => {
|
|
74
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
75
|
+
const fullKey = keyPrefix + key2;
|
|
76
|
+
const value = await kvStore.get(fullKey);
|
|
77
|
+
if (value !== void 0 && isValid && !isValid(value)) {
|
|
78
|
+
console.warn(
|
|
79
|
+
`indexedDb adapter: Auto-discard invalid value for ${key2}:`,
|
|
80
|
+
value
|
|
81
|
+
);
|
|
82
|
+
await kvStore.delete(fullKey);
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
},
|
|
87
|
+
delete: async (key2) => {
|
|
88
|
+
if (IS_NOT_BROWSER) return;
|
|
89
|
+
await kvStore.delete(keyPrefix + key2);
|
|
90
|
+
},
|
|
91
|
+
clear: async () => {
|
|
92
|
+
if (IS_NOT_BROWSER) return;
|
|
93
|
+
await kvStore.clear();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
87
97
|
|
|
88
98
|
// src/sharedState.ts
|
|
89
99
|
var SharedStateConfig = {
|
|
@@ -101,41 +111,11 @@ var SharedStateConfig = {
|
|
|
101
111
|
function configureSharedState(config) {
|
|
102
112
|
Object.assign(SharedStateConfig, config);
|
|
103
113
|
}
|
|
104
|
-
|
|
105
|
-
dataByKey = /* @__PURE__ */ new Map();
|
|
106
|
-
subscribersByKey = /* @__PURE__ */ new Map();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
get(key) {
|
|
110
|
-
return this.dataByKey.get(key);
|
|
111
|
-
}
|
|
112
|
-
set(key, value) {
|
|
113
|
-
const prev = this.dataByKey.get(key);
|
|
114
|
-
if (prev === value) return;
|
|
115
|
-
this.dataByKey.set(key, value);
|
|
116
|
-
this.notify(key, value, prev);
|
|
117
|
-
}
|
|
118
|
-
delete(key) {
|
|
119
|
-
this.dataByKey.delete(key);
|
|
120
|
-
}
|
|
121
|
-
setNoNotify(key, value) {
|
|
122
|
-
this.dataByKey.set(key, value);
|
|
123
|
-
}
|
|
124
|
-
// Subscribe to changes of a specific key
|
|
125
|
-
subscribe(key, subscriber) {
|
|
126
|
-
let subscribers = this.subscribersByKey.get(key);
|
|
127
|
-
if (!subscribers) {
|
|
128
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
129
|
-
this.subscribersByKey.set(key, subscribers);
|
|
130
|
-
}
|
|
131
|
-
subscribers.add(subscriber);
|
|
132
|
-
return () => {
|
|
133
|
-
subscribers.delete(subscriber);
|
|
134
|
-
if (subscribers.size === 0) this.subscribersByKey.delete(key);
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
notify(key, next, prev) {
|
|
138
|
-
const subscribers = this.subscribersByKey.get(key);
|
|
114
|
+
function createPubSubStore() {
|
|
115
|
+
const dataByKey = /* @__PURE__ */ new Map();
|
|
116
|
+
const subscribersByKey = /* @__PURE__ */ new Map();
|
|
117
|
+
function notify(key, next, prev) {
|
|
118
|
+
const subscribers = subscribersByKey.get(key);
|
|
139
119
|
if (!subscribers) return;
|
|
140
120
|
const copy = Array.from(subscribers.values());
|
|
141
121
|
for (const subscriber of copy) {
|
|
@@ -146,125 +126,148 @@ var PubSubStore = class {
|
|
|
146
126
|
}
|
|
147
127
|
}
|
|
148
128
|
}
|
|
149
|
-
|
|
150
|
-
|
|
129
|
+
return {
|
|
130
|
+
// This could be undefined if T includes undefined, else it should never
|
|
131
|
+
// return undefined because the user has to always specify the default value.
|
|
132
|
+
get(key) {
|
|
133
|
+
return dataByKey.get(key);
|
|
134
|
+
},
|
|
135
|
+
set(key, value) {
|
|
136
|
+
const prev = dataByKey.get(key);
|
|
137
|
+
if (prev === value) return;
|
|
138
|
+
dataByKey.set(key, value);
|
|
139
|
+
notify(key, value, prev);
|
|
140
|
+
},
|
|
141
|
+
delete(key) {
|
|
142
|
+
dataByKey.delete(key);
|
|
143
|
+
},
|
|
144
|
+
setNoNotify(key, value) {
|
|
145
|
+
dataByKey.set(key, value);
|
|
146
|
+
},
|
|
147
|
+
// Subscribe to changes of a specific key
|
|
148
|
+
subscribe(key, subscriber) {
|
|
149
|
+
let subscribers = subscribersByKey.get(key);
|
|
150
|
+
if (!subscribers) {
|
|
151
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
152
|
+
subscribersByKey.set(key, subscribers);
|
|
153
|
+
}
|
|
154
|
+
subscribers.add(subscriber);
|
|
155
|
+
return () => {
|
|
156
|
+
subscribers.delete(subscriber);
|
|
157
|
+
if (subscribers.size === 0) subscribersByKey.delete(key);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
var pubSubStore = createPubSubStore();
|
|
151
163
|
var STORAGE_KEY = "state";
|
|
152
164
|
var stateKey = 0;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
165
|
+
function sharedState(defaultValue, options) {
|
|
166
|
+
let readyResolve = () => {
|
|
167
|
+
};
|
|
168
|
+
const readyPromise = new Promise((resolve) => {
|
|
169
|
+
readyResolve = resolve;
|
|
170
|
+
});
|
|
171
|
+
const resolveReadyPromise = readyResolve;
|
|
172
|
+
const pubSubKey = String(stateKey++);
|
|
173
|
+
const storageAdapter = getStorageAdapter(
|
|
174
|
+
options,
|
|
175
|
+
SharedStateConfig.storeExpiryMs
|
|
176
|
+
);
|
|
177
|
+
pubSubStore.setNoNotify(pubSubKey, defaultValue);
|
|
178
|
+
const lazyLoad = options?.lazyLoad;
|
|
179
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
180
|
+
const obj = {
|
|
181
|
+
/** Allow users to await for the completion of data init. */
|
|
182
|
+
readyPromise,
|
|
183
|
+
lazy,
|
|
184
|
+
initStarted: false,
|
|
185
|
+
initDone: false,
|
|
186
|
+
/** `subscribe` function required by useSyncExternalStore. */
|
|
187
|
+
subscribe(subscriber) {
|
|
188
|
+
return pubSubStore.subscribe(pubSubKey, () => subscriber());
|
|
189
|
+
},
|
|
190
|
+
/** Returns true if there is a backing store. */
|
|
191
|
+
hasStore() {
|
|
192
|
+
return Boolean(storageAdapter);
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* Set a value directly into the store. Does nothing if there is no
|
|
196
|
+
* backing store.
|
|
197
|
+
*
|
|
198
|
+
* Warning: If you want to set the value properly, you should use
|
|
199
|
+
* `setValue()` instead which also updates the in-memory copy.
|
|
200
|
+
*/
|
|
201
|
+
async setInStore(value) {
|
|
202
|
+
return await storageAdapter?.set(STORAGE_KEY, value);
|
|
203
|
+
},
|
|
204
|
+
/**
|
|
205
|
+
* Get a value directly from the store. Returns undefined if there is no
|
|
206
|
+
* backing store.
|
|
207
|
+
*/
|
|
208
|
+
async getFromStore() {
|
|
209
|
+
return await storageAdapter?.get(STORAGE_KEY);
|
|
210
|
+
},
|
|
211
|
+
/**
|
|
212
|
+
* Get the value of the shared state.
|
|
213
|
+
* `getSnapshot` function — must return same ref if value unchanged.
|
|
214
|
+
* Note that this would return the default value if data init from store is
|
|
215
|
+
* not completed yet.
|
|
216
|
+
*/
|
|
217
|
+
getSnapshot() {
|
|
218
|
+
return pubSubStore.get(pubSubKey);
|
|
219
|
+
},
|
|
220
|
+
/** Set the value of the shared state. */
|
|
221
|
+
setValue(next) {
|
|
222
|
+
if (typeof next === "function") {
|
|
223
|
+
const prev = pubSubStore.get(pubSubKey);
|
|
224
|
+
next = next(prev);
|
|
225
|
+
}
|
|
226
|
+
pubSubStore.set(pubSubKey, next);
|
|
227
|
+
void obj.setInStore(next);
|
|
228
|
+
},
|
|
229
|
+
/**
|
|
230
|
+
* Remove the value of the shared state. Actually this just sets the value
|
|
231
|
+
* back to the given default value.
|
|
232
|
+
*/
|
|
233
|
+
delete() {
|
|
234
|
+
pubSubStore.set(pubSubKey, defaultValue);
|
|
235
|
+
void storageAdapter?.set(STORAGE_KEY, defaultValue);
|
|
236
|
+
},
|
|
237
|
+
/** Initial default handling. */
|
|
238
|
+
async initDefaultValueOnce() {
|
|
239
|
+
if (obj.initDone || obj.initStarted) return;
|
|
240
|
+
obj.initStarted = true;
|
|
241
|
+
const stored = await obj.getFromStore();
|
|
242
|
+
const value = stored !== void 0 ? stored : defaultValue;
|
|
243
|
+
pubSubStore.set(pubSubKey, value);
|
|
244
|
+
obj.initDone = true;
|
|
245
|
+
resolveReadyPromise();
|
|
224
246
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Remove the value of the shared state. Actually this just sets the value
|
|
230
|
-
* back to the given default value.
|
|
231
|
-
*/
|
|
232
|
-
delete() {
|
|
233
|
-
pubSubStore.set(this.pubSubKey, this.defaultValue);
|
|
234
|
-
void this.storageAdapter?.set(STORAGE_KEY, this.defaultValue);
|
|
235
|
-
}
|
|
236
|
-
/** Initial default handling. */
|
|
237
|
-
async initDefaultValueOnce() {
|
|
238
|
-
if (this.initDone || this.initStarted) return;
|
|
239
|
-
this.initStarted = true;
|
|
240
|
-
const stored = await this.getFromStore();
|
|
241
|
-
const value = stored !== void 0 ? stored : this.defaultValue;
|
|
242
|
-
pubSubStore.set(this.pubSubKey, value);
|
|
243
|
-
this.initDone = true;
|
|
244
|
-
this.resolveReadyPromise();
|
|
247
|
+
};
|
|
248
|
+
if (!lazy) {
|
|
249
|
+
void obj.initDefaultValueOnce();
|
|
245
250
|
}
|
|
246
|
-
|
|
247
|
-
function sharedState(defaultValue, options) {
|
|
248
|
-
return new SharedState(defaultValue, options);
|
|
251
|
+
return obj;
|
|
249
252
|
}
|
|
250
253
|
function useSharedState(state) {
|
|
251
|
-
state.initDefaultValueOnce();
|
|
254
|
+
void state.initDefaultValueOnce();
|
|
252
255
|
const value = useSyncExternalStore(
|
|
253
256
|
(onStoreChange) => state.subscribe(onStoreChange),
|
|
254
257
|
() => state.getSnapshot(),
|
|
255
258
|
// getServerSnapshot is needed to prevent SSR dev errors in nextjs.
|
|
256
259
|
() => state.getSnapshot()
|
|
257
260
|
);
|
|
258
|
-
return [value, state.
|
|
261
|
+
return [value, state.setValue];
|
|
259
262
|
}
|
|
260
263
|
function useSharedStateValue(state) {
|
|
261
264
|
return useSharedState(state)[0];
|
|
262
265
|
}
|
|
263
266
|
function useSharedStateSetter(state) {
|
|
264
|
-
|
|
267
|
+
void state.initDefaultValueOnce();
|
|
268
|
+
return state.setValue;
|
|
265
269
|
}
|
|
266
270
|
export {
|
|
267
|
-
SharedState,
|
|
268
271
|
SharedStateConfig,
|
|
269
272
|
configureSharedState,
|
|
270
273
|
sharedState,
|
package/storage.cjs
CHANGED
|
@@ -20,7 +20,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/storage.ts
|
|
21
21
|
var storage_exports = {};
|
|
22
22
|
__export(storage_exports, {
|
|
23
|
-
getStorageAdapter: () => getStorageAdapter
|
|
23
|
+
getStorageAdapter: () => getStorageAdapter,
|
|
24
|
+
kvStoreAdapter: () => kvStoreAdapter,
|
|
25
|
+
localStoreAdapter: () => localStoreAdapter
|
|
24
26
|
});
|
|
25
27
|
module.exports = __toCommonJS(storage_exports);
|
|
26
28
|
var import_kvStore = require("@choksheak/ts-utils/kvStore");
|
|
@@ -33,70 +35,12 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
33
35
|
}
|
|
34
36
|
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
35
37
|
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
36
|
-
const
|
|
38
|
+
const config = { ...store, expiryMs };
|
|
37
39
|
switch (store.persistTo) {
|
|
38
|
-
case "localStorage":
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
set: (key, value) => {
|
|
44
|
-
if (IS_NOT_BROWSER) return;
|
|
45
|
-
adapter.set(key, value);
|
|
46
|
-
},
|
|
47
|
-
get: (key) => {
|
|
48
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
49
|
-
const value = adapter.get(key);
|
|
50
|
-
if (isValid && !isValid(value)) {
|
|
51
|
-
console.warn(
|
|
52
|
-
`localStorage adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
53
|
-
);
|
|
54
|
-
adapter.delete(key);
|
|
55
|
-
return void 0;
|
|
56
|
-
}
|
|
57
|
-
return value;
|
|
58
|
-
},
|
|
59
|
-
delete: (key) => {
|
|
60
|
-
if (IS_NOT_BROWSER) return;
|
|
61
|
-
adapter.delete(key);
|
|
62
|
-
},
|
|
63
|
-
clear: () => {
|
|
64
|
-
if (IS_NOT_BROWSER) return;
|
|
65
|
-
adapter.clear();
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
case "indexedDb": {
|
|
70
|
-
const keyPrefix = store.key + ":";
|
|
71
|
-
const isValid2 = store.isValid;
|
|
72
|
-
return {
|
|
73
|
-
set: async (key, value) => {
|
|
74
|
-
if (IS_NOT_BROWSER) return;
|
|
75
|
-
await import_kvStore.kvStore.set(keyPrefix + key, value, expiryMs);
|
|
76
|
-
},
|
|
77
|
-
get: async (key) => {
|
|
78
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
79
|
-
const fullKey = keyPrefix + key;
|
|
80
|
-
const value = await import_kvStore.kvStore.get(fullKey);
|
|
81
|
-
if (isValid2 && !isValid2(value)) {
|
|
82
|
-
console.warn(
|
|
83
|
-
`indexedDb adapter: Auto-discard invalid value for ${key}: ${JSON.stringify(value)}`
|
|
84
|
-
);
|
|
85
|
-
await import_kvStore.kvStore.delete(fullKey);
|
|
86
|
-
return void 0;
|
|
87
|
-
}
|
|
88
|
-
return value;
|
|
89
|
-
},
|
|
90
|
-
delete: async (key) => {
|
|
91
|
-
if (IS_NOT_BROWSER) return;
|
|
92
|
-
await import_kvStore.kvStore.delete(keyPrefix + key);
|
|
93
|
-
},
|
|
94
|
-
clear: async () => {
|
|
95
|
-
if (IS_NOT_BROWSER) return;
|
|
96
|
-
await import_kvStore.kvStore.clear();
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
}
|
|
40
|
+
case "localStorage":
|
|
41
|
+
return localStoreAdapter(config);
|
|
42
|
+
case "indexedDb":
|
|
43
|
+
return kvStoreAdapter(config);
|
|
100
44
|
default:
|
|
101
45
|
store.persistTo;
|
|
102
46
|
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
@@ -104,7 +48,77 @@ function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
|
104
48
|
}
|
|
105
49
|
return store;
|
|
106
50
|
}
|
|
51
|
+
function localStoreAdapter({
|
|
52
|
+
key,
|
|
53
|
+
expiryMs,
|
|
54
|
+
isValid
|
|
55
|
+
}) {
|
|
56
|
+
const adapter = (0, import_localStore.createLocalStore)(key, { defaultExpiryMs: expiryMs });
|
|
57
|
+
return {
|
|
58
|
+
set: (key2, value) => {
|
|
59
|
+
if (IS_NOT_BROWSER) return;
|
|
60
|
+
adapter.set(key2, value);
|
|
61
|
+
},
|
|
62
|
+
get: (key2) => {
|
|
63
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
64
|
+
const value = adapter.get(key2);
|
|
65
|
+
if (isValid && !isValid(value)) {
|
|
66
|
+
console.warn(
|
|
67
|
+
`localStorage adapter: Auto-discard invalid value for ${key2}: ${JSON.stringify(value)}`
|
|
68
|
+
);
|
|
69
|
+
adapter.delete(key2);
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
},
|
|
74
|
+
delete: (key2) => {
|
|
75
|
+
if (IS_NOT_BROWSER) return;
|
|
76
|
+
adapter.delete(key2);
|
|
77
|
+
},
|
|
78
|
+
clear: () => {
|
|
79
|
+
if (IS_NOT_BROWSER) return;
|
|
80
|
+
adapter.clear();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function kvStoreAdapter({
|
|
85
|
+
key,
|
|
86
|
+
expiryMs,
|
|
87
|
+
isValid
|
|
88
|
+
}) {
|
|
89
|
+
const keyPrefix = key + ":";
|
|
90
|
+
return {
|
|
91
|
+
set: async (key2, value) => {
|
|
92
|
+
if (IS_NOT_BROWSER) return;
|
|
93
|
+
await import_kvStore.kvStore.set(keyPrefix + key2, value, expiryMs);
|
|
94
|
+
},
|
|
95
|
+
get: async (key2) => {
|
|
96
|
+
if (IS_NOT_BROWSER) return void 0;
|
|
97
|
+
const fullKey = keyPrefix + key2;
|
|
98
|
+
const value = await import_kvStore.kvStore.get(fullKey);
|
|
99
|
+
if (value !== void 0 && isValid && !isValid(value)) {
|
|
100
|
+
console.warn(
|
|
101
|
+
`indexedDb adapter: Auto-discard invalid value for ${key2}:`,
|
|
102
|
+
value
|
|
103
|
+
);
|
|
104
|
+
await import_kvStore.kvStore.delete(fullKey);
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
},
|
|
109
|
+
delete: async (key2) => {
|
|
110
|
+
if (IS_NOT_BROWSER) return;
|
|
111
|
+
await import_kvStore.kvStore.delete(keyPrefix + key2);
|
|
112
|
+
},
|
|
113
|
+
clear: async () => {
|
|
114
|
+
if (IS_NOT_BROWSER) return;
|
|
115
|
+
await import_kvStore.kvStore.clear();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
107
119
|
// Annotate the CommonJS export names for ESM import in node:
|
|
108
120
|
0 && (module.exports = {
|
|
109
|
-
getStorageAdapter
|
|
121
|
+
getStorageAdapter,
|
|
122
|
+
kvStoreAdapter,
|
|
123
|
+
localStoreAdapter
|
|
110
124
|
});
|
package/storage.d.mts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import { Duration } from '@choksheak/ts-utils/duration';
|
|
1
2
|
import { StorageAdapter } from '@choksheak/ts-utils/storageAdapter';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @packageDocumentation
|
|
6
|
+
*
|
|
7
|
+
* Backing storage implementation for the shared state. Note that this uses the
|
|
8
|
+
* LocalStore and KvStore from package "@choksheak/ts-utils", but is customized
|
|
9
|
+
* to be used for shared states.
|
|
10
|
+
*
|
|
11
|
+
* Normally you will not need to use this module directly, but if you have a
|
|
12
|
+
* need for it, feel free to use it.
|
|
13
|
+
*/
|
|
14
|
+
|
|
3
15
|
/** List of all available pre-built persistence options. */
|
|
4
16
|
type PersistTo = "localStorage" | "indexedDb";
|
|
5
17
|
/** Options to configure a pre-built store adapter. */
|
|
@@ -9,10 +21,11 @@ type StoreOptions = {
|
|
|
9
21
|
/** Storage key to act as a namespace to store the data. */
|
|
10
22
|
key: string;
|
|
11
23
|
/** How long to wait before the value is considered expired. */
|
|
12
|
-
expiryMs?: number;
|
|
24
|
+
expiryMs?: number | Duration;
|
|
13
25
|
/** Returns true if the given value supposed to be of type T is valid. */
|
|
14
26
|
isValid?: (u: unknown) => boolean;
|
|
15
27
|
};
|
|
28
|
+
type StoreConstructorArgs = Omit<StoreOptions, "persistTo">;
|
|
16
29
|
/** Either use a pre-built store, or a custom one you provide. */
|
|
17
30
|
type StorageOptions<T> = {
|
|
18
31
|
/**
|
|
@@ -21,6 +34,30 @@ type StorageOptions<T> = {
|
|
|
21
34
|
*/
|
|
22
35
|
store?: StorageAdapter<T> | StoreOptions;
|
|
23
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Convert a StorageOptions<T> config object into an actual storage adapter, or
|
|
39
|
+
* returns null if a storage adapter is not configured.
|
|
40
|
+
*/
|
|
24
41
|
declare function getStorageAdapter<T>(options: StorageOptions<T> | undefined, defaultStoreExpiryMs: number): StorageAdapter<T> | null;
|
|
42
|
+
/**
|
|
43
|
+
* Create a new SSR-safe local store adapter, which allows users to reuse the
|
|
44
|
+
* same adapter in multiple shared states or shared queries.
|
|
45
|
+
*/
|
|
46
|
+
declare function localStoreAdapter<T>({ key, expiryMs, isValid, }: StoreConstructorArgs): {
|
|
47
|
+
set: (key: string, value: T) => void;
|
|
48
|
+
get: (key: string) => T | undefined;
|
|
49
|
+
delete: (key: string) => void;
|
|
50
|
+
clear: () => void;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Create a new SSR-safe indexed DB KV store adapter, which allows users to
|
|
54
|
+
* reuse the same adapter in multiple shared states or shared queries.
|
|
55
|
+
*/
|
|
56
|
+
declare function kvStoreAdapter<T>({ key, expiryMs, isValid, }: StoreConstructorArgs): {
|
|
57
|
+
set: (key: string, value: T) => Promise<void>;
|
|
58
|
+
get: (key: string) => Promise<T | undefined>;
|
|
59
|
+
delete: (key: string) => Promise<void>;
|
|
60
|
+
clear: () => Promise<void>;
|
|
61
|
+
};
|
|
25
62
|
|
|
26
|
-
export { type PersistTo, type StorageOptions, type StoreOptions, getStorageAdapter };
|
|
63
|
+
export { type PersistTo, type StorageOptions, type StoreConstructorArgs, type StoreOptions, getStorageAdapter, kvStoreAdapter, localStoreAdapter };
|