@choksheak/react-utils 0.1.1 → 0.1.3
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/.nojekyll +1 -0
- package/docs/assets/hierarchy.js +1 -0
- package/docs/assets/highlight.css +22 -0
- package/docs/assets/icons.js +18 -0
- package/docs/assets/icons.svg +1 -0
- package/docs/assets/main.js +60 -0
- package/docs/assets/navigation.js +1 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1633 -0
- package/docs/classes/sharedState.SharedStateConfig.html +8 -0
- package/docs/functions/sharedState.sharedState.html +3 -0
- package/docs/functions/sharedState.useSharedState.html +3 -0
- package/docs/functions/sharedState.useSharedStateSetter.html +2 -0
- package/docs/functions/sharedState.useSharedStateValue.html +2 -0
- package/docs/hierarchy.html +1 -0
- package/docs/index.html +2 -0
- package/docs/modules/fetcher.html +1 -0
- package/docs/modules/index.html +1 -0
- package/docs/modules/sharedState.html +1 -0
- package/docs/modules.html +1 -0
- package/docs/types/sharedState.SharedState.html +6 -0
- package/docs/variables/fetcher.fetcher.html +2 -0
- package/docs/variables/fetcher.fetcherWithRetries.html +2 -0
- package/fetcher.d.mts +8 -0
- package/fetcher.d.ts +4 -0
- package/fetcher.js +51 -0
- package/fetcher.mjs +15 -0
- package/index.d.mts +2 -0
- package/index.d.ts +0 -0
- package/index.mjs +12 -0
- package/package.json +2 -2
- package/sharedState.d.mts +55 -0
- package/sharedState.d.ts +53 -0
- package/sharedState.js +188 -0
- package/sharedState.mjs +159 -0
- package/src/fetcher.ts +14 -0
- package/src/index.ts +4 -0
- package/src/sharedState.ts +272 -0
package/index.mjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
3
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
var require_index = __commonJS({
|
|
8
|
+
"src/index.ts"() {
|
|
9
|
+
console.log(`Hello from Chok's React Utils`);
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
export default require_index();
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@choksheak/react-utils",
|
|
3
3
|
"license": "The Unlicense",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"description": "Miscellaneous React+TypeScript utilities with support for full tree-shaking.",
|
|
6
6
|
"private": false,
|
|
7
7
|
"main": "index.js",
|
|
8
8
|
"module": "index.mjs",
|
|
9
9
|
"types": "index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
-
"
|
|
11
|
+
"**/*"
|
|
12
12
|
],
|
|
13
13
|
"exports": {
|
|
14
14
|
"./package.json": "./package.json",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "shared state" is a React state that shares the same value across the
|
|
3
|
+
* entire app. It is largely similar to the jotai library, but is much smaller
|
|
4
|
+
* and much easier to work with when used for persisting in local storage or
|
|
5
|
+
* indexed db, with an automatic expiration for persisted values.
|
|
6
|
+
*
|
|
7
|
+
* This library aims to be the shortest, simplest, easiest to use, and most
|
|
8
|
+
* efficient implementation of shared global React states with seamless support
|
|
9
|
+
* for client persistence.
|
|
10
|
+
*/
|
|
11
|
+
/************************************************************************/
|
|
12
|
+
/************************************************************************/
|
|
13
|
+
/**
|
|
14
|
+
* These values are configurable by the user at any time.
|
|
15
|
+
*/
|
|
16
|
+
declare class SharedStateConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Default duration for persistence expiration in milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
static defaultPersistExpiryMs: number;
|
|
21
|
+
/**
|
|
22
|
+
* True to load from persistence only on mount, false (default) to load in
|
|
23
|
+
* the top level scope when the shared state is defined.
|
|
24
|
+
*/
|
|
25
|
+
static lazyLoad: boolean;
|
|
26
|
+
}
|
|
27
|
+
type SharedState<T> = {
|
|
28
|
+
subscribe: (subscriber: () => void) => () => void;
|
|
29
|
+
getSnapshot: () => T | undefined;
|
|
30
|
+
initDefaultValueOnce: () => Promise<void> | void;
|
|
31
|
+
initDone: boolean;
|
|
32
|
+
setValue: (next: T | ((prev: T | undefined) => T)) => void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Create a new shared state object. Put this code at the top level scope:
|
|
36
|
+
*
|
|
37
|
+
* const myState = sharedState<number>(0);
|
|
38
|
+
*/
|
|
39
|
+
declare function sharedState<T>(defaultValue: T, options?: {
|
|
40
|
+
localStorageKey?: string;
|
|
41
|
+
indexedDbKey?: string;
|
|
42
|
+
persistExpiryMs?: number;
|
|
43
|
+
lazyLoad?: boolean;
|
|
44
|
+
}): SharedState<T>;
|
|
45
|
+
/**
|
|
46
|
+
* React hook for subscribing to a key in the global store.
|
|
47
|
+
* const [my, setMy] = useSharedState(myState);
|
|
48
|
+
*/
|
|
49
|
+
declare function useSharedState<T>(state: SharedState<T>): [T | undefined, (next: T | ((prev: T | undefined) => T)) => void];
|
|
50
|
+
/** E.g. const my = useSharedStateValue(myState) */
|
|
51
|
+
declare function useSharedStateValue<T>(state: SharedState<T>): T | undefined;
|
|
52
|
+
/** E.g. const setMy = useSharedStateSetter(myState) */
|
|
53
|
+
declare function useSharedStateSetter<T>(state: SharedState<T>): (next: T | ((prev: T | undefined) => T)) => void;
|
|
54
|
+
|
|
55
|
+
export { type SharedState, SharedStateConfig, sharedState, useSharedState, useSharedStateSetter, useSharedStateValue };
|
package/sharedState.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "shared state" is a React state that shares the same value across the
|
|
3
|
+
* entire app. It is largely similar to the jotai library, but is much smaller
|
|
4
|
+
* and much easier to work with when used for persisting in local storage or
|
|
5
|
+
* indexed db, with an automatic expiration for persisted values.
|
|
6
|
+
*
|
|
7
|
+
* This library aims to be the shortest, simplest, easiest to use, and most
|
|
8
|
+
* efficient implementation of shared global React states with seamless support
|
|
9
|
+
* for client persistence.
|
|
10
|
+
*/
|
|
11
|
+
/************************************************************************/
|
|
12
|
+
/************************************************************************/
|
|
13
|
+
/**
|
|
14
|
+
* These values are configurable by the user at any time.
|
|
15
|
+
*/
|
|
16
|
+
export declare class SharedStateConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Default duration for persistence expiration in milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
static defaultPersistExpiryMs: number;
|
|
21
|
+
/**
|
|
22
|
+
* True to load from persistence only on mount, false (default) to load in
|
|
23
|
+
* the top level scope when the shared state is defined.
|
|
24
|
+
*/
|
|
25
|
+
static lazyLoad: boolean;
|
|
26
|
+
}
|
|
27
|
+
export type SharedState<T> = {
|
|
28
|
+
subscribe: (subscriber: () => void) => () => void;
|
|
29
|
+
getSnapshot: () => T | undefined;
|
|
30
|
+
initDefaultValueOnce: () => Promise<void> | void;
|
|
31
|
+
initDone: boolean;
|
|
32
|
+
setValue: (next: T | ((prev: T | undefined) => T)) => void;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Create a new shared state object. Put this code at the top level scope:
|
|
36
|
+
*
|
|
37
|
+
* const myState = sharedState<number>(0);
|
|
38
|
+
*/
|
|
39
|
+
export declare function sharedState<T>(defaultValue: T, options?: {
|
|
40
|
+
localStorageKey?: string;
|
|
41
|
+
indexedDbKey?: string;
|
|
42
|
+
persistExpiryMs?: number;
|
|
43
|
+
lazyLoad?: boolean;
|
|
44
|
+
}): SharedState<T>;
|
|
45
|
+
/**
|
|
46
|
+
* React hook for subscribing to a key in the global store.
|
|
47
|
+
* const [my, setMy] = useSharedState(myState);
|
|
48
|
+
*/
|
|
49
|
+
export declare function useSharedState<T>(state: SharedState<T>): [T | undefined, (next: T | ((prev: T | undefined) => T)) => void];
|
|
50
|
+
/** E.g. const my = useSharedStateValue(myState) */
|
|
51
|
+
export declare function useSharedStateValue<T>(state: SharedState<T>): T | undefined;
|
|
52
|
+
/** E.g. const setMy = useSharedStateSetter(myState) */
|
|
53
|
+
export declare function useSharedStateSetter<T>(state: SharedState<T>): (next: T | ((prev: T | undefined) => T)) => void;
|
package/sharedState.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/sharedState.ts
|
|
21
|
+
var sharedState_exports = {};
|
|
22
|
+
__export(sharedState_exports, {
|
|
23
|
+
SharedStateConfig: () => SharedStateConfig,
|
|
24
|
+
sharedState: () => sharedState,
|
|
25
|
+
useSharedState: () => useSharedState,
|
|
26
|
+
useSharedStateSetter: () => useSharedStateSetter,
|
|
27
|
+
useSharedStateValue: () => useSharedStateValue
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(sharedState_exports);
|
|
30
|
+
var import_kvStore = require("@choksheak/ts-utils/kvStore");
|
|
31
|
+
var import_timeConstants = require("@choksheak/ts-utils/timeConstants");
|
|
32
|
+
var import_react = require("react");
|
|
33
|
+
var SharedStateConfig = class {
|
|
34
|
+
/**
|
|
35
|
+
* Default duration for persistence expiration in milliseconds.
|
|
36
|
+
*/
|
|
37
|
+
static defaultPersistExpiryMs = import_timeConstants.MS_PER_DAY;
|
|
38
|
+
/**
|
|
39
|
+
* True to load from persistence only on mount, false (default) to load in
|
|
40
|
+
* the top level scope when the shared state is defined.
|
|
41
|
+
*/
|
|
42
|
+
static lazyLoad = false;
|
|
43
|
+
};
|
|
44
|
+
var PubSubStore = class {
|
|
45
|
+
dataByKey = /* @__PURE__ */ new Map();
|
|
46
|
+
subscribersByKey = /* @__PURE__ */ new Map();
|
|
47
|
+
get(key) {
|
|
48
|
+
return this.dataByKey.get(key);
|
|
49
|
+
}
|
|
50
|
+
set(key, value) {
|
|
51
|
+
const prev = this.dataByKey.get(key);
|
|
52
|
+
if (prev === value) return;
|
|
53
|
+
this.dataByKey.set(key, value);
|
|
54
|
+
this.notify(key, value, prev);
|
|
55
|
+
}
|
|
56
|
+
// Subscribe to changes of a specific key
|
|
57
|
+
subscribe(key, subscriber) {
|
|
58
|
+
let subscribers = this.subscribersByKey.get(key);
|
|
59
|
+
if (!subscribers) {
|
|
60
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
61
|
+
this.subscribersByKey.set(key, subscribers);
|
|
62
|
+
}
|
|
63
|
+
subscribers.add(subscriber);
|
|
64
|
+
return () => {
|
|
65
|
+
subscribers.delete(subscriber);
|
|
66
|
+
if (subscribers.size === 0) this.subscribersByKey.delete(key);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
notify(key, next, prev) {
|
|
70
|
+
const subscribers = this.subscribersByKey.get(key);
|
|
71
|
+
if (!subscribers) return;
|
|
72
|
+
const copy = Array.from(subscribers.values());
|
|
73
|
+
for (const subscriber of copy) {
|
|
74
|
+
try {
|
|
75
|
+
subscriber(next, prev);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error(`Error invoking subscriber:`, e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var store = new PubSubStore();
|
|
83
|
+
var stateKey = 0;
|
|
84
|
+
function sharedState(defaultValue, options) {
|
|
85
|
+
const { localStorageKey, indexedDbKey, persistExpiryMs, lazyLoad } = options ?? {};
|
|
86
|
+
const key = String(stateKey++);
|
|
87
|
+
let persistenceAdapter;
|
|
88
|
+
const expiryMs = persistExpiryMs ?? SharedStateConfig.defaultPersistExpiryMs;
|
|
89
|
+
if (localStorageKey) {
|
|
90
|
+
persistenceAdapter = new LocalStorageAdapter(localStorageKey, expiryMs);
|
|
91
|
+
} else if (indexedDbKey) {
|
|
92
|
+
persistenceAdapter = new IndexedDbAdapter(indexedDbKey, expiryMs);
|
|
93
|
+
}
|
|
94
|
+
function subscribe(subscriber) {
|
|
95
|
+
return store.subscribe(key, () => subscriber());
|
|
96
|
+
}
|
|
97
|
+
function getSnapshot() {
|
|
98
|
+
return store.get(key);
|
|
99
|
+
}
|
|
100
|
+
function setValue(next) {
|
|
101
|
+
if (typeof next === "function") {
|
|
102
|
+
const prev = store.get(key);
|
|
103
|
+
next = next(prev);
|
|
104
|
+
}
|
|
105
|
+
store.set(key, next);
|
|
106
|
+
void persistenceAdapter?.save?.(next);
|
|
107
|
+
}
|
|
108
|
+
const state = {
|
|
109
|
+
subscribe,
|
|
110
|
+
getSnapshot,
|
|
111
|
+
// initial default handling
|
|
112
|
+
initDefaultValueOnce: async () => {
|
|
113
|
+
if (state.initDone) return;
|
|
114
|
+
state.initDone = true;
|
|
115
|
+
const v = await persistenceAdapter?.load?.();
|
|
116
|
+
store.set(key, v !== void 0 ? v : defaultValue);
|
|
117
|
+
},
|
|
118
|
+
initDone: false,
|
|
119
|
+
setValue
|
|
120
|
+
};
|
|
121
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
122
|
+
if (!lazy) {
|
|
123
|
+
void state.initDefaultValueOnce();
|
|
124
|
+
}
|
|
125
|
+
return state;
|
|
126
|
+
}
|
|
127
|
+
function useSharedState(state) {
|
|
128
|
+
state.initDefaultValueOnce();
|
|
129
|
+
const value = (0, import_react.useSyncExternalStore)(state.subscribe, state.getSnapshot);
|
|
130
|
+
return [value, state.setValue];
|
|
131
|
+
}
|
|
132
|
+
function useSharedStateValue(state) {
|
|
133
|
+
return useSharedState(state)[0];
|
|
134
|
+
}
|
|
135
|
+
function useSharedStateSetter(state) {
|
|
136
|
+
return useSharedState(state)[1];
|
|
137
|
+
}
|
|
138
|
+
var LocalStorageAdapter = class {
|
|
139
|
+
constructor(key, expiryMs) {
|
|
140
|
+
this.key = key;
|
|
141
|
+
this.expiryMs = expiryMs;
|
|
142
|
+
}
|
|
143
|
+
save(value) {
|
|
144
|
+
if (typeof window === "undefined") return;
|
|
145
|
+
window.localStorage.setItem(
|
|
146
|
+
this.key,
|
|
147
|
+
JSON.stringify({
|
|
148
|
+
value,
|
|
149
|
+
expiresAt: Date.now() + this.expiryMs
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
load() {
|
|
154
|
+
if (typeof window === "undefined") return void 0;
|
|
155
|
+
const raw = window.localStorage.getItem(this.key);
|
|
156
|
+
if (!raw) return void 0;
|
|
157
|
+
const box = JSON.parse(raw);
|
|
158
|
+
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
159
|
+
window.localStorage.removeItem(this.key);
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
return box.value;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var IndexedDbAdapter = class {
|
|
166
|
+
constructor(key, expiryMs) {
|
|
167
|
+
this.key = key;
|
|
168
|
+
this.expiryMs = expiryMs;
|
|
169
|
+
}
|
|
170
|
+
async save(value) {
|
|
171
|
+
if (typeof window === "undefined") return;
|
|
172
|
+
await import_kvStore.kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
173
|
+
}
|
|
174
|
+
async load() {
|
|
175
|
+
if (typeof window === "undefined") return void 0;
|
|
176
|
+
const raw = await import_kvStore.kvStore.get(this.key);
|
|
177
|
+
if (raw === void 0) return void 0;
|
|
178
|
+
return JSON.parse(raw);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
182
|
+
0 && (module.exports = {
|
|
183
|
+
SharedStateConfig,
|
|
184
|
+
sharedState,
|
|
185
|
+
useSharedState,
|
|
186
|
+
useSharedStateSetter,
|
|
187
|
+
useSharedStateValue
|
|
188
|
+
});
|
package/sharedState.mjs
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// src/sharedState.ts
|
|
2
|
+
import { kvStore } from "@choksheak/ts-utils/kvStore";
|
|
3
|
+
import { MS_PER_DAY } from "@choksheak/ts-utils/timeConstants";
|
|
4
|
+
import { useSyncExternalStore } from "react";
|
|
5
|
+
var SharedStateConfig = class {
|
|
6
|
+
/**
|
|
7
|
+
* Default duration for persistence expiration in milliseconds.
|
|
8
|
+
*/
|
|
9
|
+
static defaultPersistExpiryMs = MS_PER_DAY;
|
|
10
|
+
/**
|
|
11
|
+
* True to load from persistence only on mount, false (default) to load in
|
|
12
|
+
* the top level scope when the shared state is defined.
|
|
13
|
+
*/
|
|
14
|
+
static lazyLoad = false;
|
|
15
|
+
};
|
|
16
|
+
var PubSubStore = class {
|
|
17
|
+
dataByKey = /* @__PURE__ */ new Map();
|
|
18
|
+
subscribersByKey = /* @__PURE__ */ new Map();
|
|
19
|
+
get(key) {
|
|
20
|
+
return this.dataByKey.get(key);
|
|
21
|
+
}
|
|
22
|
+
set(key, value) {
|
|
23
|
+
const prev = this.dataByKey.get(key);
|
|
24
|
+
if (prev === value) return;
|
|
25
|
+
this.dataByKey.set(key, value);
|
|
26
|
+
this.notify(key, value, prev);
|
|
27
|
+
}
|
|
28
|
+
// Subscribe to changes of a specific key
|
|
29
|
+
subscribe(key, subscriber) {
|
|
30
|
+
let subscribers = this.subscribersByKey.get(key);
|
|
31
|
+
if (!subscribers) {
|
|
32
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
33
|
+
this.subscribersByKey.set(key, subscribers);
|
|
34
|
+
}
|
|
35
|
+
subscribers.add(subscriber);
|
|
36
|
+
return () => {
|
|
37
|
+
subscribers.delete(subscriber);
|
|
38
|
+
if (subscribers.size === 0) this.subscribersByKey.delete(key);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
notify(key, next, prev) {
|
|
42
|
+
const subscribers = this.subscribersByKey.get(key);
|
|
43
|
+
if (!subscribers) return;
|
|
44
|
+
const copy = Array.from(subscribers.values());
|
|
45
|
+
for (const subscriber of copy) {
|
|
46
|
+
try {
|
|
47
|
+
subscriber(next, prev);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error(`Error invoking subscriber:`, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var store = new PubSubStore();
|
|
55
|
+
var stateKey = 0;
|
|
56
|
+
function sharedState(defaultValue, options) {
|
|
57
|
+
const { localStorageKey, indexedDbKey, persistExpiryMs, lazyLoad } = options ?? {};
|
|
58
|
+
const key = String(stateKey++);
|
|
59
|
+
let persistenceAdapter;
|
|
60
|
+
const expiryMs = persistExpiryMs ?? SharedStateConfig.defaultPersistExpiryMs;
|
|
61
|
+
if (localStorageKey) {
|
|
62
|
+
persistenceAdapter = new LocalStorageAdapter(localStorageKey, expiryMs);
|
|
63
|
+
} else if (indexedDbKey) {
|
|
64
|
+
persistenceAdapter = new IndexedDbAdapter(indexedDbKey, expiryMs);
|
|
65
|
+
}
|
|
66
|
+
function subscribe(subscriber) {
|
|
67
|
+
return store.subscribe(key, () => subscriber());
|
|
68
|
+
}
|
|
69
|
+
function getSnapshot() {
|
|
70
|
+
return store.get(key);
|
|
71
|
+
}
|
|
72
|
+
function setValue(next) {
|
|
73
|
+
if (typeof next === "function") {
|
|
74
|
+
const prev = store.get(key);
|
|
75
|
+
next = next(prev);
|
|
76
|
+
}
|
|
77
|
+
store.set(key, next);
|
|
78
|
+
void persistenceAdapter?.save?.(next);
|
|
79
|
+
}
|
|
80
|
+
const state = {
|
|
81
|
+
subscribe,
|
|
82
|
+
getSnapshot,
|
|
83
|
+
// initial default handling
|
|
84
|
+
initDefaultValueOnce: async () => {
|
|
85
|
+
if (state.initDone) return;
|
|
86
|
+
state.initDone = true;
|
|
87
|
+
const v = await persistenceAdapter?.load?.();
|
|
88
|
+
store.set(key, v !== void 0 ? v : defaultValue);
|
|
89
|
+
},
|
|
90
|
+
initDone: false,
|
|
91
|
+
setValue
|
|
92
|
+
};
|
|
93
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
94
|
+
if (!lazy) {
|
|
95
|
+
void state.initDefaultValueOnce();
|
|
96
|
+
}
|
|
97
|
+
return state;
|
|
98
|
+
}
|
|
99
|
+
function useSharedState(state) {
|
|
100
|
+
state.initDefaultValueOnce();
|
|
101
|
+
const value = useSyncExternalStore(state.subscribe, state.getSnapshot);
|
|
102
|
+
return [value, state.setValue];
|
|
103
|
+
}
|
|
104
|
+
function useSharedStateValue(state) {
|
|
105
|
+
return useSharedState(state)[0];
|
|
106
|
+
}
|
|
107
|
+
function useSharedStateSetter(state) {
|
|
108
|
+
return useSharedState(state)[1];
|
|
109
|
+
}
|
|
110
|
+
var LocalStorageAdapter = class {
|
|
111
|
+
constructor(key, expiryMs) {
|
|
112
|
+
this.key = key;
|
|
113
|
+
this.expiryMs = expiryMs;
|
|
114
|
+
}
|
|
115
|
+
save(value) {
|
|
116
|
+
if (typeof window === "undefined") return;
|
|
117
|
+
window.localStorage.setItem(
|
|
118
|
+
this.key,
|
|
119
|
+
JSON.stringify({
|
|
120
|
+
value,
|
|
121
|
+
expiresAt: Date.now() + this.expiryMs
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
load() {
|
|
126
|
+
if (typeof window === "undefined") return void 0;
|
|
127
|
+
const raw = window.localStorage.getItem(this.key);
|
|
128
|
+
if (!raw) return void 0;
|
|
129
|
+
const box = JSON.parse(raw);
|
|
130
|
+
if (!box || !box?.expiresAt || Date.now() >= box.expiresAt) {
|
|
131
|
+
window.localStorage.removeItem(this.key);
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
return box.value;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
var IndexedDbAdapter = class {
|
|
138
|
+
constructor(key, expiryMs) {
|
|
139
|
+
this.key = key;
|
|
140
|
+
this.expiryMs = expiryMs;
|
|
141
|
+
}
|
|
142
|
+
async save(value) {
|
|
143
|
+
if (typeof window === "undefined") return;
|
|
144
|
+
await kvStore.set(this.key, JSON.stringify(value), this.expiryMs);
|
|
145
|
+
}
|
|
146
|
+
async load() {
|
|
147
|
+
if (typeof window === "undefined") return void 0;
|
|
148
|
+
const raw = await kvStore.get(this.key);
|
|
149
|
+
if (raw === void 0) return void 0;
|
|
150
|
+
return JSON.parse(raw);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
export {
|
|
154
|
+
SharedStateConfig,
|
|
155
|
+
sharedState,
|
|
156
|
+
useSharedState,
|
|
157
|
+
useSharedStateSetter,
|
|
158
|
+
useSharedStateValue
|
|
159
|
+
};
|
package/src/fetcher.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import wretch from "wretch";
|
|
2
|
+
import { retry } from "wretch/middlewares";
|
|
3
|
+
|
|
4
|
+
/** Name this as "fetcher" to make it easier to understand. */
|
|
5
|
+
export const fetcher = wretch();
|
|
6
|
+
|
|
7
|
+
/** Fetcher with default short retries. */
|
|
8
|
+
export const fetcherWithRetries = wretch().middlewares([
|
|
9
|
+
retry({
|
|
10
|
+
maxAttempts: 3,
|
|
11
|
+
delayTimer: 1000,
|
|
12
|
+
retryOnNetworkError: true,
|
|
13
|
+
}),
|
|
14
|
+
]);
|
package/src/index.ts
ADDED