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
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createChannel,
|
|
6
6
|
createLeader,
|
|
7
7
|
createPresence,
|
|
8
|
+
createSharedReducer,
|
|
8
9
|
createSharedStore,
|
|
9
10
|
DEFAULT_NAME,
|
|
10
11
|
NoopTransport
|
|
@@ -49,6 +50,12 @@ var unsubscribe = () => noop;
|
|
|
49
50
|
var INERT = Object.freeze({
|
|
50
51
|
clientId: SERVER_CLIENT_ID,
|
|
51
52
|
// store
|
|
53
|
+
//
|
|
54
|
+
// Resolved, unlike the leader's `waitForLeadership` below. A server render
|
|
55
|
+
// has nothing to restore and is documented to render defaults, so `await
|
|
56
|
+
// store.hydrated` must proceed — a never-settling promise would hang the
|
|
57
|
+
// render rather than describe it honestly.
|
|
58
|
+
hydrated: Promise.resolve(),
|
|
52
59
|
state: EMPTY,
|
|
53
60
|
getVersions: () => EMPTY,
|
|
54
61
|
set: noop,
|
|
@@ -57,6 +64,8 @@ var INERT = Object.freeze({
|
|
|
57
64
|
// store + leader share getSnapshot; presence has getPeers
|
|
58
65
|
getSnapshot: () => EMPTY,
|
|
59
66
|
getPeers: () => NO_PEERS,
|
|
67
|
+
// presence
|
|
68
|
+
setMetadata: noop,
|
|
60
69
|
// leader
|
|
61
70
|
resign: noop,
|
|
62
71
|
setEligible: noop,
|
|
@@ -67,7 +76,20 @@ var INERT = Object.freeze({
|
|
|
67
76
|
var createServerStore = () => INERT;
|
|
68
77
|
var createServerPresence = () => INERT;
|
|
69
78
|
var NO_LEADER = Object.freeze({ leaderId: null, isLeader: false });
|
|
70
|
-
var
|
|
79
|
+
var NEVER = () => new Promise(() => {
|
|
80
|
+
});
|
|
81
|
+
var createServerLeader = () => ({
|
|
82
|
+
...INERT,
|
|
83
|
+
strategy: "heartbeat",
|
|
84
|
+
getSnapshot: () => NO_LEADER,
|
|
85
|
+
waitForLeadership: NEVER
|
|
86
|
+
});
|
|
87
|
+
var createServerReducer = (initial) => ({
|
|
88
|
+
...INERT,
|
|
89
|
+
getSnapshot: () => initial,
|
|
90
|
+
dispatch: noop,
|
|
91
|
+
pendingCount: () => 0
|
|
92
|
+
});
|
|
71
93
|
var createServerChannel = (name) => ({ ...INERT, name, post: noop, on: unsubscribe });
|
|
72
94
|
|
|
73
95
|
// src/registry.ts
|
|
@@ -88,15 +110,17 @@ function getSharedStore(name = DEFAULT_NAME, scope = "everywhere") {
|
|
|
88
110
|
function configSignature(options) {
|
|
89
111
|
const persist = options?.persist;
|
|
90
112
|
if (!persist) return "none";
|
|
91
|
-
return `persist:${persist.keys?.join(",") ?? "*"}:${persist.debounceMs ?? "default"}`;
|
|
113
|
+
return `persist:${persist.keys?.join(",") ?? "*"}:${persist.debounceMs ?? "default"}:v${persist.version ?? 0}`;
|
|
92
114
|
}
|
|
93
115
|
function configureStore(name, scope, options) {
|
|
94
116
|
const key = `${scope} ${name}`;
|
|
95
117
|
if (stores.has(key)) {
|
|
96
118
|
if (configSignature(storeConfig.get(key)) === configSignature(options)) return;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
if (process.env.NODE_ENV !== "production") {
|
|
120
|
+
devWarn(
|
|
121
|
+
`[use-everywhere] defineStore('${name}') ran after that store was already created, with different options. The live store keeps the configuration it was built with. Move defineStore to module scope, before any component reads the store.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
100
124
|
return;
|
|
101
125
|
}
|
|
102
126
|
storeConfig.set(key, options);
|
|
@@ -110,11 +134,12 @@ function getStore(name, scope = "everywhere") {
|
|
|
110
134
|
}
|
|
111
135
|
return store;
|
|
112
136
|
}
|
|
113
|
-
function getPresence(name) {
|
|
114
|
-
|
|
137
|
+
function getPresence(name, includeSelf = false) {
|
|
138
|
+
const key = includeSelf ? `self ${name}` : name;
|
|
139
|
+
let presence = presences.get(key);
|
|
115
140
|
if (!presence) {
|
|
116
|
-
presence = isServer() ? createServerPresence() : createPresence(name);
|
|
117
|
-
presences.set(
|
|
141
|
+
presence = isServer() ? createServerPresence() : createPresence(name, { includeSelf });
|
|
142
|
+
presences.set(key, presence);
|
|
118
143
|
}
|
|
119
144
|
return presence;
|
|
120
145
|
}
|
|
@@ -135,26 +160,53 @@ function warnOnLeaderOptionConflict(name, options) {
|
|
|
135
160
|
for (const key of ["heartbeatMs", "leaseMs"]) {
|
|
136
161
|
const requested = options[key];
|
|
137
162
|
if (requested !== void 0 && requested !== first?.[key]) {
|
|
163
|
+
if (process.env.NODE_ENV !== "production") {
|
|
164
|
+
devWarn(
|
|
165
|
+
`[use-everywhere] leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
var channelConfig = /* @__PURE__ */ new Map();
|
|
172
|
+
function configureChannel(name, options) {
|
|
173
|
+
if (channels.has(name)) {
|
|
174
|
+
const before = Object.keys(channelConfig.get(name)?.schema ?? {}).sort();
|
|
175
|
+
const after = Object.keys(options.schema ?? {}).sort();
|
|
176
|
+
if (before.join() === after.join()) return;
|
|
177
|
+
if (process.env.NODE_ENV !== "production") {
|
|
138
178
|
devWarn(
|
|
139
|
-
`[use-everywhere]
|
|
179
|
+
`[use-everywhere] defineChannel('${name}') ran after that channel was already created, with different options. The live channel keeps the configuration it was built with. Move defineChannel to module scope, before any component sends or receives on it.`
|
|
140
180
|
);
|
|
141
181
|
}
|
|
182
|
+
return;
|
|
142
183
|
}
|
|
184
|
+
channelConfig.set(name, options);
|
|
185
|
+
}
|
|
186
|
+
var reducers = /* @__PURE__ */ new Map();
|
|
187
|
+
function getReducer(name, key, reducer, initial) {
|
|
188
|
+
const id = `${name} ${key}`;
|
|
189
|
+
let existing = reducers.get(id);
|
|
190
|
+
if (!existing) {
|
|
191
|
+
existing = isServer() ? createServerReducer(initial) : createSharedReducer(name, reducer, initial, { key, leader: getLeader(name) });
|
|
192
|
+
reducers.set(id, existing);
|
|
193
|
+
}
|
|
194
|
+
return existing;
|
|
143
195
|
}
|
|
144
196
|
function getChannel(name) {
|
|
145
197
|
let channel = channels.get(name);
|
|
146
198
|
if (!channel) {
|
|
147
|
-
channel = isServer() ? createServerChannel(name) : createChannel(name);
|
|
199
|
+
channel = isServer() ? createServerChannel(name) : createChannel(name, channelConfig.get(name));
|
|
148
200
|
channels.set(name, channel);
|
|
149
201
|
}
|
|
150
202
|
return channel;
|
|
151
203
|
}
|
|
152
204
|
|
|
153
205
|
// src/use-peers.ts
|
|
154
|
-
import { useCallback, useSyncExternalStore } from "react";
|
|
206
|
+
import { useCallback, useEffect, useSyncExternalStore } from "react";
|
|
155
207
|
var NO_PEERS2 = Object.freeze([]);
|
|
156
208
|
function usePeers(options) {
|
|
157
|
-
const presence = getPresence(options?.name ?? DEFAULT_NAME);
|
|
209
|
+
const presence = getPresence(options?.name ?? DEFAULT_NAME, options?.includeSelf ?? false);
|
|
158
210
|
return useSyncExternalStore(
|
|
159
211
|
useCallback((onChange) => presence.subscribe(onChange), [presence]),
|
|
160
212
|
() => presence.getPeers(),
|
|
@@ -173,6 +225,12 @@ function useClientId(options) {
|
|
|
173
225
|
() => SERVER_CLIENT_ID
|
|
174
226
|
);
|
|
175
227
|
}
|
|
228
|
+
function usePresenceMetadata(metadata, options) {
|
|
229
|
+
const presence = getPresence(options?.name ?? DEFAULT_NAME, options?.includeSelf ?? false);
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
presence.setMetadata(metadata);
|
|
232
|
+
}, [presence, metadata]);
|
|
233
|
+
}
|
|
176
234
|
|
|
177
235
|
export {
|
|
178
236
|
warnOnInitialMismatch,
|
|
@@ -181,7 +239,10 @@ export {
|
|
|
181
239
|
configureStore,
|
|
182
240
|
getStore,
|
|
183
241
|
getLeader,
|
|
242
|
+
configureChannel,
|
|
243
|
+
getReducer,
|
|
184
244
|
getChannel,
|
|
185
245
|
usePeers,
|
|
186
|
-
useClientId
|
|
246
|
+
useClientId,
|
|
247
|
+
usePresenceMetadata
|
|
187
248
|
};
|
package/dist/devtools/index.cjs
CHANGED
|
@@ -42,6 +42,12 @@ var unsubscribe = () => noop;
|
|
|
42
42
|
var INERT = Object.freeze({
|
|
43
43
|
clientId: SERVER_CLIENT_ID,
|
|
44
44
|
// store
|
|
45
|
+
//
|
|
46
|
+
// Resolved, unlike the leader's `waitForLeadership` below. A server render
|
|
47
|
+
// has nothing to restore and is documented to render defaults, so `await
|
|
48
|
+
// store.hydrated` must proceed — a never-settling promise would hang the
|
|
49
|
+
// render rather than describe it honestly.
|
|
50
|
+
hydrated: Promise.resolve(),
|
|
45
51
|
state: EMPTY,
|
|
46
52
|
getVersions: () => EMPTY,
|
|
47
53
|
set: noop,
|
|
@@ -50,6 +56,8 @@ var INERT = Object.freeze({
|
|
|
50
56
|
// store + leader share getSnapshot; presence has getPeers
|
|
51
57
|
getSnapshot: () => EMPTY,
|
|
52
58
|
getPeers: () => NO_PEERS,
|
|
59
|
+
// presence
|
|
60
|
+
setMetadata: noop,
|
|
53
61
|
// leader
|
|
54
62
|
resign: noop,
|
|
55
63
|
setEligible: noop,
|
|
@@ -83,11 +91,12 @@ function getStore(name, scope = "everywhere") {
|
|
|
83
91
|
}
|
|
84
92
|
return store;
|
|
85
93
|
}
|
|
86
|
-
function getPresence(name) {
|
|
87
|
-
|
|
94
|
+
function getPresence(name, includeSelf = false) {
|
|
95
|
+
const key = includeSelf ? `self ${name}` : name;
|
|
96
|
+
let presence = presences.get(key);
|
|
88
97
|
if (!presence) {
|
|
89
|
-
presence = isServer() ? createServerPresence() : (0, import_core.createPresence)(name);
|
|
90
|
-
presences.set(
|
|
98
|
+
presence = isServer() ? createServerPresence() : (0, import_core.createPresence)(name, { includeSelf });
|
|
99
|
+
presences.set(key, presence);
|
|
91
100
|
}
|
|
92
101
|
return presence;
|
|
93
102
|
}
|
|
@@ -96,7 +105,7 @@ function getPresence(name) {
|
|
|
96
105
|
var import_react = require("react");
|
|
97
106
|
var NO_PEERS2 = Object.freeze([]);
|
|
98
107
|
function usePeers(options) {
|
|
99
|
-
const presence = getPresence(options?.name ?? import_core.DEFAULT_NAME);
|
|
108
|
+
const presence = getPresence(options?.name ?? import_core.DEFAULT_NAME, options?.includeSelf ?? false);
|
|
100
109
|
return (0, import_react.useSyncExternalStore)(
|
|
101
110
|
(0, import_react.useCallback)((onChange) => presence.subscribe(onChange), [presence]),
|
|
102
111
|
() => presence.getPeers(),
|
package/dist/devtools/index.js
CHANGED