use-everywhere 0.6.0 → 0.8.0
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/dist/{chunk-YOXUZYKA.js → chunk-75TICQ7E.js} +75 -14
- package/dist/devtools/index.cjs +14 -5
- package/dist/devtools/index.js +1 -1
- package/dist/index.cjs +270 -66
- package/dist/index.d.cts +242 -10
- package/dist/index.d.ts +242 -10
- package/dist/index.js +171 -27
- package/package.json +23 -11
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_NAME,
|
|
4
|
+
configureChannel,
|
|
4
5
|
configureStore,
|
|
5
6
|
getChannel,
|
|
6
7
|
getLeader,
|
|
8
|
+
getReducer,
|
|
7
9
|
getSharedStore,
|
|
8
10
|
getStore,
|
|
9
11
|
useClientId,
|
|
10
12
|
usePeers,
|
|
13
|
+
usePresenceMetadata,
|
|
11
14
|
warnOnInitialMismatch
|
|
12
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-75TICQ7E.js";
|
|
13
16
|
|
|
14
17
|
// src/use-shared-state.ts
|
|
15
18
|
import { useCallback, useSyncExternalStore } from "react";
|
|
@@ -17,7 +20,9 @@ function useSharedState(key, initial, options) {
|
|
|
17
20
|
const storeName = options?.store ?? DEFAULT_NAME;
|
|
18
21
|
const store = getStore(storeName, options?.scope ?? "everywhere");
|
|
19
22
|
store.registerKey(key, initial);
|
|
20
|
-
|
|
23
|
+
if (process.env.NODE_ENV !== "production") {
|
|
24
|
+
warnOnInitialMismatch(storeName, key, initial);
|
|
25
|
+
}
|
|
21
26
|
const value = useSyncExternalStore(
|
|
22
27
|
useCallback((onChange) => store.subscribeKey(key, onChange), [store, key]),
|
|
23
28
|
() => store.getSnapshot()[key],
|
|
@@ -30,27 +35,98 @@ function useSharedState(key, initial, options) {
|
|
|
30
35
|
return [value, setValue];
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
// src/use-shared-reducer.ts
|
|
39
|
+
import { useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
40
|
+
function useSharedReducer(reducer, initial, options) {
|
|
41
|
+
const engine = getReducer(
|
|
42
|
+
options?.name ?? DEFAULT_NAME,
|
|
43
|
+
options?.key ?? "default",
|
|
44
|
+
reducer,
|
|
45
|
+
initial
|
|
46
|
+
);
|
|
47
|
+
const value = useSyncExternalStore2(
|
|
48
|
+
useCallback2((onChange) => engine.subscribe(onChange), [engine]),
|
|
49
|
+
() => engine.getSnapshot(),
|
|
50
|
+
() => initial
|
|
51
|
+
);
|
|
52
|
+
const dispatch = useCallback2((action) => engine.dispatch(action), [engine]);
|
|
53
|
+
return [value, dispatch];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/use-shared-selector.ts
|
|
57
|
+
import { useCallback as useCallback3, useRef, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
58
|
+
function useSharedStore(selector, options) {
|
|
59
|
+
const store = getStore(options?.store ?? DEFAULT_NAME, options?.scope ?? "everywhere");
|
|
60
|
+
const equal = options?.equal ?? Object.is;
|
|
61
|
+
const selectorRef = useRef(selector);
|
|
62
|
+
selectorRef.current = selector;
|
|
63
|
+
const equalRef = useRef(equal);
|
|
64
|
+
equalRef.current = equal;
|
|
65
|
+
const cache = useRef(null);
|
|
66
|
+
const getSelection = useCallback3(() => {
|
|
67
|
+
const source = store.getSnapshot();
|
|
68
|
+
const select = selectorRef.current;
|
|
69
|
+
const previous = cache.current;
|
|
70
|
+
if (previous && previous.source === source && previous.select === select) return previous.value;
|
|
71
|
+
const next = select(source);
|
|
72
|
+
const value = previous && equalRef.current(previous.value, next) ? previous.value : next;
|
|
73
|
+
cache.current = { source, select, value };
|
|
74
|
+
return value;
|
|
75
|
+
}, [store]);
|
|
76
|
+
return useSyncExternalStore3(
|
|
77
|
+
useCallback3((onChange) => store.subscribe(onChange), [store]),
|
|
78
|
+
getSelection,
|
|
79
|
+
getSelection
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function shallowEqual(a, b) {
|
|
83
|
+
if (Object.is(a, b)) return true;
|
|
84
|
+
if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
|
|
85
|
+
const left = a;
|
|
86
|
+
const right = b;
|
|
87
|
+
const keys = Object.keys(left);
|
|
88
|
+
if (keys.length !== Object.keys(right).length) return false;
|
|
89
|
+
return keys.every((key) => Object.is(left[key], right[key]));
|
|
90
|
+
}
|
|
91
|
+
|
|
33
92
|
// src/use-message.ts
|
|
34
|
-
import { useEffect, useRef } from "react";
|
|
93
|
+
import { useEffect, useRef as useRef2 } from "react";
|
|
35
94
|
function useChannel(name) {
|
|
36
95
|
return getChannel(name);
|
|
37
96
|
}
|
|
38
|
-
function useMessage(channel, type, handler) {
|
|
39
|
-
const handlerRef =
|
|
97
|
+
function useMessage(channel, type, handler, options) {
|
|
98
|
+
const handlerRef = useRef2(handler);
|
|
40
99
|
useEffect(() => {
|
|
41
100
|
handlerRef.current = handler;
|
|
42
101
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
102
|
+
const enabled = options?.enabled ?? true;
|
|
103
|
+
const once = options?.once ?? false;
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (!enabled) return;
|
|
106
|
+
return channel.on(type, (payload, meta) => handlerRef.current(payload, meta), { once });
|
|
107
|
+
}, [channel, type, enabled, once]);
|
|
108
|
+
}
|
|
109
|
+
function useAnswer(channel, type, responder, options) {
|
|
110
|
+
const responderRef = useRef2(responder);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
responderRef.current = responder;
|
|
113
|
+
});
|
|
114
|
+
const enabled = options?.enabled ?? true;
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!enabled) return;
|
|
117
|
+
return channel.answer(type, (payload, meta) => responderRef.current(payload, meta));
|
|
118
|
+
}, [channel, type, enabled]);
|
|
47
119
|
}
|
|
48
120
|
function useSend(channel) {
|
|
49
121
|
return channel.post;
|
|
50
122
|
}
|
|
123
|
+
function useAsk(channel) {
|
|
124
|
+
return channel.ask;
|
|
125
|
+
}
|
|
51
126
|
|
|
52
127
|
// src/define-channel.ts
|
|
53
|
-
function defineChannel(name) {
|
|
128
|
+
function defineChannel(name, options) {
|
|
129
|
+
if (options) configureChannel(name, options);
|
|
54
130
|
const useBoundSend = () => useSend(useChannel(name));
|
|
55
131
|
const useBoundMessage = (type, handler) => useMessage(useChannel(name), type, handler);
|
|
56
132
|
return {
|
|
@@ -68,7 +144,10 @@ function defineStore(name, options = {}) {
|
|
|
68
144
|
persist: {
|
|
69
145
|
adapter: options.persist,
|
|
70
146
|
...options.persistKeys ? { keys: options.persistKeys } : {},
|
|
71
|
-
...options.persistDebounceMs === void 0 ? {} : { debounceMs: options.persistDebounceMs }
|
|
147
|
+
...options.persistDebounceMs === void 0 ? {} : { debounceMs: options.persistDebounceMs },
|
|
148
|
+
...options.persistVersion === void 0 ? {} : { version: options.persistVersion },
|
|
149
|
+
...options.migrate ? { migrate: options.migrate } : {},
|
|
150
|
+
...options.onRestoreError ? { onRestoreError: options.onRestoreError } : {}
|
|
72
151
|
}
|
|
73
152
|
});
|
|
74
153
|
}
|
|
@@ -78,18 +157,35 @@ function defineStore(name, options = {}) {
|
|
|
78
157
|
};
|
|
79
158
|
}
|
|
80
159
|
|
|
160
|
+
// src/use-hydrated.ts
|
|
161
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
162
|
+
function useHydrated(options) {
|
|
163
|
+
const store = getStore(options?.store ?? DEFAULT_NAME, options?.scope ?? "everywhere");
|
|
164
|
+
const [ready, setReady] = useState(false);
|
|
165
|
+
useEffect2(() => {
|
|
166
|
+
let cancelled = false;
|
|
167
|
+
void store.hydrated.then(() => {
|
|
168
|
+
if (!cancelled) setReady(true);
|
|
169
|
+
});
|
|
170
|
+
return () => {
|
|
171
|
+
cancelled = true;
|
|
172
|
+
};
|
|
173
|
+
}, [store]);
|
|
174
|
+
return ready;
|
|
175
|
+
}
|
|
176
|
+
|
|
81
177
|
// src/use-leader.ts
|
|
82
|
-
import { useCallback as
|
|
178
|
+
import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
83
179
|
var NO_LEADER = Object.freeze({ leaderId: null, isLeader: false });
|
|
84
180
|
function useLeader(options) {
|
|
85
181
|
const leader = getLeader(options?.name ?? DEFAULT_NAME, options);
|
|
86
182
|
const eligible = options?.eligible;
|
|
87
|
-
|
|
183
|
+
useEffect3(() => {
|
|
88
184
|
if (eligible === void 0) return;
|
|
89
185
|
leader.setEligible(eligible);
|
|
90
186
|
}, [leader, eligible]);
|
|
91
|
-
return
|
|
92
|
-
|
|
187
|
+
return useSyncExternalStore4(
|
|
188
|
+
useCallback4((onChange) => leader.subscribe(onChange), [leader]),
|
|
93
189
|
() => leader.getSnapshot(),
|
|
94
190
|
() => NO_LEADER
|
|
95
191
|
);
|
|
@@ -99,18 +195,42 @@ function useIsLeader(options) {
|
|
|
99
195
|
}
|
|
100
196
|
function useLeaderEffect(effect, options) {
|
|
101
197
|
const { isLeader } = useLeader(options);
|
|
102
|
-
const effectRef =
|
|
103
|
-
|
|
198
|
+
const effectRef = useRef3(effect);
|
|
199
|
+
useEffect3(() => {
|
|
104
200
|
effectRef.current = effect;
|
|
105
201
|
});
|
|
106
|
-
|
|
202
|
+
useEffect3(() => {
|
|
107
203
|
if (!isLeader) return;
|
|
108
204
|
return effectRef.current();
|
|
109
205
|
}, [isLeader]);
|
|
110
206
|
}
|
|
111
207
|
|
|
208
|
+
// src/namespace.ts
|
|
209
|
+
import {
|
|
210
|
+
createNamespace as createCoreNamespace
|
|
211
|
+
} from "@use-everywhere/core";
|
|
212
|
+
function createNamespace(namespace) {
|
|
213
|
+
const core = createCoreNamespace(namespace);
|
|
214
|
+
const { busName } = core;
|
|
215
|
+
return {
|
|
216
|
+
...core,
|
|
217
|
+
defineStore: (name, options) => defineStore(busName(name), options),
|
|
218
|
+
defineChannel: (name, options) => defineChannel(busName(name), options),
|
|
219
|
+
getSharedStore: (name, scope) => getSharedStore(busName(name), scope),
|
|
220
|
+
useSharedState: (key, initial, options) => useSharedState(key, initial, { ...options, store: busName(options?.store) }),
|
|
221
|
+
useSharedStore: (selector, options) => useSharedStore(selector, { ...options, store: busName(options?.store) }),
|
|
222
|
+
usePeers: (options) => usePeers({ ...options, name: busName(options?.name) }),
|
|
223
|
+
usePresenceMetadata: (metadata, options) => usePresenceMetadata(metadata, { ...options, name: busName(options?.name) }),
|
|
224
|
+
useHydrated: (options) => useHydrated({ ...options, store: busName(options?.store) }),
|
|
225
|
+
useClientId: (options) => useClientId({ name: busName(options?.name) }),
|
|
226
|
+
useLeader: (options) => useLeader({ ...options, name: busName(options?.name) }),
|
|
227
|
+
useIsLeader: (options) => useIsLeader({ ...options, name: busName(options?.name) }),
|
|
228
|
+
useLeaderEffect: (effect, options) => useLeaderEffect(effect, { ...options, name: busName(options?.name) })
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
112
232
|
// src/use-opened-window.ts
|
|
113
|
-
import { useCallback as
|
|
233
|
+
import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState2 } from "react";
|
|
114
234
|
import { WindowClosedError } from "@use-everywhere/core";
|
|
115
235
|
var IDLE = Object.freeze({
|
|
116
236
|
status: "idle",
|
|
@@ -118,13 +238,13 @@ var IDLE = Object.freeze({
|
|
|
118
238
|
error: void 0
|
|
119
239
|
});
|
|
120
240
|
function useOpenedWindow(factory) {
|
|
121
|
-
const [state, setState] =
|
|
122
|
-
const current =
|
|
123
|
-
const factoryRef =
|
|
124
|
-
|
|
241
|
+
const [state, setState] = useState2(IDLE);
|
|
242
|
+
const current = useRef4(null);
|
|
243
|
+
const factoryRef = useRef4(factory);
|
|
244
|
+
useEffect4(() => {
|
|
125
245
|
factoryRef.current = factory;
|
|
126
246
|
});
|
|
127
|
-
const open =
|
|
247
|
+
const open = useCallback5(() => {
|
|
128
248
|
current.current?.close();
|
|
129
249
|
let opened;
|
|
130
250
|
try {
|
|
@@ -159,10 +279,10 @@ function useOpenedWindow(factory) {
|
|
|
159
279
|
}
|
|
160
280
|
);
|
|
161
281
|
}, []);
|
|
162
|
-
const post =
|
|
282
|
+
const post = useCallback5((type, payload) => {
|
|
163
283
|
current.current?.post(type, payload);
|
|
164
284
|
}, []);
|
|
165
|
-
const close =
|
|
285
|
+
const close = useCallback5(() => current.current?.close(), []);
|
|
166
286
|
return { ...state, open, post, close };
|
|
167
287
|
}
|
|
168
288
|
|
|
@@ -170,11 +290,14 @@ function useOpenedWindow(factory) {
|
|
|
170
290
|
import {
|
|
171
291
|
createChannel,
|
|
172
292
|
createSharedStore,
|
|
293
|
+
createSharedReducer,
|
|
173
294
|
createPresence,
|
|
174
295
|
createLeader,
|
|
175
296
|
webStorageAdapter,
|
|
176
297
|
localStorageAdapter,
|
|
177
298
|
sessionStorageAdapter,
|
|
299
|
+
indexedDbAdapter,
|
|
300
|
+
jsonSerializer,
|
|
178
301
|
openWindow,
|
|
179
302
|
connectToOpener,
|
|
180
303
|
CID_PARAM,
|
|
@@ -184,10 +307,15 @@ import {
|
|
|
184
307
|
observeBus,
|
|
185
308
|
enableDebug,
|
|
186
309
|
getBusNames,
|
|
310
|
+
getTransportKind,
|
|
311
|
+
getWireSkew,
|
|
312
|
+
WIRE_VERSION,
|
|
187
313
|
BroadcastChannelTransport,
|
|
188
314
|
NoopTransport,
|
|
315
|
+
StorageTransport,
|
|
189
316
|
defaultTransport,
|
|
190
|
-
isBroadcastChannelAvailable
|
|
317
|
+
isBroadcastChannelAvailable,
|
|
318
|
+
isStorageEventAvailable
|
|
191
319
|
} from "@use-everywhere/core";
|
|
192
320
|
export {
|
|
193
321
|
BroadcastChannelTransport,
|
|
@@ -195,11 +323,15 @@ export {
|
|
|
195
323
|
DEFAULT_NAME,
|
|
196
324
|
HandshakeTimeoutError,
|
|
197
325
|
NoopTransport,
|
|
326
|
+
StorageTransport,
|
|
327
|
+
WIRE_VERSION,
|
|
198
328
|
WindowClosedError2 as WindowClosedError,
|
|
199
329
|
connectToOpener,
|
|
200
330
|
createChannel,
|
|
201
331
|
createLeader,
|
|
332
|
+
createNamespace,
|
|
202
333
|
createPresence,
|
|
334
|
+
createSharedReducer,
|
|
203
335
|
createSharedStore,
|
|
204
336
|
defaultTransport,
|
|
205
337
|
defineChannel,
|
|
@@ -208,21 +340,33 @@ export {
|
|
|
208
340
|
getBusNames,
|
|
209
341
|
getLeader,
|
|
210
342
|
getSharedStore,
|
|
343
|
+
getTransportKind,
|
|
344
|
+
getWireSkew,
|
|
345
|
+
indexedDbAdapter,
|
|
211
346
|
isBroadcastChannelAvailable,
|
|
347
|
+
isStorageEventAvailable,
|
|
348
|
+
jsonSerializer,
|
|
212
349
|
localStorageAdapter,
|
|
213
350
|
newer,
|
|
214
351
|
observeBus,
|
|
215
352
|
openWindow,
|
|
216
353
|
sessionStorageAdapter,
|
|
354
|
+
shallowEqual,
|
|
355
|
+
useAnswer,
|
|
356
|
+
useAsk,
|
|
217
357
|
useChannel,
|
|
218
358
|
useClientId,
|
|
359
|
+
useHydrated,
|
|
219
360
|
useIsLeader,
|
|
220
361
|
useLeader,
|
|
221
362
|
useLeaderEffect,
|
|
222
363
|
useMessage,
|
|
223
364
|
useOpenedWindow,
|
|
224
365
|
usePeers,
|
|
366
|
+
usePresenceMetadata,
|
|
225
367
|
useSend,
|
|
368
|
+
useSharedReducer,
|
|
226
369
|
useSharedState,
|
|
370
|
+
useSharedStore,
|
|
227
371
|
webStorageAdapter
|
|
228
372
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "use-everywhere",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "React hooks for state and messages shared across tabs, windows, and workers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Jonatan Kruszewski <jonakrusze@gmail.com>",
|
|
@@ -69,59 +69,71 @@
|
|
|
69
69
|
"name": "everything (import *)",
|
|
70
70
|
"path": "dist/index.js",
|
|
71
71
|
"import": "*",
|
|
72
|
-
"limit": "
|
|
72
|
+
"limit": "10.89 kB"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"name": "useSharedState",
|
|
76
76
|
"path": "dist/index.js",
|
|
77
77
|
"import": "{ useSharedState }",
|
|
78
|
-
"limit": "
|
|
78
|
+
"limit": "4.24 kB"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "useSharedStore (selector)",
|
|
82
|
+
"path": "dist/index.js",
|
|
83
|
+
"import": "{ useSharedStore, shallowEqual }",
|
|
84
|
+
"limit": "4.40 kB"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "useSharedReducer",
|
|
88
|
+
"path": "dist/index.js",
|
|
89
|
+
"import": "{ useSharedReducer }",
|
|
90
|
+
"limit": "4.21 kB"
|
|
79
91
|
},
|
|
80
92
|
{
|
|
81
93
|
"name": "useChannel + useMessage + useSend",
|
|
82
94
|
"path": "dist/index.js",
|
|
83
95
|
"import": "{ useChannel, useMessage, useSend }",
|
|
84
|
-
"limit": "
|
|
96
|
+
"limit": "3.07 kB"
|
|
85
97
|
},
|
|
86
98
|
{
|
|
87
99
|
"name": "usePeers + useClientId",
|
|
88
100
|
"path": "dist/index.js",
|
|
89
101
|
"import": "{ usePeers, useClientId }",
|
|
90
|
-
"limit": "
|
|
102
|
+
"limit": "2.77 kB"
|
|
91
103
|
},
|
|
92
104
|
{
|
|
93
105
|
"name": "useOpenedWindow + openWindow",
|
|
94
106
|
"path": "dist/index.js",
|
|
95
107
|
"import": "{ useOpenedWindow, openWindow }",
|
|
96
|
-
"limit": "1.
|
|
108
|
+
"limit": "1.47 kB"
|
|
97
109
|
},
|
|
98
110
|
{
|
|
99
111
|
"name": "useLeader + useLeaderEffect",
|
|
100
112
|
"path": "dist/index.js",
|
|
101
113
|
"import": "{ useLeader, useLeaderEffect }",
|
|
102
|
-
"limit": "
|
|
114
|
+
"limit": "3.65 kB"
|
|
103
115
|
},
|
|
104
116
|
{
|
|
105
117
|
"name": "defineStore (persisted)",
|
|
106
118
|
"path": "dist/index.js",
|
|
107
119
|
"import": "{ defineStore, localStorageAdapter }",
|
|
108
|
-
"limit": "
|
|
120
|
+
"limit": "4.67 kB"
|
|
109
121
|
},
|
|
110
122
|
{
|
|
111
123
|
"name": "Inspector (devtools subpath)",
|
|
112
124
|
"path": "dist/devtools/index.js",
|
|
113
125
|
"import": "{ Inspector }",
|
|
114
|
-
"limit": "
|
|
126
|
+
"limit": "6.32 kB"
|
|
115
127
|
},
|
|
116
128
|
{
|
|
117
129
|
"name": "MemoryHub (testing subpath)",
|
|
118
130
|
"path": "dist/testing.js",
|
|
119
131
|
"import": "{ MemoryHub }",
|
|
120
|
-
"limit": "
|
|
132
|
+
"limit": "264 B"
|
|
121
133
|
}
|
|
122
134
|
],
|
|
123
135
|
"dependencies": {
|
|
124
|
-
"@use-everywhere/core": "0.
|
|
136
|
+
"@use-everywhere/core": "0.8.0"
|
|
125
137
|
},
|
|
126
138
|
"peerDependencies": {
|
|
127
139
|
"react": ">=18"
|