@choksheak/react-utils 0.1.5 → 0.1.7
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/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/sharedQuery.SharedQuery.html +15 -0
- package/docs/classes/sharedState.SharedState.html +8 -0
- package/docs/classes/sharedState.SharedStateConfig.html +7 -6
- package/docs/classes/utils_storage.InMemoryStorageAdapter.html +9 -0
- package/docs/classes/utils_storage.IndexedDbAdapter.html +9 -0
- package/docs/classes/utils_storage.LocalStorageAdapter.html +9 -0
- package/docs/functions/sharedQuery.sharedQuery.html +5 -0
- package/docs/functions/sharedQuery.useQuery.html +4 -0
- package/docs/functions/sharedState.sharedState.html +2 -2
- package/docs/functions/sharedState.useSharedState.html +2 -2
- package/docs/functions/sharedState.useSharedStateSetter.html +2 -2
- package/docs/functions/sharedState.useSharedStateValue.html +2 -2
- package/docs/functions/utils_storage.getStorageAdapter.html +1 -0
- package/docs/functions/utils_stringify.stringifyDeterministicForKeys.html +7 -0
- package/docs/modules/sharedQuery.html +1 -0
- package/docs/modules/sharedState.html +1 -1
- package/docs/modules/utils_storage.html +1 -0
- package/docs/modules/utils_stringify.html +1 -0
- package/docs/modules.html +1 -1
- package/docs/types/sharedQuery.FetchFn.html +1 -0
- package/docs/types/sharedQuery.FetchedData.html +3 -0
- package/docs/types/sharedQuery.QueryStateValue.html +4 -0
- package/docs/types/sharedQuery.SharedQueryOptions.html +1 -0
- package/docs/types/sharedQuery.SharedQueryState.html +3 -0
- package/docs/types/sharedState.SharedStateOptions.html +1 -0
- package/docs/types/utils_storage.StorageAdapter.html +5 -0
- package/docs/types/utils_storage.StorageOptions.html +5 -0
- package/docs/variables/fetcher.fetcher.html +1 -1
- package/docs/variables/fetcher.fetcherWithRetries.html +1 -1
- package/package.json +9 -6
- package/sharedQuery.d.mts +61 -0
- package/sharedQuery.d.ts +58 -0
- package/sharedQuery.js +471 -0
- package/sharedQuery.mjs +444 -0
- package/sharedState.d.mts +22 -16
- package/sharedState.d.ts +20 -15
- package/sharedState.js +157 -85
- package/sharedState.mjs +156 -85
- package/src/sharedQuery.ts +303 -0
- package/src/sharedState.ts +75 -137
- package/src/utils/storage.ts +197 -0
- package/src/utils/stringify.test.ts +38 -0
- package/src/utils/stringify.ts +49 -0
- package/utils/storage.d.mts +46 -0
- package/utils/storage.d.ts +44 -0
- package/utils/storage.js +162 -0
- package/utils/storage.mjs +134 -0
- package/utils/stringify.d.mts +12 -0
- package/utils/stringify.d.ts +10 -0
- package/utils/stringify.js +58 -0
- package/utils/stringify.mjs +33 -0
- package/utils/stringify.test.d.mts +2 -0
- package/utils/stringify.test.d.ts +2 -0
- package/utils/stringify.test.js +64 -0
- package/utils/stringify.test.mjs +81 -0
- package/docs/types/sharedState.SharedState.html +0 -6
package/sharedState.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/sharedState.ts
|
|
21
21
|
var sharedState_exports = {};
|
|
22
22
|
__export(sharedState_exports, {
|
|
23
|
+
SharedState: () => SharedState,
|
|
23
24
|
SharedStateConfig: () => SharedStateConfig,
|
|
24
25
|
sharedState: () => sharedState,
|
|
25
26
|
useSharedState: () => useSharedState,
|
|
@@ -27,16 +28,119 @@ __export(sharedState_exports, {
|
|
|
27
28
|
useSharedStateValue: () => useSharedStateValue
|
|
28
29
|
});
|
|
29
30
|
module.exports = __toCommonJS(sharedState_exports);
|
|
30
|
-
var
|
|
31
|
+
var import_kvStore2 = require("@choksheak/ts-utils/kvStore");
|
|
31
32
|
var import_timeConstants = require("@choksheak/ts-utils/timeConstants");
|
|
32
33
|
var import_react = require("react");
|
|
34
|
+
|
|
35
|
+
// src/utils/storage.ts
|
|
36
|
+
var import_kvStore = require("@choksheak/ts-utils/kvStore");
|
|
37
|
+
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
38
|
+
const { localStorageKey, indexedDbKey, storeExpiryMs, store } = options ?? {};
|
|
39
|
+
if (store) {
|
|
40
|
+
return store;
|
|
41
|
+
}
|
|
42
|
+
const expiryMs = storeExpiryMs || defaultStoreExpiryMs;
|
|
43
|
+
if (localStorageKey) {
|
|
44
|
+
return new LocalStorageAdapter(localStorageKey, expiryMs);
|
|
45
|
+
}
|
|
46
|
+
if (indexedDbKey) {
|
|
47
|
+
return new IndexedDbAdapter(indexedDbKey, expiryMs);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
var LocalStorageAdapter = class {
|
|
52
|
+
constructor(keyPrefix, expiryMs) {
|
|
53
|
+
this.keyPrefix = keyPrefix;
|
|
54
|
+
this.expiryMs = expiryMs;
|
|
55
|
+
}
|
|
56
|
+
save(key, value) {
|
|
57
|
+
if (typeof window === "undefined") return;
|
|
58
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
59
|
+
window.localStorage.setItem(
|
|
60
|
+
fullKey,
|
|
61
|
+
JSON.stringify({
|
|
62
|
+
value,
|
|
63
|
+
expiresAt: this.expiryMs ? Date.now() + this.expiryMs : 0
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
load(key) {
|
|
68
|
+
if (typeof window === "undefined") return void 0;
|
|
69
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
70
|
+
const raw = window.localStorage.getItem(fullKey);
|
|
71
|
+
if (!raw) return void 0;
|
|
72
|
+
try {
|
|
73
|
+
const box = JSON.parse(raw);
|
|
74
|
+
if (!box || box.expiresAt && Date.now() >= box.expiresAt) {
|
|
75
|
+
window.localStorage.removeItem(fullKey);
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
return box.value;
|
|
79
|
+
} catch {
|
|
80
|
+
window.localStorage.removeItem(fullKey);
|
|
81
|
+
return void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
delete(key) {
|
|
85
|
+
if (typeof window === "undefined") return;
|
|
86
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
87
|
+
window.localStorage.removeItem(fullKey);
|
|
88
|
+
}
|
|
89
|
+
clear() {
|
|
90
|
+
if (typeof window === "undefined") return;
|
|
91
|
+
for (const key of Object.keys(window.localStorage)) {
|
|
92
|
+
if (key.startsWith(this.keyPrefix)) {
|
|
93
|
+
window.localStorage.removeItem(key);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var IndexedDbAdapter = class {
|
|
99
|
+
constructor(keyPrefix, expiryMs) {
|
|
100
|
+
this.keyPrefix = keyPrefix;
|
|
101
|
+
this.expiryMs = expiryMs;
|
|
102
|
+
}
|
|
103
|
+
async save(key, value) {
|
|
104
|
+
if (typeof window === "undefined") return;
|
|
105
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
106
|
+
await import_kvStore.kvStore.set(fullKey, JSON.stringify(value), this.expiryMs);
|
|
107
|
+
}
|
|
108
|
+
async load(key) {
|
|
109
|
+
if (typeof window === "undefined") return void 0;
|
|
110
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
111
|
+
const raw = await import_kvStore.kvStore.get(fullKey);
|
|
112
|
+
if (raw === void 0) return void 0;
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(raw);
|
|
115
|
+
} catch {
|
|
116
|
+
await import_kvStore.kvStore.delete(fullKey);
|
|
117
|
+
return void 0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async delete(key) {
|
|
121
|
+
if (typeof window === "undefined") return;
|
|
122
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
123
|
+
await import_kvStore.kvStore.delete(fullKey);
|
|
124
|
+
}
|
|
125
|
+
async clear() {
|
|
126
|
+
if (typeof window === "undefined") return;
|
|
127
|
+
await import_kvStore.kvStore.forEach(async (k) => {
|
|
128
|
+
if (k.startsWith(this.keyPrefix)) {
|
|
129
|
+
await import_kvStore.kvStore.delete(k);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/sharedState.ts
|
|
33
136
|
var SharedStateConfig = class {
|
|
34
137
|
/**
|
|
35
|
-
* Default duration for
|
|
138
|
+
* Default duration for storage expiration in milliseconds. Initial value
|
|
139
|
+
* is 30 days, but can be changed to anything.
|
|
36
140
|
*/
|
|
37
|
-
static
|
|
141
|
+
static storeExpiryMs = import_timeConstants.MS_PER_DAY * 30;
|
|
38
142
|
/**
|
|
39
|
-
* True to load from
|
|
143
|
+
* True to load from storage only on mount, false (default) to load in
|
|
40
144
|
* the top level scope when the shared state is defined.
|
|
41
145
|
*/
|
|
42
146
|
static lazyLoad = false;
|
|
@@ -55,6 +159,9 @@ var PubSubStore = class {
|
|
|
55
159
|
this.dataByKey.set(key, value);
|
|
56
160
|
this.notify(key, value, prev);
|
|
57
161
|
}
|
|
162
|
+
delete(key) {
|
|
163
|
+
this.dataByKey.delete(key);
|
|
164
|
+
}
|
|
58
165
|
setNoNotify(key, value) {
|
|
59
166
|
this.dataByKey.set(key, value);
|
|
60
167
|
}
|
|
@@ -84,51 +191,58 @@ var PubSubStore = class {
|
|
|
84
191
|
}
|
|
85
192
|
}
|
|
86
193
|
};
|
|
87
|
-
var
|
|
194
|
+
var pubSubStore = new PubSubStore();
|
|
195
|
+
var STORAGE_KEY = "state";
|
|
88
196
|
var stateKey = 0;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
197
|
+
var SharedState = class {
|
|
198
|
+
constructor(defaultValue, options) {
|
|
199
|
+
this.defaultValue = defaultValue;
|
|
200
|
+
this.pubSubKey = String(stateKey++);
|
|
201
|
+
this.storageAdapter = getStorageAdapter(options, import_kvStore2.DEFAULT_EXPIRY_DELTA_MS);
|
|
202
|
+
pubSubStore.setNoNotify(this.pubSubKey, defaultValue);
|
|
203
|
+
const lazyLoad = options?.lazyLoad;
|
|
204
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
205
|
+
if (!lazy) {
|
|
206
|
+
void this.initDefaultValueOnce();
|
|
207
|
+
}
|
|
98
208
|
}
|
|
99
|
-
|
|
100
|
-
|
|
209
|
+
pubSubKey;
|
|
210
|
+
storageAdapter;
|
|
211
|
+
initDone = false;
|
|
212
|
+
// `subscribe` function required by useSyncExternalStore
|
|
213
|
+
subscribe(subscriber) {
|
|
214
|
+
return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
|
|
101
215
|
}
|
|
102
|
-
|
|
103
|
-
|
|
216
|
+
// Get the value of the shared state.
|
|
217
|
+
// `getSnapshot` function — must return same ref if value unchanged
|
|
218
|
+
getSnapshot() {
|
|
219
|
+
return pubSubStore.get(this.pubSubKey);
|
|
104
220
|
}
|
|
105
|
-
|
|
221
|
+
// Set the value of the shared state.
|
|
222
|
+
setValue(next) {
|
|
106
223
|
if (typeof next === "function") {
|
|
107
|
-
const prev =
|
|
224
|
+
const prev = pubSubStore.get(this.pubSubKey);
|
|
108
225
|
next = next(prev);
|
|
109
226
|
}
|
|
110
|
-
|
|
111
|
-
void
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
void state.initDefaultValueOnce();
|
|
130
|
-
}
|
|
131
|
-
return state;
|
|
227
|
+
pubSubStore.set(this.pubSubKey, next);
|
|
228
|
+
void this.storageAdapter?.save(STORAGE_KEY, next);
|
|
229
|
+
}
|
|
230
|
+
// Remove the value of the shared state. Actually this just sets the value
|
|
231
|
+
// back to the given default value.
|
|
232
|
+
delete() {
|
|
233
|
+
pubSubStore.set(this.pubSubKey, this.defaultValue);
|
|
234
|
+
void this.storageAdapter?.save(STORAGE_KEY, this.defaultValue);
|
|
235
|
+
}
|
|
236
|
+
// Initial default handling
|
|
237
|
+
async initDefaultValueOnce() {
|
|
238
|
+
if (this.initDone) return;
|
|
239
|
+
this.initDone = true;
|
|
240
|
+
const v = await this.storageAdapter?.load(STORAGE_KEY);
|
|
241
|
+
pubSubStore.set(this.pubSubKey, v !== void 0 ? v : this.defaultValue);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
function sharedState(defaultValue, options) {
|
|
245
|
+
return new SharedState(defaultValue, options);
|
|
132
246
|
}
|
|
133
247
|
function useSharedState(state) {
|
|
134
248
|
state.initDefaultValueOnce();
|
|
@@ -146,51 +260,9 @@ function useSharedStateValue(state) {
|
|
|
146
260
|
function useSharedStateSetter(state) {
|
|
147
261
|
return useSharedState(state)[1];
|
|
148
262
|
}
|
|
149
|
-
var LocalStorageAdapter = class {
|
|
150
|
-
constructor(key, expiryMs) {
|
|
151
|
-
this.key = key;
|
|
152
|
-
this.expiryMs = expiryMs;
|
|
153
|
-
}
|
|
154
|
-
save(value) {
|
|
155
|
-
if (typeof window === "undefined") return;
|
|
156
|
-
window.localStorage.setItem(
|
|
157
|
-
this.key,
|
|
158
|
-
JSON.stringify({
|
|
159
|
-
value,
|
|
160
|
-
expiresAt: Date.now() + this.expiryMs
|
|
161
|
-
})
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
load() {
|
|
165
|
-
if (typeof window === "undefined") return void 0;
|
|
166
|
-
const raw = window.localStorage.getItem(this.key);
|
|
167
|
-
if (!raw) return void 0;
|
|
168
|
-
const box = JSON.parse(raw);
|
|
169
|
-
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
170
|
-
window.localStorage.removeItem(this.key);
|
|
171
|
-
return void 0;
|
|
172
|
-
}
|
|
173
|
-
return box.value;
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
var IndexedDbAdapter = class {
|
|
177
|
-
constructor(key, expiryMs) {
|
|
178
|
-
this.key = key;
|
|
179
|
-
this.expiryMs = expiryMs;
|
|
180
|
-
}
|
|
181
|
-
async save(value) {
|
|
182
|
-
if (typeof window === "undefined") return;
|
|
183
|
-
await import_kvStore.kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
184
|
-
}
|
|
185
|
-
async load() {
|
|
186
|
-
if (typeof window === "undefined") return void 0;
|
|
187
|
-
const raw = await import_kvStore.kvStore.get(this.key);
|
|
188
|
-
if (raw === void 0) return void 0;
|
|
189
|
-
return JSON.parse(raw);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
263
|
// Annotate the CommonJS export names for ESM import in node:
|
|
193
264
|
0 && (module.exports = {
|
|
265
|
+
SharedState,
|
|
194
266
|
SharedStateConfig,
|
|
195
267
|
sharedState,
|
|
196
268
|
useSharedState,
|
package/sharedState.mjs
CHANGED
|
@@ -1,14 +1,117 @@
|
|
|
1
1
|
// src/sharedState.ts
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_EXPIRY_DELTA_MS } from "@choksheak/ts-utils/kvStore";
|
|
3
3
|
import { MS_PER_DAY } from "@choksheak/ts-utils/timeConstants";
|
|
4
4
|
import { useSyncExternalStore } from "react";
|
|
5
|
+
|
|
6
|
+
// src/utils/storage.ts
|
|
7
|
+
import { kvStore } from "@choksheak/ts-utils/kvStore";
|
|
8
|
+
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
9
|
+
const { localStorageKey, indexedDbKey, storeExpiryMs, store } = options ?? {};
|
|
10
|
+
if (store) {
|
|
11
|
+
return store;
|
|
12
|
+
}
|
|
13
|
+
const expiryMs = storeExpiryMs || defaultStoreExpiryMs;
|
|
14
|
+
if (localStorageKey) {
|
|
15
|
+
return new LocalStorageAdapter(localStorageKey, expiryMs);
|
|
16
|
+
}
|
|
17
|
+
if (indexedDbKey) {
|
|
18
|
+
return new IndexedDbAdapter(indexedDbKey, expiryMs);
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
var LocalStorageAdapter = class {
|
|
23
|
+
constructor(keyPrefix, expiryMs) {
|
|
24
|
+
this.keyPrefix = keyPrefix;
|
|
25
|
+
this.expiryMs = expiryMs;
|
|
26
|
+
}
|
|
27
|
+
save(key, value) {
|
|
28
|
+
if (typeof window === "undefined") return;
|
|
29
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
30
|
+
window.localStorage.setItem(
|
|
31
|
+
fullKey,
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
value,
|
|
34
|
+
expiresAt: this.expiryMs ? Date.now() + this.expiryMs : 0
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
load(key) {
|
|
39
|
+
if (typeof window === "undefined") return void 0;
|
|
40
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
41
|
+
const raw = window.localStorage.getItem(fullKey);
|
|
42
|
+
if (!raw) return void 0;
|
|
43
|
+
try {
|
|
44
|
+
const box = JSON.parse(raw);
|
|
45
|
+
if (!box || box.expiresAt && Date.now() >= box.expiresAt) {
|
|
46
|
+
window.localStorage.removeItem(fullKey);
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
return box.value;
|
|
50
|
+
} catch {
|
|
51
|
+
window.localStorage.removeItem(fullKey);
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
delete(key) {
|
|
56
|
+
if (typeof window === "undefined") return;
|
|
57
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
58
|
+
window.localStorage.removeItem(fullKey);
|
|
59
|
+
}
|
|
60
|
+
clear() {
|
|
61
|
+
if (typeof window === "undefined") return;
|
|
62
|
+
for (const key of Object.keys(window.localStorage)) {
|
|
63
|
+
if (key.startsWith(this.keyPrefix)) {
|
|
64
|
+
window.localStorage.removeItem(key);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var IndexedDbAdapter = class {
|
|
70
|
+
constructor(keyPrefix, expiryMs) {
|
|
71
|
+
this.keyPrefix = keyPrefix;
|
|
72
|
+
this.expiryMs = expiryMs;
|
|
73
|
+
}
|
|
74
|
+
async save(key, value) {
|
|
75
|
+
if (typeof window === "undefined") return;
|
|
76
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
77
|
+
await kvStore.set(fullKey, JSON.stringify(value), this.expiryMs);
|
|
78
|
+
}
|
|
79
|
+
async load(key) {
|
|
80
|
+
if (typeof window === "undefined") return void 0;
|
|
81
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
82
|
+
const raw = await kvStore.get(fullKey);
|
|
83
|
+
if (raw === void 0) return void 0;
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(raw);
|
|
86
|
+
} catch {
|
|
87
|
+
await kvStore.delete(fullKey);
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async delete(key) {
|
|
92
|
+
if (typeof window === "undefined") return;
|
|
93
|
+
const fullKey = this.keyPrefix + ":" + key;
|
|
94
|
+
await kvStore.delete(fullKey);
|
|
95
|
+
}
|
|
96
|
+
async clear() {
|
|
97
|
+
if (typeof window === "undefined") return;
|
|
98
|
+
await kvStore.forEach(async (k) => {
|
|
99
|
+
if (k.startsWith(this.keyPrefix)) {
|
|
100
|
+
await kvStore.delete(k);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/sharedState.ts
|
|
5
107
|
var SharedStateConfig = class {
|
|
6
108
|
/**
|
|
7
|
-
* Default duration for
|
|
109
|
+
* Default duration for storage expiration in milliseconds. Initial value
|
|
110
|
+
* is 30 days, but can be changed to anything.
|
|
8
111
|
*/
|
|
9
|
-
static
|
|
112
|
+
static storeExpiryMs = MS_PER_DAY * 30;
|
|
10
113
|
/**
|
|
11
|
-
* True to load from
|
|
114
|
+
* True to load from storage only on mount, false (default) to load in
|
|
12
115
|
* the top level scope when the shared state is defined.
|
|
13
116
|
*/
|
|
14
117
|
static lazyLoad = false;
|
|
@@ -27,6 +130,9 @@ var PubSubStore = class {
|
|
|
27
130
|
this.dataByKey.set(key, value);
|
|
28
131
|
this.notify(key, value, prev);
|
|
29
132
|
}
|
|
133
|
+
delete(key) {
|
|
134
|
+
this.dataByKey.delete(key);
|
|
135
|
+
}
|
|
30
136
|
setNoNotify(key, value) {
|
|
31
137
|
this.dataByKey.set(key, value);
|
|
32
138
|
}
|
|
@@ -56,51 +162,58 @@ var PubSubStore = class {
|
|
|
56
162
|
}
|
|
57
163
|
}
|
|
58
164
|
};
|
|
59
|
-
var
|
|
165
|
+
var pubSubStore = new PubSubStore();
|
|
166
|
+
var STORAGE_KEY = "state";
|
|
60
167
|
var stateKey = 0;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
168
|
+
var SharedState = class {
|
|
169
|
+
constructor(defaultValue, options) {
|
|
170
|
+
this.defaultValue = defaultValue;
|
|
171
|
+
this.pubSubKey = String(stateKey++);
|
|
172
|
+
this.storageAdapter = getStorageAdapter(options, DEFAULT_EXPIRY_DELTA_MS);
|
|
173
|
+
pubSubStore.setNoNotify(this.pubSubKey, defaultValue);
|
|
174
|
+
const lazyLoad = options?.lazyLoad;
|
|
175
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
176
|
+
if (!lazy) {
|
|
177
|
+
void this.initDefaultValueOnce();
|
|
178
|
+
}
|
|
70
179
|
}
|
|
71
|
-
|
|
72
|
-
|
|
180
|
+
pubSubKey;
|
|
181
|
+
storageAdapter;
|
|
182
|
+
initDone = false;
|
|
183
|
+
// `subscribe` function required by useSyncExternalStore
|
|
184
|
+
subscribe(subscriber) {
|
|
185
|
+
return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
|
|
73
186
|
}
|
|
74
|
-
|
|
75
|
-
|
|
187
|
+
// Get the value of the shared state.
|
|
188
|
+
// `getSnapshot` function — must return same ref if value unchanged
|
|
189
|
+
getSnapshot() {
|
|
190
|
+
return pubSubStore.get(this.pubSubKey);
|
|
76
191
|
}
|
|
77
|
-
|
|
192
|
+
// Set the value of the shared state.
|
|
193
|
+
setValue(next) {
|
|
78
194
|
if (typeof next === "function") {
|
|
79
|
-
const prev =
|
|
195
|
+
const prev = pubSubStore.get(this.pubSubKey);
|
|
80
196
|
next = next(prev);
|
|
81
197
|
}
|
|
82
|
-
|
|
83
|
-
void
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
void state.initDefaultValueOnce();
|
|
102
|
-
}
|
|
103
|
-
return state;
|
|
198
|
+
pubSubStore.set(this.pubSubKey, next);
|
|
199
|
+
void this.storageAdapter?.save(STORAGE_KEY, next);
|
|
200
|
+
}
|
|
201
|
+
// Remove the value of the shared state. Actually this just sets the value
|
|
202
|
+
// back to the given default value.
|
|
203
|
+
delete() {
|
|
204
|
+
pubSubStore.set(this.pubSubKey, this.defaultValue);
|
|
205
|
+
void this.storageAdapter?.save(STORAGE_KEY, this.defaultValue);
|
|
206
|
+
}
|
|
207
|
+
// Initial default handling
|
|
208
|
+
async initDefaultValueOnce() {
|
|
209
|
+
if (this.initDone) return;
|
|
210
|
+
this.initDone = true;
|
|
211
|
+
const v = await this.storageAdapter?.load(STORAGE_KEY);
|
|
212
|
+
pubSubStore.set(this.pubSubKey, v !== void 0 ? v : this.defaultValue);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
function sharedState(defaultValue, options) {
|
|
216
|
+
return new SharedState(defaultValue, options);
|
|
104
217
|
}
|
|
105
218
|
function useSharedState(state) {
|
|
106
219
|
state.initDefaultValueOnce();
|
|
@@ -118,50 +231,8 @@ function useSharedStateValue(state) {
|
|
|
118
231
|
function useSharedStateSetter(state) {
|
|
119
232
|
return useSharedState(state)[1];
|
|
120
233
|
}
|
|
121
|
-
var LocalStorageAdapter = class {
|
|
122
|
-
constructor(key, expiryMs) {
|
|
123
|
-
this.key = key;
|
|
124
|
-
this.expiryMs = expiryMs;
|
|
125
|
-
}
|
|
126
|
-
save(value) {
|
|
127
|
-
if (typeof window === "undefined") return;
|
|
128
|
-
window.localStorage.setItem(
|
|
129
|
-
this.key,
|
|
130
|
-
JSON.stringify({
|
|
131
|
-
value,
|
|
132
|
-
expiresAt: Date.now() + this.expiryMs
|
|
133
|
-
})
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
load() {
|
|
137
|
-
if (typeof window === "undefined") return void 0;
|
|
138
|
-
const raw = window.localStorage.getItem(this.key);
|
|
139
|
-
if (!raw) return void 0;
|
|
140
|
-
const box = JSON.parse(raw);
|
|
141
|
-
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
142
|
-
window.localStorage.removeItem(this.key);
|
|
143
|
-
return void 0;
|
|
144
|
-
}
|
|
145
|
-
return box.value;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
var IndexedDbAdapter = class {
|
|
149
|
-
constructor(key, expiryMs) {
|
|
150
|
-
this.key = key;
|
|
151
|
-
this.expiryMs = expiryMs;
|
|
152
|
-
}
|
|
153
|
-
async save(value) {
|
|
154
|
-
if (typeof window === "undefined") return;
|
|
155
|
-
await kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
156
|
-
}
|
|
157
|
-
async load() {
|
|
158
|
-
if (typeof window === "undefined") return void 0;
|
|
159
|
-
const raw = await kvStore.get(this.key);
|
|
160
|
-
if (raw === void 0) return void 0;
|
|
161
|
-
return JSON.parse(raw);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
234
|
export {
|
|
235
|
+
SharedState,
|
|
165
236
|
SharedStateConfig,
|
|
166
237
|
sharedState,
|
|
167
238
|
useSharedState,
|