use-everywhere 0.7.0 → 0.9.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/README.md +2 -2
- package/dist/{chunk-LNJEF4DI.js → chunk-R7PBPLF7.js} +83 -18
- package/dist/devtools/index.cjs +153 -9
- package/dist/devtools/index.js +140 -5
- package/dist/index.cjs +275 -73
- package/dist/index.d.cts +242 -10
- package/dist/index.d.ts +242 -10
- package/dist/index.js +164 -26
- package/package.json +23 -11
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SharedStore, LeaderOptions, Leader, MessageMap, Channel, MessageMeta, PersistAdapter, Peer, LeaderSnapshot, OpenedWindow, WindowClosedError } from '@use-everywhere/core';
|
|
2
|
-
export { BroadcastChannelTransport, BusEvent, BusObserver, BusWire, CID_PARAM, Channel, CommonOptions, ConnectToOpenerOptions, DEFAULT_NAME, DebugOptions, HandshakeTimeoutError, Leader, LeaderOptions, LeaderSnapshot, LeaderStrategy, MessageEventLike, MessageMap, MessageMeta, NoopTransport, OpenWindowOptions, OpenedWindow, OpenerConnection, Peer, PeerKind, PersistAdapter, PersistOptions, Persisted, Presence, PresenceOptions, SharedStore, SharedStoreOptions, StorageLike, StorageTransport, Transport, TransportKind, Version, WebStorageAdapterOptions, WindowClosedError, WindowEventTarget, WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, isBroadcastChannelAvailable, isStorageEventAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter } from '@use-everywhere/core';
|
|
1
|
+
import { SharedStore, LeaderOptions, Leader, MessageMap, ReplyMap, Channel, MessageMeta, ChannelOptions, PersistAdapter, RestoreError, Peer, LeaderSnapshot, Namespace, OpenedWindow, WindowClosedError } from '@use-everywhere/core';
|
|
2
|
+
export { AskOptions, BroadcastChannelTransport, BusEvent, BusObserver, BusWire, CID_PARAM, Channel, ChannelOptions, CommonOptions, ConnectToOpenerOptions, DEFAULT_NAME, DebugOptions, HandshakeTimeoutError, IndexedDbAdapterOptions, InvalidPayload, Leader, LeaderOptions, LeaderSnapshot, LeaderStrategy, MessageEventLike, MessageMap, MessageMeta, Namespace, NoopTransport, OnInvalid, OnOptions, OpenWindowOptions, OpenedWindow, OpenerConnection, Peer, PeerKind, PersistAdapter, PersistOptions, Persisted, PostOptions, Presence, PresenceOptions, ReplyMap, RestoreError, SchemaMap, SchemaOptions, Serializer, SharedReducer, SharedReducerOptions, SharedStore, SharedStoreOptions, StandardSchemaV1, StorageLike, StorageTransport, Transport, TransportKind, Version, WIRE_VERSION, WebStorageAdapterOptions, WindowClosedError, WindowEventTarget, WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter } from '@use-everywhere/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* How far a shared value travels:
|
|
@@ -29,6 +29,92 @@ interface UseSharedStateOptions {
|
|
|
29
29
|
*/
|
|
30
30
|
declare function useSharedState<T>(key: string, initial: T, options?: UseSharedStateOptions): [T, (next: T | ((prev: T) => T)) => void];
|
|
31
31
|
|
|
32
|
+
interface UseSharedReducerOptions {
|
|
33
|
+
/** Bus name. Default 'use-everywhere'. */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** Which reducer this is, when several share a bus. Default 'default'. */
|
|
36
|
+
key?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Like `useReducer`, but every tab, window, and worker on this origin applies
|
|
40
|
+
* the same actions in the same order.
|
|
41
|
+
*
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const [count, dispatch] = useSharedReducer((n, action) => n + action.by, 0);
|
|
44
|
+
* <button onClick={() => dispatch({ by: 1 })}>{count}</button>;
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Reach for this instead of `useSharedState` whenever a write is *relative to
|
|
48
|
+
* what is already there* — a counter, a total, a list you append to. Shared
|
|
49
|
+
* state converges last-writer-wins on the value, so two tabs incrementing at
|
|
50
|
+
* once both write the same result and one increment vanishes. A reducer sends
|
|
51
|
+
* the action rather than the result, and two increments are two actions.
|
|
52
|
+
*
|
|
53
|
+
* For a plain register — a theme, a selection, a draft — `useSharedState` is
|
|
54
|
+
* still the right tool and the cheaper one.
|
|
55
|
+
*
|
|
56
|
+
* ## The rules
|
|
57
|
+
*
|
|
58
|
+
* **Actions must survive the wire.** They are structured-cloned to every peer,
|
|
59
|
+
* so no functions, no class instances, no DOM nodes.
|
|
60
|
+
*
|
|
61
|
+
* **The reducer must be pure**, and it must be the *same* reducer everywhere.
|
|
62
|
+
* Every client folds the same actions itself; a client whose fold differs gets
|
|
63
|
+
* a different answer, and nothing can detect that. The first caller's function
|
|
64
|
+
* is the one used for the life of the page — a re-render passes a new identity
|
|
65
|
+
* every time, and swapping the fold under a history already applied is exactly
|
|
66
|
+
* the divergence this exists to prevent.
|
|
67
|
+
*
|
|
68
|
+
* **Ordering needs a leader**, which this shares with `useLeader` on the same
|
|
69
|
+
* bus rather than electing a second one. Before a seat is filled — the first
|
|
70
|
+
* moments of the first tab — dispatches are held locally and ordered once it
|
|
71
|
+
* is. They are never lost, only pending.
|
|
72
|
+
*/
|
|
73
|
+
declare function useSharedReducer<S, A>(reducer: (state: S, action: A) => S, initial: S, options?: UseSharedReducerOptions): [S, (action: A) => void];
|
|
74
|
+
|
|
75
|
+
interface UseSharedSelectorOptions extends UseSharedStateOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Whether two selected values count as the same. Default `Object.is`.
|
|
78
|
+
*
|
|
79
|
+
* A selector returning an object or array builds a new one every time it
|
|
80
|
+
* runs, so `Object.is` never matches and the component re-renders on every
|
|
81
|
+
* write to the store. Pass a shallow comparison for those.
|
|
82
|
+
*/
|
|
83
|
+
equal?: (a: unknown, b: unknown) => boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read a derived value from a shared store, re-rendering only when it changes.
|
|
87
|
+
*
|
|
88
|
+
* `useSharedState` subscribes to one key, which is the right shape for reading
|
|
89
|
+
* one thing. Reading *across* keys meant either one hook per key or a
|
|
90
|
+
* subscription to the whole store, and the latter re-renders on every write to
|
|
91
|
+
* anything in it.
|
|
92
|
+
*
|
|
93
|
+
* ```tsx
|
|
94
|
+
* const total = useSharedStore<Cart, number>(
|
|
95
|
+
* (cart) => cart.items.length + cart.saved.length,
|
|
96
|
+
* );
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* The selector runs on every store change; the component only re-renders when
|
|
100
|
+
* its result changes by `equal`.
|
|
101
|
+
*
|
|
102
|
+
* ## It reads, it does not declare
|
|
103
|
+
*
|
|
104
|
+
* `useSharedState(key, initial)` registers `key` with a default. A selector
|
|
105
|
+
* does not — it sees whatever is in the store, so a key nothing has registered
|
|
106
|
+
* or written yet is `undefined`. Write selectors that tolerate that, or declare
|
|
107
|
+
* the defaults with `useSharedState` (or a store initial) somewhere that
|
|
108
|
+
* mounts first.
|
|
109
|
+
*
|
|
110
|
+
* The same applies on a server, where the store is inert and empty: the
|
|
111
|
+
* selector runs against `{}`, and its result is what the server-rendered markup
|
|
112
|
+
* shows.
|
|
113
|
+
*/
|
|
114
|
+
declare function useSharedStore<S extends Record<string, unknown>, T>(selector: (state: S) => T, options?: UseSharedSelectorOptions): T;
|
|
115
|
+
/** Shallow equality over an object or array, for selectors that build one. */
|
|
116
|
+
declare function shallowEqual(a: unknown, b: unknown): boolean;
|
|
117
|
+
|
|
32
118
|
/** Registry stores hold arbitrary keys — shape is decided by the hooks using them. */
|
|
33
119
|
type AnyStore = SharedStore<Record<string, unknown>>;
|
|
34
120
|
|
|
@@ -43,14 +129,41 @@ declare function getSharedStore(name?: string, scope?: ShareScope): AnyStore;
|
|
|
43
129
|
declare function getLeader(name: string, options?: LeaderOptions): Leader;
|
|
44
130
|
|
|
45
131
|
/** Get the page-wide typed channel for `name` (one instance per name). */
|
|
46
|
-
declare function useChannel<M extends MessageMap>(name: string): Channel<M>;
|
|
132
|
+
declare function useChannel<M extends MessageMap, R extends ReplyMap<M> = Record<never, never>>(name: string): Channel<M, R>;
|
|
133
|
+
interface UseMessageOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Subscribe at all. Default true.
|
|
136
|
+
*
|
|
137
|
+
* `false` unsubscribes rather than filtering inside the handler, so a
|
|
138
|
+
* component that is not interested costs nothing — and the alternative,
|
|
139
|
+
* calling the hook conditionally, is not allowed.
|
|
140
|
+
*/
|
|
141
|
+
enabled?: boolean;
|
|
142
|
+
/** Unsubscribe after the first message. */
|
|
143
|
+
once?: boolean;
|
|
144
|
+
}
|
|
47
145
|
/**
|
|
48
146
|
* Subscribe to one message type. The handler is kept fresh without
|
|
49
147
|
* resubscribing, so it may close over render state.
|
|
50
148
|
*/
|
|
51
|
-
declare function useMessage<M extends MessageMap, K extends keyof M & string>(channel: Channel<M>, type: K, handler: (payload: M[K], meta: MessageMeta) => void): void;
|
|
149
|
+
declare function useMessage<M extends MessageMap, R extends ReplyMap<M>, K extends keyof M & string>(channel: Channel<M, R>, type: K, handler: (payload: M[K], meta: MessageMeta) => void, options?: UseMessageOptions): void;
|
|
150
|
+
/**
|
|
151
|
+
* Answer `ask`s of one type for as long as this component is mounted.
|
|
152
|
+
*
|
|
153
|
+
* A hook rather than a bare `channel.answer` call because a responder is a
|
|
154
|
+
* subscription: registering one in a render would leave the last unmounted
|
|
155
|
+
* component answering for the page.
|
|
156
|
+
*
|
|
157
|
+
* One responder per type per channel — registering a second replaces the first,
|
|
158
|
+
* which is the same first-wins-then-replace rule `answer` has in core.
|
|
159
|
+
*/
|
|
160
|
+
declare function useAnswer<M extends MessageMap, R extends ReplyMap<M>, K extends keyof M & keyof R & string>(channel: Channel<M, R>, type: K, responder: (payload: M[K], meta: MessageMeta) => R[K], options?: {
|
|
161
|
+
enabled?: boolean;
|
|
162
|
+
}): void;
|
|
52
163
|
/** The channel's post function (stable identity per channel). */
|
|
53
|
-
declare function useSend<M extends MessageMap
|
|
164
|
+
declare function useSend<M extends MessageMap, R extends ReplyMap<M>>(channel: Channel<M, R>): Channel<M, R>['post'];
|
|
165
|
+
/** The channel's ask function (stable identity per channel). */
|
|
166
|
+
declare function useAsk<M extends MessageMap, R extends ReplyMap<M>>(channel: Channel<M, R>): Channel<M, R>['ask'];
|
|
54
167
|
|
|
55
168
|
/** A channel bound to a name and message map: typed hooks with no per-call generics. */
|
|
56
169
|
interface ChannelHooks<M extends MessageMap> {
|
|
@@ -70,8 +183,11 @@ interface ChannelHooks<M extends MessageMap> {
|
|
|
70
183
|
* typed hooks back. Sugar over useChannel/useMessage/useSend — the same
|
|
71
184
|
* page-wide channel singleton is shared, so mixing bound and standalone
|
|
72
185
|
* hooks for one name is safe.
|
|
186
|
+
*
|
|
187
|
+
* Options are registered here and applied when the channel is first needed, so
|
|
188
|
+
* declaring a `schema` at module scope still constructs nothing on import.
|
|
73
189
|
*/
|
|
74
|
-
declare function defineChannel<M extends MessageMap>(name: string): ChannelHooks<M>;
|
|
190
|
+
declare function defineChannel<M extends MessageMap>(name: string, options?: ChannelOptions<M>): ChannelHooks<M>;
|
|
75
191
|
|
|
76
192
|
interface DefineStoreOptions {
|
|
77
193
|
/** Restore this store from disk on first use, and write it back as it changes. */
|
|
@@ -80,6 +196,16 @@ interface DefineStoreOptions {
|
|
|
80
196
|
persistKeys?: string[];
|
|
81
197
|
/** Coalesce disk writes for this long. Default 100. */
|
|
82
198
|
persistDebounceMs?: number;
|
|
199
|
+
/**
|
|
200
|
+
* The version of your persisted state's shape. Bump it whenever a key changes
|
|
201
|
+
* meaning or type, and supply `migrate` to carry old data forward. Default 0,
|
|
202
|
+
* which is also what anything written before this existed reads as.
|
|
203
|
+
*/
|
|
204
|
+
persistVersion?: number;
|
|
205
|
+
/** Bring persisted state written at an older `persistVersion` up to the current one. */
|
|
206
|
+
migrate?: (state: Record<string, unknown>, from: number) => Record<string, unknown>;
|
|
207
|
+
/** Called when persisted state is refused instead of restored. Defaults to a development warning. */
|
|
208
|
+
onRestoreError?: (error: RestoreError) => void;
|
|
83
209
|
/** How far this store is shared. Default 'everywhere'. */
|
|
84
210
|
scope?: ShareScope;
|
|
85
211
|
}
|
|
@@ -106,10 +232,25 @@ interface StoreHooks<S extends Record<string, unknown>> {
|
|
|
106
232
|
*/
|
|
107
233
|
declare function defineStore<S extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: DefineStoreOptions): StoreHooks<S>;
|
|
108
234
|
|
|
109
|
-
|
|
110
|
-
|
|
235
|
+
interface UsePeersOptions {
|
|
236
|
+
/** Bus name. Default 'use-everywhere'. */
|
|
111
237
|
name?: string;
|
|
112
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Include this client in the list. Default false.
|
|
240
|
+
*
|
|
241
|
+
* The default answers "who *else* is here", which is what a presence strip
|
|
242
|
+
* asks. Turn it on for an avatar list, where leaving yourself out means every
|
|
243
|
+
* tab renders a different list of the same room.
|
|
244
|
+
*/
|
|
245
|
+
includeSelf?: boolean;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The tabs/windows/workers currently alive on this origin.
|
|
249
|
+
*
|
|
250
|
+
* Each peer carries whatever it published about itself as `metadata` — see
|
|
251
|
+
* {@link usePresenceMetadata} for publishing this client's.
|
|
252
|
+
*/
|
|
253
|
+
declare function usePeers(options?: UsePeersOptions): readonly Peer[];
|
|
113
254
|
/**
|
|
114
255
|
* This client's own id on the presence bus (matches patch origin ids).
|
|
115
256
|
*
|
|
@@ -123,6 +264,51 @@ declare function usePeers(options?: {
|
|
|
123
264
|
declare function useClientId(options?: {
|
|
124
265
|
name?: string;
|
|
125
266
|
}): string;
|
|
267
|
+
/**
|
|
268
|
+
* Publish what this client wants peers to know about it — a display name, a tab
|
|
269
|
+
* title, a cursor.
|
|
270
|
+
*
|
|
271
|
+
* Safe to call with a fresh object every render: the value is compared by
|
|
272
|
+
* contents, so an unchanged one announces nothing and re-renders nobody.
|
|
273
|
+
*
|
|
274
|
+
* ```tsx
|
|
275
|
+
* usePresenceMetadata({ name: user.name, editing: currentDocId });
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* Published in an effect rather than during render, because announcing is a
|
|
279
|
+
* side effect on every other tab — and a render that React throws away must not
|
|
280
|
+
* be one other tabs already saw.
|
|
281
|
+
*/
|
|
282
|
+
declare function usePresenceMetadata(metadata: unknown, options?: UsePeersOptions): void;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Whether a persisted store has finished restoring.
|
|
286
|
+
*
|
|
287
|
+
* `false` on the first render, then `true` once the restore lands — including
|
|
288
|
+
* the cases where there is nothing to restore, which settle immediately.
|
|
289
|
+
*
|
|
290
|
+
* Deliberately `false` first even for a synchronous adapter that has already
|
|
291
|
+
* finished. The alternative is a value that differs between the server render
|
|
292
|
+
* and the browser's hydrating render, which is a hydration mismatch on every
|
|
293
|
+
* app that uses it — the same reason `useClientId` reports `''` until the
|
|
294
|
+
* commit after hydration.
|
|
295
|
+
*
|
|
296
|
+
* The gap this closes only exists for **async** adapters, and it is one
|
|
297
|
+
* last-writer-wins makes invisible: a keystroke landing before the restore
|
|
298
|
+
* writes at counter 1, the restore arrives holding counter 5, and the newer
|
|
299
|
+
* keystroke is correctly discarded. The behaviour is right; the surprise is
|
|
300
|
+
* total. Gate the input and there is no gap:
|
|
301
|
+
*
|
|
302
|
+
* ```tsx
|
|
303
|
+
* const ready = useHydrated({ store: 'settings' });
|
|
304
|
+
* return <input disabled={!ready} value={draft} onChange={…} />;
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* A synchronous adapter — `localStorageAdapter` and friends — has already
|
|
308
|
+
* restored by the time the store is handed back, so there is no gap to guard
|
|
309
|
+
* and this simply flips to `true` in the commit after mount.
|
|
310
|
+
*/
|
|
311
|
+
declare function useHydrated(options?: UseSharedStateOptions): boolean;
|
|
126
312
|
|
|
127
313
|
interface UseLeaderOptions extends LeaderOptions {
|
|
128
314
|
/** Which bus to elect on. Defaults to the shared default name. */
|
|
@@ -143,6 +329,52 @@ declare function useIsLeader(options?: UseLeaderOptions): boolean;
|
|
|
143
329
|
*/
|
|
144
330
|
declare function useLeaderEffect(effect: () => void | (() => void), options?: UseLeaderOptions): void;
|
|
145
331
|
|
|
332
|
+
/**
|
|
333
|
+
* The React half of a namespace: the same hooks, with every bus name prefixed.
|
|
334
|
+
*
|
|
335
|
+
* Options keep their meaning — `store` and `name` are still relative names, and
|
|
336
|
+
* `scope` still says how far a value travels, which is a different axis from
|
|
337
|
+
* which namespace it lives in.
|
|
338
|
+
*/
|
|
339
|
+
interface ReactNamespace extends Namespace {
|
|
340
|
+
defineStore<S extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: DefineStoreOptions): StoreHooks<S>;
|
|
341
|
+
defineChannel<M extends MessageMap>(name?: string, options?: ChannelOptions<M>): ChannelHooks<M>;
|
|
342
|
+
getSharedStore(name?: string, scope?: UseSharedStateOptions['scope']): AnyStore;
|
|
343
|
+
useSharedState<T>(key: string, initial: T, options?: UseSharedStateOptions): [T, (next: T | ((prev: T) => T)) => void];
|
|
344
|
+
useSharedStore<S extends Record<string, unknown>, T>(selector: (state: S) => T, options?: UseSharedSelectorOptions): T;
|
|
345
|
+
usePeers(options?: UsePeersOptions): readonly Peer[];
|
|
346
|
+
usePresenceMetadata(metadata: unknown, options?: UsePeersOptions): void;
|
|
347
|
+
useHydrated(options?: UseSharedStateOptions): boolean;
|
|
348
|
+
useClientId(options?: {
|
|
349
|
+
name?: string;
|
|
350
|
+
}): string;
|
|
351
|
+
useLeader(options?: UseLeaderOptions): LeaderSnapshot;
|
|
352
|
+
useIsLeader(options?: UseLeaderOptions): boolean;
|
|
353
|
+
useLeaderEffect(effect: () => void | (() => void), options?: UseLeaderOptions): void;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Namespaced hooks and factories, so two independently deployed apps on one
|
|
357
|
+
* origin cannot collide by both taking the defaults.
|
|
358
|
+
*
|
|
359
|
+
* ```ts
|
|
360
|
+
* // checkout/bus.ts
|
|
361
|
+
* export const checkout = createNamespace('checkout');
|
|
362
|
+
*
|
|
363
|
+
* // anywhere in the checkout app
|
|
364
|
+
* const [items, setItems] = checkout.useSharedState('items', []);
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* Call it **at module scope**, like `defineStore` and `defineChannel`. It
|
|
368
|
+
* builds a small object of bound functions, and rebuilding that on every render
|
|
369
|
+
* would hand React a new `useSharedState` identity each time — harmless for the
|
|
370
|
+
* hooks themselves, which key off the bus name, but pointless work and a
|
|
371
|
+
* confusing thing to see in a profile.
|
|
372
|
+
*
|
|
373
|
+
* See the core `createNamespace` for what a namespace does and does not
|
|
374
|
+
* guarantee. In short: it prevents collision, not access.
|
|
375
|
+
*/
|
|
376
|
+
declare function createNamespace(namespace: string): ReactNamespace;
|
|
377
|
+
|
|
146
378
|
type OpenedWindowStatus = 'idle' | 'opening' | 'connected' | 'done' | 'closed-early' | 'error';
|
|
147
379
|
/**
|
|
148
380
|
* The flow's state as a discriminated union, so `status` narrows `result` and
|
|
@@ -199,4 +431,4 @@ type UseOpenedWindow<Out extends MessageMap, In extends MessageMap, R> = OpenedW
|
|
|
199
431
|
*/
|
|
200
432
|
declare function useOpenedWindow<Out extends MessageMap, In extends MessageMap, R = unknown>(factory: () => OpenedWindow<Out, In, R>): UseOpenedWindow<Out, In, R>;
|
|
201
433
|
|
|
202
|
-
export { type AnyStore, type ChannelHooks, type DefineStoreOptions, type OpenedWindowControls, type OpenedWindowState, type OpenedWindowStatus, type ShareScope, type StoreHooks, type UseLeaderOptions, type UseOpenedWindow, type UseSharedStateOptions, defineChannel, defineStore, getLeader, getSharedStore, useChannel, useClientId, useIsLeader, useLeader, useLeaderEffect, useMessage, useOpenedWindow, usePeers, useSend, useSharedState };
|
|
434
|
+
export { type AnyStore, type ChannelHooks, type DefineStoreOptions, type OpenedWindowControls, type OpenedWindowState, type OpenedWindowStatus, type ReactNamespace, type ShareScope, type StoreHooks, type UseLeaderOptions, type UseMessageOptions, type UseOpenedWindow, type UsePeersOptions, type UseSharedReducerOptions, type UseSharedSelectorOptions, type UseSharedStateOptions, createNamespace, defineChannel, defineStore, getLeader, getSharedStore, shallowEqual, useAnswer, useAsk, useChannel, useClientId, useHydrated, useIsLeader, useLeader, useLeaderEffect, useMessage, useOpenedWindow, usePeers, usePresenceMetadata, useSend, useSharedReducer, useSharedState, useSharedStore };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SharedStore, LeaderOptions, Leader, MessageMap, Channel, MessageMeta, PersistAdapter, Peer, LeaderSnapshot, OpenedWindow, WindowClosedError } from '@use-everywhere/core';
|
|
2
|
-
export { BroadcastChannelTransport, BusEvent, BusObserver, BusWire, CID_PARAM, Channel, CommonOptions, ConnectToOpenerOptions, DEFAULT_NAME, DebugOptions, HandshakeTimeoutError, Leader, LeaderOptions, LeaderSnapshot, LeaderStrategy, MessageEventLike, MessageMap, MessageMeta, NoopTransport, OpenWindowOptions, OpenedWindow, OpenerConnection, Peer, PeerKind, PersistAdapter, PersistOptions, Persisted, Presence, PresenceOptions, SharedStore, SharedStoreOptions, StorageLike, StorageTransport, Transport, TransportKind, Version, WebStorageAdapterOptions, WindowClosedError, WindowEventTarget, WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, isBroadcastChannelAvailable, isStorageEventAvailable, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter } from '@use-everywhere/core';
|
|
1
|
+
import { SharedStore, LeaderOptions, Leader, MessageMap, ReplyMap, Channel, MessageMeta, ChannelOptions, PersistAdapter, RestoreError, Peer, LeaderSnapshot, Namespace, OpenedWindow, WindowClosedError } from '@use-everywhere/core';
|
|
2
|
+
export { AskOptions, BroadcastChannelTransport, BusEvent, BusObserver, BusWire, CID_PARAM, Channel, ChannelOptions, CommonOptions, ConnectToOpenerOptions, DEFAULT_NAME, DebugOptions, HandshakeTimeoutError, IndexedDbAdapterOptions, InvalidPayload, Leader, LeaderOptions, LeaderSnapshot, LeaderStrategy, MessageEventLike, MessageMap, MessageMeta, Namespace, NoopTransport, OnInvalid, OnOptions, OpenWindowOptions, OpenedWindow, OpenerConnection, Peer, PeerKind, PersistAdapter, PersistOptions, Persisted, PostOptions, Presence, PresenceOptions, ReplyMap, RestoreError, SchemaMap, SchemaOptions, Serializer, SharedReducer, SharedReducerOptions, SharedStore, SharedStoreOptions, StandardSchemaV1, StorageLike, StorageTransport, Transport, TransportKind, Version, WIRE_VERSION, WebStorageAdapterOptions, WindowClosedError, WindowEventTarget, WindowLike, connectToOpener, createChannel, createLeader, createPresence, createSharedReducer, createSharedStore, defaultTransport, enableDebug, getBusNames, getTransportKind, getWireSkew, indexedDbAdapter, isBroadcastChannelAvailable, isStorageEventAvailable, jsonSerializer, localStorageAdapter, newer, observeBus, openWindow, sessionStorageAdapter, webStorageAdapter } from '@use-everywhere/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* How far a shared value travels:
|
|
@@ -29,6 +29,92 @@ interface UseSharedStateOptions {
|
|
|
29
29
|
*/
|
|
30
30
|
declare function useSharedState<T>(key: string, initial: T, options?: UseSharedStateOptions): [T, (next: T | ((prev: T) => T)) => void];
|
|
31
31
|
|
|
32
|
+
interface UseSharedReducerOptions {
|
|
33
|
+
/** Bus name. Default 'use-everywhere'. */
|
|
34
|
+
name?: string;
|
|
35
|
+
/** Which reducer this is, when several share a bus. Default 'default'. */
|
|
36
|
+
key?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Like `useReducer`, but every tab, window, and worker on this origin applies
|
|
40
|
+
* the same actions in the same order.
|
|
41
|
+
*
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const [count, dispatch] = useSharedReducer((n, action) => n + action.by, 0);
|
|
44
|
+
* <button onClick={() => dispatch({ by: 1 })}>{count}</button>;
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Reach for this instead of `useSharedState` whenever a write is *relative to
|
|
48
|
+
* what is already there* — a counter, a total, a list you append to. Shared
|
|
49
|
+
* state converges last-writer-wins on the value, so two tabs incrementing at
|
|
50
|
+
* once both write the same result and one increment vanishes. A reducer sends
|
|
51
|
+
* the action rather than the result, and two increments are two actions.
|
|
52
|
+
*
|
|
53
|
+
* For a plain register — a theme, a selection, a draft — `useSharedState` is
|
|
54
|
+
* still the right tool and the cheaper one.
|
|
55
|
+
*
|
|
56
|
+
* ## The rules
|
|
57
|
+
*
|
|
58
|
+
* **Actions must survive the wire.** They are structured-cloned to every peer,
|
|
59
|
+
* so no functions, no class instances, no DOM nodes.
|
|
60
|
+
*
|
|
61
|
+
* **The reducer must be pure**, and it must be the *same* reducer everywhere.
|
|
62
|
+
* Every client folds the same actions itself; a client whose fold differs gets
|
|
63
|
+
* a different answer, and nothing can detect that. The first caller's function
|
|
64
|
+
* is the one used for the life of the page — a re-render passes a new identity
|
|
65
|
+
* every time, and swapping the fold under a history already applied is exactly
|
|
66
|
+
* the divergence this exists to prevent.
|
|
67
|
+
*
|
|
68
|
+
* **Ordering needs a leader**, which this shares with `useLeader` on the same
|
|
69
|
+
* bus rather than electing a second one. Before a seat is filled — the first
|
|
70
|
+
* moments of the first tab — dispatches are held locally and ordered once it
|
|
71
|
+
* is. They are never lost, only pending.
|
|
72
|
+
*/
|
|
73
|
+
declare function useSharedReducer<S, A>(reducer: (state: S, action: A) => S, initial: S, options?: UseSharedReducerOptions): [S, (action: A) => void];
|
|
74
|
+
|
|
75
|
+
interface UseSharedSelectorOptions extends UseSharedStateOptions {
|
|
76
|
+
/**
|
|
77
|
+
* Whether two selected values count as the same. Default `Object.is`.
|
|
78
|
+
*
|
|
79
|
+
* A selector returning an object or array builds a new one every time it
|
|
80
|
+
* runs, so `Object.is` never matches and the component re-renders on every
|
|
81
|
+
* write to the store. Pass a shallow comparison for those.
|
|
82
|
+
*/
|
|
83
|
+
equal?: (a: unknown, b: unknown) => boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read a derived value from a shared store, re-rendering only when it changes.
|
|
87
|
+
*
|
|
88
|
+
* `useSharedState` subscribes to one key, which is the right shape for reading
|
|
89
|
+
* one thing. Reading *across* keys meant either one hook per key or a
|
|
90
|
+
* subscription to the whole store, and the latter re-renders on every write to
|
|
91
|
+
* anything in it.
|
|
92
|
+
*
|
|
93
|
+
* ```tsx
|
|
94
|
+
* const total = useSharedStore<Cart, number>(
|
|
95
|
+
* (cart) => cart.items.length + cart.saved.length,
|
|
96
|
+
* );
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* The selector runs on every store change; the component only re-renders when
|
|
100
|
+
* its result changes by `equal`.
|
|
101
|
+
*
|
|
102
|
+
* ## It reads, it does not declare
|
|
103
|
+
*
|
|
104
|
+
* `useSharedState(key, initial)` registers `key` with a default. A selector
|
|
105
|
+
* does not — it sees whatever is in the store, so a key nothing has registered
|
|
106
|
+
* or written yet is `undefined`. Write selectors that tolerate that, or declare
|
|
107
|
+
* the defaults with `useSharedState` (or a store initial) somewhere that
|
|
108
|
+
* mounts first.
|
|
109
|
+
*
|
|
110
|
+
* The same applies on a server, where the store is inert and empty: the
|
|
111
|
+
* selector runs against `{}`, and its result is what the server-rendered markup
|
|
112
|
+
* shows.
|
|
113
|
+
*/
|
|
114
|
+
declare function useSharedStore<S extends Record<string, unknown>, T>(selector: (state: S) => T, options?: UseSharedSelectorOptions): T;
|
|
115
|
+
/** Shallow equality over an object or array, for selectors that build one. */
|
|
116
|
+
declare function shallowEqual(a: unknown, b: unknown): boolean;
|
|
117
|
+
|
|
32
118
|
/** Registry stores hold arbitrary keys — shape is decided by the hooks using them. */
|
|
33
119
|
type AnyStore = SharedStore<Record<string, unknown>>;
|
|
34
120
|
|
|
@@ -43,14 +129,41 @@ declare function getSharedStore(name?: string, scope?: ShareScope): AnyStore;
|
|
|
43
129
|
declare function getLeader(name: string, options?: LeaderOptions): Leader;
|
|
44
130
|
|
|
45
131
|
/** Get the page-wide typed channel for `name` (one instance per name). */
|
|
46
|
-
declare function useChannel<M extends MessageMap>(name: string): Channel<M>;
|
|
132
|
+
declare function useChannel<M extends MessageMap, R extends ReplyMap<M> = Record<never, never>>(name: string): Channel<M, R>;
|
|
133
|
+
interface UseMessageOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Subscribe at all. Default true.
|
|
136
|
+
*
|
|
137
|
+
* `false` unsubscribes rather than filtering inside the handler, so a
|
|
138
|
+
* component that is not interested costs nothing — and the alternative,
|
|
139
|
+
* calling the hook conditionally, is not allowed.
|
|
140
|
+
*/
|
|
141
|
+
enabled?: boolean;
|
|
142
|
+
/** Unsubscribe after the first message. */
|
|
143
|
+
once?: boolean;
|
|
144
|
+
}
|
|
47
145
|
/**
|
|
48
146
|
* Subscribe to one message type. The handler is kept fresh without
|
|
49
147
|
* resubscribing, so it may close over render state.
|
|
50
148
|
*/
|
|
51
|
-
declare function useMessage<M extends MessageMap, K extends keyof M & string>(channel: Channel<M>, type: K, handler: (payload: M[K], meta: MessageMeta) => void): void;
|
|
149
|
+
declare function useMessage<M extends MessageMap, R extends ReplyMap<M>, K extends keyof M & string>(channel: Channel<M, R>, type: K, handler: (payload: M[K], meta: MessageMeta) => void, options?: UseMessageOptions): void;
|
|
150
|
+
/**
|
|
151
|
+
* Answer `ask`s of one type for as long as this component is mounted.
|
|
152
|
+
*
|
|
153
|
+
* A hook rather than a bare `channel.answer` call because a responder is a
|
|
154
|
+
* subscription: registering one in a render would leave the last unmounted
|
|
155
|
+
* component answering for the page.
|
|
156
|
+
*
|
|
157
|
+
* One responder per type per channel — registering a second replaces the first,
|
|
158
|
+
* which is the same first-wins-then-replace rule `answer` has in core.
|
|
159
|
+
*/
|
|
160
|
+
declare function useAnswer<M extends MessageMap, R extends ReplyMap<M>, K extends keyof M & keyof R & string>(channel: Channel<M, R>, type: K, responder: (payload: M[K], meta: MessageMeta) => R[K], options?: {
|
|
161
|
+
enabled?: boolean;
|
|
162
|
+
}): void;
|
|
52
163
|
/** The channel's post function (stable identity per channel). */
|
|
53
|
-
declare function useSend<M extends MessageMap
|
|
164
|
+
declare function useSend<M extends MessageMap, R extends ReplyMap<M>>(channel: Channel<M, R>): Channel<M, R>['post'];
|
|
165
|
+
/** The channel's ask function (stable identity per channel). */
|
|
166
|
+
declare function useAsk<M extends MessageMap, R extends ReplyMap<M>>(channel: Channel<M, R>): Channel<M, R>['ask'];
|
|
54
167
|
|
|
55
168
|
/** A channel bound to a name and message map: typed hooks with no per-call generics. */
|
|
56
169
|
interface ChannelHooks<M extends MessageMap> {
|
|
@@ -70,8 +183,11 @@ interface ChannelHooks<M extends MessageMap> {
|
|
|
70
183
|
* typed hooks back. Sugar over useChannel/useMessage/useSend — the same
|
|
71
184
|
* page-wide channel singleton is shared, so mixing bound and standalone
|
|
72
185
|
* hooks for one name is safe.
|
|
186
|
+
*
|
|
187
|
+
* Options are registered here and applied when the channel is first needed, so
|
|
188
|
+
* declaring a `schema` at module scope still constructs nothing on import.
|
|
73
189
|
*/
|
|
74
|
-
declare function defineChannel<M extends MessageMap>(name: string): ChannelHooks<M>;
|
|
190
|
+
declare function defineChannel<M extends MessageMap>(name: string, options?: ChannelOptions<M>): ChannelHooks<M>;
|
|
75
191
|
|
|
76
192
|
interface DefineStoreOptions {
|
|
77
193
|
/** Restore this store from disk on first use, and write it back as it changes. */
|
|
@@ -80,6 +196,16 @@ interface DefineStoreOptions {
|
|
|
80
196
|
persistKeys?: string[];
|
|
81
197
|
/** Coalesce disk writes for this long. Default 100. */
|
|
82
198
|
persistDebounceMs?: number;
|
|
199
|
+
/**
|
|
200
|
+
* The version of your persisted state's shape. Bump it whenever a key changes
|
|
201
|
+
* meaning or type, and supply `migrate` to carry old data forward. Default 0,
|
|
202
|
+
* which is also what anything written before this existed reads as.
|
|
203
|
+
*/
|
|
204
|
+
persistVersion?: number;
|
|
205
|
+
/** Bring persisted state written at an older `persistVersion` up to the current one. */
|
|
206
|
+
migrate?: (state: Record<string, unknown>, from: number) => Record<string, unknown>;
|
|
207
|
+
/** Called when persisted state is refused instead of restored. Defaults to a development warning. */
|
|
208
|
+
onRestoreError?: (error: RestoreError) => void;
|
|
83
209
|
/** How far this store is shared. Default 'everywhere'. */
|
|
84
210
|
scope?: ShareScope;
|
|
85
211
|
}
|
|
@@ -106,10 +232,25 @@ interface StoreHooks<S extends Record<string, unknown>> {
|
|
|
106
232
|
*/
|
|
107
233
|
declare function defineStore<S extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: DefineStoreOptions): StoreHooks<S>;
|
|
108
234
|
|
|
109
|
-
|
|
110
|
-
|
|
235
|
+
interface UsePeersOptions {
|
|
236
|
+
/** Bus name. Default 'use-everywhere'. */
|
|
111
237
|
name?: string;
|
|
112
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Include this client in the list. Default false.
|
|
240
|
+
*
|
|
241
|
+
* The default answers "who *else* is here", which is what a presence strip
|
|
242
|
+
* asks. Turn it on for an avatar list, where leaving yourself out means every
|
|
243
|
+
* tab renders a different list of the same room.
|
|
244
|
+
*/
|
|
245
|
+
includeSelf?: boolean;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The tabs/windows/workers currently alive on this origin.
|
|
249
|
+
*
|
|
250
|
+
* Each peer carries whatever it published about itself as `metadata` — see
|
|
251
|
+
* {@link usePresenceMetadata} for publishing this client's.
|
|
252
|
+
*/
|
|
253
|
+
declare function usePeers(options?: UsePeersOptions): readonly Peer[];
|
|
113
254
|
/**
|
|
114
255
|
* This client's own id on the presence bus (matches patch origin ids).
|
|
115
256
|
*
|
|
@@ -123,6 +264,51 @@ declare function usePeers(options?: {
|
|
|
123
264
|
declare function useClientId(options?: {
|
|
124
265
|
name?: string;
|
|
125
266
|
}): string;
|
|
267
|
+
/**
|
|
268
|
+
* Publish what this client wants peers to know about it — a display name, a tab
|
|
269
|
+
* title, a cursor.
|
|
270
|
+
*
|
|
271
|
+
* Safe to call with a fresh object every render: the value is compared by
|
|
272
|
+
* contents, so an unchanged one announces nothing and re-renders nobody.
|
|
273
|
+
*
|
|
274
|
+
* ```tsx
|
|
275
|
+
* usePresenceMetadata({ name: user.name, editing: currentDocId });
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* Published in an effect rather than during render, because announcing is a
|
|
279
|
+
* side effect on every other tab — and a render that React throws away must not
|
|
280
|
+
* be one other tabs already saw.
|
|
281
|
+
*/
|
|
282
|
+
declare function usePresenceMetadata(metadata: unknown, options?: UsePeersOptions): void;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Whether a persisted store has finished restoring.
|
|
286
|
+
*
|
|
287
|
+
* `false` on the first render, then `true` once the restore lands — including
|
|
288
|
+
* the cases where there is nothing to restore, which settle immediately.
|
|
289
|
+
*
|
|
290
|
+
* Deliberately `false` first even for a synchronous adapter that has already
|
|
291
|
+
* finished. The alternative is a value that differs between the server render
|
|
292
|
+
* and the browser's hydrating render, which is a hydration mismatch on every
|
|
293
|
+
* app that uses it — the same reason `useClientId` reports `''` until the
|
|
294
|
+
* commit after hydration.
|
|
295
|
+
*
|
|
296
|
+
* The gap this closes only exists for **async** adapters, and it is one
|
|
297
|
+
* last-writer-wins makes invisible: a keystroke landing before the restore
|
|
298
|
+
* writes at counter 1, the restore arrives holding counter 5, and the newer
|
|
299
|
+
* keystroke is correctly discarded. The behaviour is right; the surprise is
|
|
300
|
+
* total. Gate the input and there is no gap:
|
|
301
|
+
*
|
|
302
|
+
* ```tsx
|
|
303
|
+
* const ready = useHydrated({ store: 'settings' });
|
|
304
|
+
* return <input disabled={!ready} value={draft} onChange={…} />;
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* A synchronous adapter — `localStorageAdapter` and friends — has already
|
|
308
|
+
* restored by the time the store is handed back, so there is no gap to guard
|
|
309
|
+
* and this simply flips to `true` in the commit after mount.
|
|
310
|
+
*/
|
|
311
|
+
declare function useHydrated(options?: UseSharedStateOptions): boolean;
|
|
126
312
|
|
|
127
313
|
interface UseLeaderOptions extends LeaderOptions {
|
|
128
314
|
/** Which bus to elect on. Defaults to the shared default name. */
|
|
@@ -143,6 +329,52 @@ declare function useIsLeader(options?: UseLeaderOptions): boolean;
|
|
|
143
329
|
*/
|
|
144
330
|
declare function useLeaderEffect(effect: () => void | (() => void), options?: UseLeaderOptions): void;
|
|
145
331
|
|
|
332
|
+
/**
|
|
333
|
+
* The React half of a namespace: the same hooks, with every bus name prefixed.
|
|
334
|
+
*
|
|
335
|
+
* Options keep their meaning — `store` and `name` are still relative names, and
|
|
336
|
+
* `scope` still says how far a value travels, which is a different axis from
|
|
337
|
+
* which namespace it lives in.
|
|
338
|
+
*/
|
|
339
|
+
interface ReactNamespace extends Namespace {
|
|
340
|
+
defineStore<S extends Record<string, unknown> = Record<string, unknown>>(name: string, options?: DefineStoreOptions): StoreHooks<S>;
|
|
341
|
+
defineChannel<M extends MessageMap>(name?: string, options?: ChannelOptions<M>): ChannelHooks<M>;
|
|
342
|
+
getSharedStore(name?: string, scope?: UseSharedStateOptions['scope']): AnyStore;
|
|
343
|
+
useSharedState<T>(key: string, initial: T, options?: UseSharedStateOptions): [T, (next: T | ((prev: T) => T)) => void];
|
|
344
|
+
useSharedStore<S extends Record<string, unknown>, T>(selector: (state: S) => T, options?: UseSharedSelectorOptions): T;
|
|
345
|
+
usePeers(options?: UsePeersOptions): readonly Peer[];
|
|
346
|
+
usePresenceMetadata(metadata: unknown, options?: UsePeersOptions): void;
|
|
347
|
+
useHydrated(options?: UseSharedStateOptions): boolean;
|
|
348
|
+
useClientId(options?: {
|
|
349
|
+
name?: string;
|
|
350
|
+
}): string;
|
|
351
|
+
useLeader(options?: UseLeaderOptions): LeaderSnapshot;
|
|
352
|
+
useIsLeader(options?: UseLeaderOptions): boolean;
|
|
353
|
+
useLeaderEffect(effect: () => void | (() => void), options?: UseLeaderOptions): void;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Namespaced hooks and factories, so two independently deployed apps on one
|
|
357
|
+
* origin cannot collide by both taking the defaults.
|
|
358
|
+
*
|
|
359
|
+
* ```ts
|
|
360
|
+
* // checkout/bus.ts
|
|
361
|
+
* export const checkout = createNamespace('checkout');
|
|
362
|
+
*
|
|
363
|
+
* // anywhere in the checkout app
|
|
364
|
+
* const [items, setItems] = checkout.useSharedState('items', []);
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* Call it **at module scope**, like `defineStore` and `defineChannel`. It
|
|
368
|
+
* builds a small object of bound functions, and rebuilding that on every render
|
|
369
|
+
* would hand React a new `useSharedState` identity each time — harmless for the
|
|
370
|
+
* hooks themselves, which key off the bus name, but pointless work and a
|
|
371
|
+
* confusing thing to see in a profile.
|
|
372
|
+
*
|
|
373
|
+
* See the core `createNamespace` for what a namespace does and does not
|
|
374
|
+
* guarantee. In short: it prevents collision, not access.
|
|
375
|
+
*/
|
|
376
|
+
declare function createNamespace(namespace: string): ReactNamespace;
|
|
377
|
+
|
|
146
378
|
type OpenedWindowStatus = 'idle' | 'opening' | 'connected' | 'done' | 'closed-early' | 'error';
|
|
147
379
|
/**
|
|
148
380
|
* The flow's state as a discriminated union, so `status` narrows `result` and
|
|
@@ -199,4 +431,4 @@ type UseOpenedWindow<Out extends MessageMap, In extends MessageMap, R> = OpenedW
|
|
|
199
431
|
*/
|
|
200
432
|
declare function useOpenedWindow<Out extends MessageMap, In extends MessageMap, R = unknown>(factory: () => OpenedWindow<Out, In, R>): UseOpenedWindow<Out, In, R>;
|
|
201
433
|
|
|
202
|
-
export { type AnyStore, type ChannelHooks, type DefineStoreOptions, type OpenedWindowControls, type OpenedWindowState, type OpenedWindowStatus, type ShareScope, type StoreHooks, type UseLeaderOptions, type UseOpenedWindow, type UseSharedStateOptions, defineChannel, defineStore, getLeader, getSharedStore, useChannel, useClientId, useIsLeader, useLeader, useLeaderEffect, useMessage, useOpenedWindow, usePeers, useSend, useSharedState };
|
|
434
|
+
export { type AnyStore, type ChannelHooks, type DefineStoreOptions, type OpenedWindowControls, type OpenedWindowState, type OpenedWindowStatus, type ReactNamespace, type ShareScope, type StoreHooks, type UseLeaderOptions, type UseMessageOptions, type UseOpenedWindow, type UsePeersOptions, type UseSharedReducerOptions, type UseSharedSelectorOptions, type UseSharedStateOptions, createNamespace, defineChannel, defineStore, getLeader, getSharedStore, shallowEqual, useAnswer, useAsk, useChannel, useClientId, useHydrated, useIsLeader, useLeader, useLeaderEffect, useMessage, useOpenedWindow, usePeers, usePresenceMetadata, useSend, useSharedReducer, useSharedState, useSharedStore };
|