@choksheak/react-utils 0.1.6 → 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 +5 -5
- 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 +21 -16
- package/sharedState.d.ts +19 -15
- package/sharedState.js +156 -85
- package/sharedState.mjs +155 -85
- package/src/sharedQuery.ts +303 -0
- package/src/sharedState.ts +74 -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,17 +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
|
|
36
139
|
* is 30 days, but can be changed to anything.
|
|
37
140
|
*/
|
|
38
|
-
static
|
|
141
|
+
static storeExpiryMs = import_timeConstants.MS_PER_DAY * 30;
|
|
39
142
|
/**
|
|
40
|
-
* True to load from
|
|
143
|
+
* True to load from storage only on mount, false (default) to load in
|
|
41
144
|
* the top level scope when the shared state is defined.
|
|
42
145
|
*/
|
|
43
146
|
static lazyLoad = false;
|
|
@@ -56,6 +159,9 @@ var PubSubStore = class {
|
|
|
56
159
|
this.dataByKey.set(key, value);
|
|
57
160
|
this.notify(key, value, prev);
|
|
58
161
|
}
|
|
162
|
+
delete(key) {
|
|
163
|
+
this.dataByKey.delete(key);
|
|
164
|
+
}
|
|
59
165
|
setNoNotify(key, value) {
|
|
60
166
|
this.dataByKey.set(key, value);
|
|
61
167
|
}
|
|
@@ -85,51 +191,58 @@ var PubSubStore = class {
|
|
|
85
191
|
}
|
|
86
192
|
}
|
|
87
193
|
};
|
|
88
|
-
var
|
|
194
|
+
var pubSubStore = new PubSubStore();
|
|
195
|
+
var STORAGE_KEY = "state";
|
|
89
196
|
var stateKey = 0;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
}
|
|
99
208
|
}
|
|
100
|
-
|
|
101
|
-
|
|
209
|
+
pubSubKey;
|
|
210
|
+
storageAdapter;
|
|
211
|
+
initDone = false;
|
|
212
|
+
// `subscribe` function required by useSyncExternalStore
|
|
213
|
+
subscribe(subscriber) {
|
|
214
|
+
return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
|
|
102
215
|
}
|
|
103
|
-
|
|
104
|
-
|
|
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);
|
|
105
220
|
}
|
|
106
|
-
|
|
221
|
+
// Set the value of the shared state.
|
|
222
|
+
setValue(next) {
|
|
107
223
|
if (typeof next === "function") {
|
|
108
|
-
const prev =
|
|
224
|
+
const prev = pubSubStore.get(this.pubSubKey);
|
|
109
225
|
next = next(prev);
|
|
110
226
|
}
|
|
111
|
-
|
|
112
|
-
void
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
void state.initDefaultValueOnce();
|
|
131
|
-
}
|
|
132
|
-
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);
|
|
133
246
|
}
|
|
134
247
|
function useSharedState(state) {
|
|
135
248
|
state.initDefaultValueOnce();
|
|
@@ -147,51 +260,9 @@ function useSharedStateValue(state) {
|
|
|
147
260
|
function useSharedStateSetter(state) {
|
|
148
261
|
return useSharedState(state)[1];
|
|
149
262
|
}
|
|
150
|
-
var LocalStorageAdapter = class {
|
|
151
|
-
constructor(key, expiryMs) {
|
|
152
|
-
this.key = key;
|
|
153
|
-
this.expiryMs = expiryMs;
|
|
154
|
-
}
|
|
155
|
-
save(value) {
|
|
156
|
-
if (typeof window === "undefined") return;
|
|
157
|
-
window.localStorage.setItem(
|
|
158
|
-
this.key,
|
|
159
|
-
JSON.stringify({
|
|
160
|
-
value,
|
|
161
|
-
expiresAt: Date.now() + this.expiryMs
|
|
162
|
-
})
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
load() {
|
|
166
|
-
if (typeof window === "undefined") return void 0;
|
|
167
|
-
const raw = window.localStorage.getItem(this.key);
|
|
168
|
-
if (!raw) return void 0;
|
|
169
|
-
const box = JSON.parse(raw);
|
|
170
|
-
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
171
|
-
window.localStorage.removeItem(this.key);
|
|
172
|
-
return void 0;
|
|
173
|
-
}
|
|
174
|
-
return box.value;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
var IndexedDbAdapter = class {
|
|
178
|
-
constructor(key, expiryMs) {
|
|
179
|
-
this.key = key;
|
|
180
|
-
this.expiryMs = expiryMs;
|
|
181
|
-
}
|
|
182
|
-
async save(value) {
|
|
183
|
-
if (typeof window === "undefined") return;
|
|
184
|
-
await import_kvStore.kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
185
|
-
}
|
|
186
|
-
async load() {
|
|
187
|
-
if (typeof window === "undefined") return void 0;
|
|
188
|
-
const raw = await import_kvStore.kvStore.get(this.key);
|
|
189
|
-
if (raw === void 0) return void 0;
|
|
190
|
-
return JSON.parse(raw);
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
263
|
// Annotate the CommonJS export names for ESM import in node:
|
|
194
264
|
0 && (module.exports = {
|
|
265
|
+
SharedState,
|
|
195
266
|
SharedStateConfig,
|
|
196
267
|
sharedState,
|
|
197
268
|
useSharedState,
|
package/sharedState.mjs
CHANGED
|
@@ -1,15 +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
|
|
8
110
|
* is 30 days, but can be changed to anything.
|
|
9
111
|
*/
|
|
10
|
-
static
|
|
112
|
+
static storeExpiryMs = MS_PER_DAY * 30;
|
|
11
113
|
/**
|
|
12
|
-
* True to load from
|
|
114
|
+
* True to load from storage only on mount, false (default) to load in
|
|
13
115
|
* the top level scope when the shared state is defined.
|
|
14
116
|
*/
|
|
15
117
|
static lazyLoad = false;
|
|
@@ -28,6 +130,9 @@ var PubSubStore = class {
|
|
|
28
130
|
this.dataByKey.set(key, value);
|
|
29
131
|
this.notify(key, value, prev);
|
|
30
132
|
}
|
|
133
|
+
delete(key) {
|
|
134
|
+
this.dataByKey.delete(key);
|
|
135
|
+
}
|
|
31
136
|
setNoNotify(key, value) {
|
|
32
137
|
this.dataByKey.set(key, value);
|
|
33
138
|
}
|
|
@@ -57,51 +162,58 @@ var PubSubStore = class {
|
|
|
57
162
|
}
|
|
58
163
|
}
|
|
59
164
|
};
|
|
60
|
-
var
|
|
165
|
+
var pubSubStore = new PubSubStore();
|
|
166
|
+
var STORAGE_KEY = "state";
|
|
61
167
|
var stateKey = 0;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
+
}
|
|
71
179
|
}
|
|
72
|
-
|
|
73
|
-
|
|
180
|
+
pubSubKey;
|
|
181
|
+
storageAdapter;
|
|
182
|
+
initDone = false;
|
|
183
|
+
// `subscribe` function required by useSyncExternalStore
|
|
184
|
+
subscribe(subscriber) {
|
|
185
|
+
return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
|
|
74
186
|
}
|
|
75
|
-
|
|
76
|
-
|
|
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);
|
|
77
191
|
}
|
|
78
|
-
|
|
192
|
+
// Set the value of the shared state.
|
|
193
|
+
setValue(next) {
|
|
79
194
|
if (typeof next === "function") {
|
|
80
|
-
const prev =
|
|
195
|
+
const prev = pubSubStore.get(this.pubSubKey);
|
|
81
196
|
next = next(prev);
|
|
82
197
|
}
|
|
83
|
-
|
|
84
|
-
void
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
void state.initDefaultValueOnce();
|
|
103
|
-
}
|
|
104
|
-
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);
|
|
105
217
|
}
|
|
106
218
|
function useSharedState(state) {
|
|
107
219
|
state.initDefaultValueOnce();
|
|
@@ -119,50 +231,8 @@ function useSharedStateValue(state) {
|
|
|
119
231
|
function useSharedStateSetter(state) {
|
|
120
232
|
return useSharedState(state)[1];
|
|
121
233
|
}
|
|
122
|
-
var LocalStorageAdapter = class {
|
|
123
|
-
constructor(key, expiryMs) {
|
|
124
|
-
this.key = key;
|
|
125
|
-
this.expiryMs = expiryMs;
|
|
126
|
-
}
|
|
127
|
-
save(value) {
|
|
128
|
-
if (typeof window === "undefined") return;
|
|
129
|
-
window.localStorage.setItem(
|
|
130
|
-
this.key,
|
|
131
|
-
JSON.stringify({
|
|
132
|
-
value,
|
|
133
|
-
expiresAt: Date.now() + this.expiryMs
|
|
134
|
-
})
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
load() {
|
|
138
|
-
if (typeof window === "undefined") return void 0;
|
|
139
|
-
const raw = window.localStorage.getItem(this.key);
|
|
140
|
-
if (!raw) return void 0;
|
|
141
|
-
const box = JSON.parse(raw);
|
|
142
|
-
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
143
|
-
window.localStorage.removeItem(this.key);
|
|
144
|
-
return void 0;
|
|
145
|
-
}
|
|
146
|
-
return box.value;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
var IndexedDbAdapter = class {
|
|
150
|
-
constructor(key, expiryMs) {
|
|
151
|
-
this.key = key;
|
|
152
|
-
this.expiryMs = expiryMs;
|
|
153
|
-
}
|
|
154
|
-
async save(value) {
|
|
155
|
-
if (typeof window === "undefined") return;
|
|
156
|
-
await kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
157
|
-
}
|
|
158
|
-
async load() {
|
|
159
|
-
if (typeof window === "undefined") return void 0;
|
|
160
|
-
const raw = await kvStore.get(this.key);
|
|
161
|
-
if (raw === void 0) return void 0;
|
|
162
|
-
return JSON.parse(raw);
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
234
|
export {
|
|
235
|
+
SharedState,
|
|
166
236
|
SharedStateConfig,
|
|
167
237
|
sharedState,
|
|
168
238
|
useSharedState,
|