use-everywhere 0.7.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.
@@ -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,
@@ -75,6 +84,12 @@ var createServerLeader = () => ({
75
84
  getSnapshot: () => NO_LEADER,
76
85
  waitForLeadership: NEVER
77
86
  });
87
+ var createServerReducer = (initial) => ({
88
+ ...INERT,
89
+ getSnapshot: () => initial,
90
+ dispatch: noop,
91
+ pendingCount: () => 0
92
+ });
78
93
  var createServerChannel = (name) => ({ ...INERT, name, post: noop, on: unsubscribe });
79
94
 
80
95
  // src/registry.ts
@@ -95,15 +110,17 @@ function getSharedStore(name = DEFAULT_NAME, scope = "everywhere") {
95
110
  function configSignature(options) {
96
111
  const persist = options?.persist;
97
112
  if (!persist) return "none";
98
- return `persist:${persist.keys?.join(",") ?? "*"}:${persist.debounceMs ?? "default"}`;
113
+ return `persist:${persist.keys?.join(",") ?? "*"}:${persist.debounceMs ?? "default"}:v${persist.version ?? 0}`;
99
114
  }
100
115
  function configureStore(name, scope, options) {
101
116
  const key = `${scope} ${name}`;
102
117
  if (stores.has(key)) {
103
118
  if (configSignature(storeConfig.get(key)) === configSignature(options)) return;
104
- devWarn(
105
- `[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.`
106
- );
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
+ }
107
124
  return;
108
125
  }
109
126
  storeConfig.set(key, options);
@@ -117,11 +134,12 @@ function getStore(name, scope = "everywhere") {
117
134
  }
118
135
  return store;
119
136
  }
120
- function getPresence(name) {
121
- let presence = presences.get(name);
137
+ function getPresence(name, includeSelf = false) {
138
+ const key = includeSelf ? `self ${name}` : name;
139
+ let presence = presences.get(key);
122
140
  if (!presence) {
123
- presence = isServer() ? createServerPresence() : createPresence(name);
124
- presences.set(name, presence);
141
+ presence = isServer() ? createServerPresence() : createPresence(name, { includeSelf });
142
+ presences.set(key, presence);
125
143
  }
126
144
  return presence;
127
145
  }
@@ -142,26 +160,53 @@ function warnOnLeaderOptionConflict(name, options) {
142
160
  for (const key of ["heartbeatMs", "leaseMs"]) {
143
161
  const requested = options[key];
144
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") {
145
178
  devWarn(
146
- `[use-everywhere] leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
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.`
147
180
  );
148
181
  }
182
+ return;
149
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;
150
195
  }
151
196
  function getChannel(name) {
152
197
  let channel = channels.get(name);
153
198
  if (!channel) {
154
- channel = isServer() ? createServerChannel(name) : createChannel(name);
199
+ channel = isServer() ? createServerChannel(name) : createChannel(name, channelConfig.get(name));
155
200
  channels.set(name, channel);
156
201
  }
157
202
  return channel;
158
203
  }
159
204
 
160
205
  // src/use-peers.ts
161
- import { useCallback, useSyncExternalStore } from "react";
206
+ import { useCallback, useEffect, useSyncExternalStore } from "react";
162
207
  var NO_PEERS2 = Object.freeze([]);
163
208
  function usePeers(options) {
164
- const presence = getPresence(options?.name ?? DEFAULT_NAME);
209
+ const presence = getPresence(options?.name ?? DEFAULT_NAME, options?.includeSelf ?? false);
165
210
  return useSyncExternalStore(
166
211
  useCallback((onChange) => presence.subscribe(onChange), [presence]),
167
212
  () => presence.getPeers(),
@@ -180,6 +225,12 @@ function useClientId(options) {
180
225
  () => SERVER_CLIENT_ID
181
226
  );
182
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
+ }
183
234
 
184
235
  export {
185
236
  warnOnInitialMismatch,
@@ -188,7 +239,10 @@ export {
188
239
  configureStore,
189
240
  getStore,
190
241
  getLeader,
242
+ configureChannel,
243
+ getReducer,
191
244
  getChannel,
192
245
  usePeers,
193
- useClientId
246
+ useClientId,
247
+ usePresenceMetadata
194
248
  };
@@ -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
- let presence = presences.get(name);
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(name, presence);
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(),
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getSharedStore,
4
4
  usePeers
5
- } from "../chunk-LNJEF4DI.js";
5
+ } from "../chunk-75TICQ7E.js";
6
6
 
7
7
  // src/devtools/inspector.tsx
8
8
  import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";