use-everywhere 0.8.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 CHANGED
@@ -142,13 +142,13 @@ per-connection nonce, and source window.
142
142
  - Values must survive structured clone (no functions, DOM nodes); state lives
143
143
  as long as at least one context holds it — nothing is persisted.
144
144
  - Testing is first-class: inject a `MemoryHub` transport to simulate many tabs
145
- in one test. See the [testing guide](https://rxova.github.io/use-everywhere/guides/testing).
145
+ in one test. See the [testing guide](https://rxova.org/packages/use-everywhere/guides/testing).
146
146
 
147
147
  This package re-exports the full framework-agnostic surface of
148
148
  [`@use-everywhere/core`](https://www.npmjs.com/package/@use-everywhere/core),
149
149
  so you never need to install core directly.
150
150
 
151
- 📖 **[Documentation](https://rxova.github.io/use-everywhere/)** — mental
151
+ 📖 **[Documentation](https://rxova.org/packages/use-everywhere/)** — mental
152
152
  model, how sync works, security model, recipes, and generated API reference.
153
153
  Source and demo app: [github.com/rxova/use-everywhere](https://github.com/rxova/use-everywhere)
154
154
 
@@ -18,10 +18,17 @@ try {
18
18
  } catch {
19
19
  }
20
20
  var warned = /* @__PURE__ */ new Set();
21
- function devWarn(message) {
22
- if (!inDev || warned.has(message)) return;
23
- warned.add(message);
24
- console.warn(message);
21
+ var DOCS = "https://rxova.org/packages/use-everywhere/errors";
22
+ function diagnostic(code, message) {
23
+ return `[use-everywhere] ${code}: ${message}
24
+ \u2192 ${DOCS}/#${code.toLowerCase()}`;
25
+ }
26
+ function devWarn(code, message) {
27
+ if (!inDev) return;
28
+ const line = diagnostic(code, message);
29
+ if (warned.has(line)) return;
30
+ warned.add(line);
31
+ console.warn(line);
25
32
  }
26
33
  var seenInitials = /* @__PURE__ */ new Map();
27
34
  function warnOnInitialMismatch(storeName, key, initial) {
@@ -35,7 +42,8 @@ function warnOnInitialMismatch(storeName, key, initial) {
35
42
  const comparable = (v) => v === null || typeof v !== "object";
36
43
  if (comparable(first) && comparable(initial) && !Object.is(first, initial)) {
37
44
  devWarn(
38
- `[use-everywhere] useSharedState('${key}') was called with different initial values (${String(first)} and ${String(initial)}). The first registration wins, so the second is ignored. Define the default once \u2014 defineStore, or a shared constant.`
45
+ "UE2001",
46
+ `useSharedState('${key}') was called with different initial values (${String(first)} and ${String(initial)}). The first registration wins, so the second is ignored. Define the default once \u2014 defineStore, or a shared constant.`
39
47
  );
40
48
  }
41
49
  }
@@ -118,7 +126,8 @@ function configureStore(name, scope, options) {
118
126
  if (configSignature(storeConfig.get(key)) === configSignature(options)) return;
119
127
  if (process.env.NODE_ENV !== "production") {
120
128
  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.`
129
+ "UE2002",
130
+ `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
131
  );
123
132
  }
124
133
  return;
@@ -162,7 +171,8 @@ function warnOnLeaderOptionConflict(name, options) {
162
171
  if (requested !== void 0 && requested !== first?.[key]) {
163
172
  if (process.env.NODE_ENV !== "production") {
164
173
  devWarn(
165
- `[use-everywhere] leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
174
+ "UE2003",
175
+ `leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
166
176
  );
167
177
  }
168
178
  }
@@ -176,7 +186,8 @@ function configureChannel(name, options) {
176
186
  if (before.join() === after.join()) return;
177
187
  if (process.env.NODE_ENV !== "production") {
178
188
  devWarn(
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.`
189
+ "UE2004",
190
+ `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.`
180
191
  );
181
192
  }
182
193
  return;
@@ -162,6 +162,13 @@ var STYLES = `
162
162
  .ue-ins__row { display: flex; gap: 8px; padding: 1px 0; }
163
163
  .ue-ins__k { color: #79c0ff; flex: none; }
164
164
  .ue-ins__v { color: #e6edf3; overflow-wrap: anywhere; }
165
+ button.ue-ins__v {
166
+ background: none;
167
+ border: 0;
168
+ padding: 0;
169
+ font: inherit;
170
+ text-align: left;
171
+ }
165
172
  .ue-ins__ver { color: #6e7681; margin-left: auto; flex: none; }
166
173
  .ue-ins__empty { color: #6e7681; }
167
174
 
@@ -172,6 +179,43 @@ var STYLES = `
172
179
  .ue-ins__dir--in { color: #3fb950; }
173
180
  .ue-ins__scope { color: #e6edf3; }
174
181
  .ue-ins__from { color: #6e7681; margin-left: auto; }
182
+
183
+ .ue-ins__tools { display: flex; gap: 6px; align-items: center; margin-bottom: 5px; }
184
+ .ue-ins__btn {
185
+ background: #21262d;
186
+ border: 1px solid #30363d;
187
+ border-radius: 5px;
188
+ color: #c9d1d9;
189
+ font: inherit;
190
+ font-size: 10px;
191
+ padding: 1px 7px;
192
+ cursor: pointer;
193
+ }
194
+ .ue-ins__btn[aria-pressed='true'] { background: #1f6feb; border-color: #1f6feb; color: #fff; }
195
+ .ue-ins__filter {
196
+ flex: 1;
197
+ min-width: 0;
198
+ background: #0d1117;
199
+ border: 1px solid #30363d;
200
+ border-radius: 5px;
201
+ color: #c9d1d9;
202
+ font: inherit;
203
+ font-size: 10px;
204
+ padding: 1px 6px;
205
+ }
206
+ .ue-ins__edit {
207
+ flex: 1;
208
+ min-width: 0;
209
+ background: #0d1117;
210
+ border: 1px solid #1f6feb;
211
+ border-radius: 4px;
212
+ color: #e6edf3;
213
+ font: inherit;
214
+ padding: 0 4px;
215
+ }
216
+ .ue-ins__v--editable { cursor: text; text-decoration: underline dotted #30363d; }
217
+ .ue-ins__v--invalid { color: #f85149; }
218
+ .ue-ins__paused { color: #d29922; }
175
219
  `;
176
220
 
177
221
  // src/devtools/inspector.tsx
@@ -190,8 +234,15 @@ function Inspector({
190
234
  const [open, setOpen] = (0, import_react2.useState)(defaultOpen);
191
235
  const [wires, setWires] = (0, import_react2.useState)([]);
192
236
  const [crown, setCrown] = (0, import_react2.useState)(null);
237
+ const [paused, setPaused] = (0, import_react2.useState)(false);
238
+ const [filter, setFilter] = (0, import_react2.useState)("");
239
+ const [editing, setEditing] = (0, import_react2.useState)(null);
193
240
  const nextId = (0, import_react2.useRef)(0);
194
241
  const crownAt = (0, import_react2.useRef)(0);
242
+ const pausedRef = (0, import_react2.useRef)(false);
243
+ (0, import_react2.useEffect)(() => {
244
+ pausedRef.current = paused;
245
+ }, [paused]);
195
246
  const peers = usePeers({ name });
196
247
  const store = getSharedStore(name);
197
248
  const subscribe = (0, import_react2.useCallback)((onChange) => store.subscribe(onChange), [store]);
@@ -217,12 +268,14 @@ function Inspector({
217
268
  crownAt.current = Date.now();
218
269
  }
219
270
  }
271
+ if (pausedRef.current) return;
220
272
  setWires(
221
273
  (prev) => [
222
274
  ...prev.slice(-(limit - 1)),
223
275
  {
224
276
  id: nextId.current++,
225
277
  direction,
278
+ scope: wire.scope,
226
279
  label: wireLabel(wire),
227
280
  from: short(wire.clientId)
228
281
  }
@@ -247,6 +300,28 @@ function Inspector({
247
300
  }, [leaseMs]);
248
301
  const selfId = store.clientId;
249
302
  const entries = Object.entries(versions);
303
+ const needle = filter.trim().toLowerCase();
304
+ const shown = needle ? wires.filter(
305
+ (wire) => wire.label.toLowerCase().includes(needle) || wire.from.toLowerCase().includes(needle)
306
+ ) : wires;
307
+ const commit = (key, draft) => {
308
+ let value;
309
+ try {
310
+ value = JSON.parse(draft);
311
+ } catch {
312
+ return;
313
+ }
314
+ store.set(key, value);
315
+ setEditing(null);
316
+ };
317
+ const draftIsValid = (draft) => {
318
+ try {
319
+ JSON.parse(draft);
320
+ return true;
321
+ } catch {
322
+ return false;
323
+ }
324
+ };
250
325
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `ue-ins ue-ins--${position}`, "data-testid": "ue-inspector", children: [
251
326
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: STYLES }),
252
327
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -294,7 +369,31 @@ function Inspector({
294
369
  ] }),
295
370
  entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ue-ins__empty", children: "no keys yet" }) : entries.map(([key, version]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__row", children: [
296
371
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ue-ins__k", children: key }),
297
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ue-ins__v", children: JSON.stringify(snapshot[key]) }),
372
+ editing?.key === key ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
373
+ "input",
374
+ {
375
+ className: `ue-ins__edit${draftIsValid(editing.draft) ? "" : " ue-ins__v--invalid"}`,
376
+ value: editing.draft,
377
+ autoFocus: true,
378
+ "aria-label": `Value of ${key}`,
379
+ onChange: (event) => setEditing({ key, draft: event.target.value }),
380
+ onKeyDown: (event) => {
381
+ if (event.key === "Enter") commit(key, editing.draft);
382
+ if (event.key === "Escape") setEditing(null);
383
+ },
384
+ onBlur: () => setEditing(null),
385
+ "data-testid": `ue-edit-${key}`
386
+ }
387
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
388
+ "button",
389
+ {
390
+ type: "button",
391
+ className: "ue-ins__v ue-ins__v--editable",
392
+ onClick: () => setEditing({ key, draft: JSON.stringify(snapshot[key]) ?? "" }),
393
+ "data-testid": `ue-value-${key}`,
394
+ children: JSON.stringify(snapshot[key])
395
+ }
396
+ ),
298
397
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "ue-ins__ver", children: [
299
398
  version[0],
300
399
  "\xB7",
@@ -305,10 +404,46 @@ function Inspector({
305
404
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__section", children: [
306
405
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__h", children: [
307
406
  "Wires (",
308
- wires.length,
309
- ")"
407
+ shown.length,
408
+ shown.length === wires.length ? "" : ` of ${wires.length}`,
409
+ ")",
410
+ paused ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ue-ins__paused", children: " \xB7 paused" }) : null
411
+ ] }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__tools", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
414
+ "button",
415
+ {
416
+ type: "button",
417
+ className: "ue-ins__btn",
418
+ "aria-pressed": paused,
419
+ onClick: () => setPaused((value) => !value),
420
+ "data-testid": "ue-pause",
421
+ children: paused ? "resume" : "pause"
422
+ }
423
+ ),
424
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
425
+ "button",
426
+ {
427
+ type: "button",
428
+ className: "ue-ins__btn",
429
+ onClick: () => setWires([]),
430
+ "data-testid": "ue-clear",
431
+ children: "clear"
432
+ }
433
+ ),
434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
435
+ "input",
436
+ {
437
+ className: "ue-ins__filter",
438
+ value: filter,
439
+ placeholder: "filter",
440
+ "aria-label": "Filter wires",
441
+ onChange: (event) => setFilter(event.target.value),
442
+ "data-testid": "ue-filter"
443
+ }
444
+ )
310
445
  ] }),
311
- wires.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ue-ins__empty", children: "nothing yet" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ue-ins__log", children: wires.map((wire) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__wire", children: [
446
+ shown.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ue-ins__empty", children: wires.length === 0 ? "nothing yet" : "no matches" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ue-ins__log", children: shown.map((wire) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ue-ins__wire", children: [
312
447
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `ue-ins__dir ue-ins__dir--${wire.direction}`, children: wire.direction === "out" ? "\u2192" : "\u2190" }),
313
448
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ue-ins__scope", children: wire.label }),
314
449
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ue-ins__from", children: wire.from })
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getSharedStore,
4
4
  usePeers
5
- } from "../chunk-75TICQ7E.js";
5
+ } from "../chunk-R7PBPLF7.js";
6
6
 
7
7
  // src/devtools/inspector.tsx
8
8
  import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
@@ -57,6 +57,13 @@ var STYLES = `
57
57
  .ue-ins__row { display: flex; gap: 8px; padding: 1px 0; }
58
58
  .ue-ins__k { color: #79c0ff; flex: none; }
59
59
  .ue-ins__v { color: #e6edf3; overflow-wrap: anywhere; }
60
+ button.ue-ins__v {
61
+ background: none;
62
+ border: 0;
63
+ padding: 0;
64
+ font: inherit;
65
+ text-align: left;
66
+ }
60
67
  .ue-ins__ver { color: #6e7681; margin-left: auto; flex: none; }
61
68
  .ue-ins__empty { color: #6e7681; }
62
69
 
@@ -67,6 +74,43 @@ var STYLES = `
67
74
  .ue-ins__dir--in { color: #3fb950; }
68
75
  .ue-ins__scope { color: #e6edf3; }
69
76
  .ue-ins__from { color: #6e7681; margin-left: auto; }
77
+
78
+ .ue-ins__tools { display: flex; gap: 6px; align-items: center; margin-bottom: 5px; }
79
+ .ue-ins__btn {
80
+ background: #21262d;
81
+ border: 1px solid #30363d;
82
+ border-radius: 5px;
83
+ color: #c9d1d9;
84
+ font: inherit;
85
+ font-size: 10px;
86
+ padding: 1px 7px;
87
+ cursor: pointer;
88
+ }
89
+ .ue-ins__btn[aria-pressed='true'] { background: #1f6feb; border-color: #1f6feb; color: #fff; }
90
+ .ue-ins__filter {
91
+ flex: 1;
92
+ min-width: 0;
93
+ background: #0d1117;
94
+ border: 1px solid #30363d;
95
+ border-radius: 5px;
96
+ color: #c9d1d9;
97
+ font: inherit;
98
+ font-size: 10px;
99
+ padding: 1px 6px;
100
+ }
101
+ .ue-ins__edit {
102
+ flex: 1;
103
+ min-width: 0;
104
+ background: #0d1117;
105
+ border: 1px solid #1f6feb;
106
+ border-radius: 4px;
107
+ color: #e6edf3;
108
+ font: inherit;
109
+ padding: 0 4px;
110
+ }
111
+ .ue-ins__v--editable { cursor: text; text-decoration: underline dotted #30363d; }
112
+ .ue-ins__v--invalid { color: #f85149; }
113
+ .ue-ins__paused { color: #d29922; }
70
114
  `;
71
115
 
72
116
  // src/devtools/inspector.tsx
@@ -85,8 +129,15 @@ function Inspector({
85
129
  const [open, setOpen] = useState(defaultOpen);
86
130
  const [wires, setWires] = useState([]);
87
131
  const [crown, setCrown] = useState(null);
132
+ const [paused, setPaused] = useState(false);
133
+ const [filter, setFilter] = useState("");
134
+ const [editing, setEditing] = useState(null);
88
135
  const nextId = useRef(0);
89
136
  const crownAt = useRef(0);
137
+ const pausedRef = useRef(false);
138
+ useEffect(() => {
139
+ pausedRef.current = paused;
140
+ }, [paused]);
90
141
  const peers = usePeers({ name });
91
142
  const store = getSharedStore(name);
92
143
  const subscribe = useCallback((onChange) => store.subscribe(onChange), [store]);
@@ -112,12 +163,14 @@ function Inspector({
112
163
  crownAt.current = Date.now();
113
164
  }
114
165
  }
166
+ if (pausedRef.current) return;
115
167
  setWires(
116
168
  (prev) => [
117
169
  ...prev.slice(-(limit - 1)),
118
170
  {
119
171
  id: nextId.current++,
120
172
  direction,
173
+ scope: wire.scope,
121
174
  label: wireLabel(wire),
122
175
  from: short(wire.clientId)
123
176
  }
@@ -142,6 +195,28 @@ function Inspector({
142
195
  }, [leaseMs]);
143
196
  const selfId = store.clientId;
144
197
  const entries = Object.entries(versions);
198
+ const needle = filter.trim().toLowerCase();
199
+ const shown = needle ? wires.filter(
200
+ (wire) => wire.label.toLowerCase().includes(needle) || wire.from.toLowerCase().includes(needle)
201
+ ) : wires;
202
+ const commit = (key, draft) => {
203
+ let value;
204
+ try {
205
+ value = JSON.parse(draft);
206
+ } catch {
207
+ return;
208
+ }
209
+ store.set(key, value);
210
+ setEditing(null);
211
+ };
212
+ const draftIsValid = (draft) => {
213
+ try {
214
+ JSON.parse(draft);
215
+ return true;
216
+ } catch {
217
+ return false;
218
+ }
219
+ };
145
220
  return /* @__PURE__ */ jsxs("div", { className: `ue-ins ue-ins--${position}`, "data-testid": "ue-inspector", children: [
146
221
  /* @__PURE__ */ jsx("style", { children: STYLES }),
147
222
  /* @__PURE__ */ jsxs(
@@ -189,7 +264,31 @@ function Inspector({
189
264
  ] }),
190
265
  entries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "ue-ins__empty", children: "no keys yet" }) : entries.map(([key, version]) => /* @__PURE__ */ jsxs("div", { className: "ue-ins__row", children: [
191
266
  /* @__PURE__ */ jsx("span", { className: "ue-ins__k", children: key }),
192
- /* @__PURE__ */ jsx("span", { className: "ue-ins__v", children: JSON.stringify(snapshot[key]) }),
267
+ editing?.key === key ? /* @__PURE__ */ jsx(
268
+ "input",
269
+ {
270
+ className: `ue-ins__edit${draftIsValid(editing.draft) ? "" : " ue-ins__v--invalid"}`,
271
+ value: editing.draft,
272
+ autoFocus: true,
273
+ "aria-label": `Value of ${key}`,
274
+ onChange: (event) => setEditing({ key, draft: event.target.value }),
275
+ onKeyDown: (event) => {
276
+ if (event.key === "Enter") commit(key, editing.draft);
277
+ if (event.key === "Escape") setEditing(null);
278
+ },
279
+ onBlur: () => setEditing(null),
280
+ "data-testid": `ue-edit-${key}`
281
+ }
282
+ ) : /* @__PURE__ */ jsx(
283
+ "button",
284
+ {
285
+ type: "button",
286
+ className: "ue-ins__v ue-ins__v--editable",
287
+ onClick: () => setEditing({ key, draft: JSON.stringify(snapshot[key]) ?? "" }),
288
+ "data-testid": `ue-value-${key}`,
289
+ children: JSON.stringify(snapshot[key])
290
+ }
291
+ ),
193
292
  /* @__PURE__ */ jsxs("span", { className: "ue-ins__ver", children: [
194
293
  version[0],
195
294
  "\xB7",
@@ -200,10 +299,46 @@ function Inspector({
200
299
  /* @__PURE__ */ jsxs("div", { className: "ue-ins__section", children: [
201
300
  /* @__PURE__ */ jsxs("div", { className: "ue-ins__h", children: [
202
301
  "Wires (",
203
- wires.length,
204
- ")"
302
+ shown.length,
303
+ shown.length === wires.length ? "" : ` of ${wires.length}`,
304
+ ")",
305
+ paused ? /* @__PURE__ */ jsx("span", { className: "ue-ins__paused", children: " \xB7 paused" }) : null
306
+ ] }),
307
+ /* @__PURE__ */ jsxs("div", { className: "ue-ins__tools", children: [
308
+ /* @__PURE__ */ jsx(
309
+ "button",
310
+ {
311
+ type: "button",
312
+ className: "ue-ins__btn",
313
+ "aria-pressed": paused,
314
+ onClick: () => setPaused((value) => !value),
315
+ "data-testid": "ue-pause",
316
+ children: paused ? "resume" : "pause"
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsx(
320
+ "button",
321
+ {
322
+ type: "button",
323
+ className: "ue-ins__btn",
324
+ onClick: () => setWires([]),
325
+ "data-testid": "ue-clear",
326
+ children: "clear"
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsx(
330
+ "input",
331
+ {
332
+ className: "ue-ins__filter",
333
+ value: filter,
334
+ placeholder: "filter",
335
+ "aria-label": "Filter wires",
336
+ onChange: (event) => setFilter(event.target.value),
337
+ "data-testid": "ue-filter"
338
+ }
339
+ )
205
340
  ] }),
206
- wires.length === 0 ? /* @__PURE__ */ jsx("div", { className: "ue-ins__empty", children: "nothing yet" }) : /* @__PURE__ */ jsx("div", { className: "ue-ins__log", children: wires.map((wire) => /* @__PURE__ */ jsxs("div", { className: "ue-ins__wire", children: [
341
+ shown.length === 0 ? /* @__PURE__ */ jsx("div", { className: "ue-ins__empty", children: wires.length === 0 ? "nothing yet" : "no matches" }) : /* @__PURE__ */ jsx("div", { className: "ue-ins__log", children: shown.map((wire) => /* @__PURE__ */ jsxs("div", { className: "ue-ins__wire", children: [
207
342
  /* @__PURE__ */ jsx("span", { className: `ue-ins__dir ue-ins__dir--${wire.direction}`, children: wire.direction === "out" ? "\u2192" : "\u2190" }),
208
343
  /* @__PURE__ */ jsx("span", { className: "ue-ins__scope", children: wire.label }),
209
344
  /* @__PURE__ */ jsx("span", { className: "ue-ins__from", children: wire.from })
package/dist/index.cjs CHANGED
@@ -85,10 +85,17 @@ try {
85
85
  } catch {
86
86
  }
87
87
  var warned = /* @__PURE__ */ new Set();
88
- function devWarn(message) {
89
- if (!inDev || warned.has(message)) return;
90
- warned.add(message);
91
- console.warn(message);
88
+ var DOCS = "https://rxova.org/packages/use-everywhere/errors";
89
+ function diagnostic(code, message) {
90
+ return `[use-everywhere] ${code}: ${message}
91
+ \u2192 ${DOCS}/#${code.toLowerCase()}`;
92
+ }
93
+ function devWarn(code, message) {
94
+ if (!inDev) return;
95
+ const line = diagnostic(code, message);
96
+ if (warned.has(line)) return;
97
+ warned.add(line);
98
+ console.warn(line);
92
99
  }
93
100
  var seenInitials = /* @__PURE__ */ new Map();
94
101
  function warnOnInitialMismatch(storeName, key, initial) {
@@ -102,7 +109,8 @@ function warnOnInitialMismatch(storeName, key, initial) {
102
109
  const comparable = (v) => v === null || typeof v !== "object";
103
110
  if (comparable(first) && comparable(initial) && !Object.is(first, initial)) {
104
111
  devWarn(
105
- `[use-everywhere] useSharedState('${key}') was called with different initial values (${String(first)} and ${String(initial)}). The first registration wins, so the second is ignored. Define the default once \u2014 defineStore, or a shared constant.`
112
+ "UE2001",
113
+ `useSharedState('${key}') was called with different initial values (${String(first)} and ${String(initial)}). The first registration wins, so the second is ignored. Define the default once \u2014 defineStore, or a shared constant.`
106
114
  );
107
115
  }
108
116
  }
@@ -188,7 +196,8 @@ function configureStore(name, scope, options) {
188
196
  if (configSignature(storeConfig.get(key)) === configSignature(options)) return;
189
197
  if (process.env.NODE_ENV !== "production") {
190
198
  devWarn(
191
- `[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.`
199
+ "UE2002",
200
+ `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.`
192
201
  );
193
202
  }
194
203
  return;
@@ -232,7 +241,8 @@ function warnOnLeaderOptionConflict(name, options) {
232
241
  if (requested !== void 0 && requested !== first?.[key]) {
233
242
  if (process.env.NODE_ENV !== "production") {
234
243
  devWarn(
235
- `[use-everywhere] leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
244
+ "UE2003",
245
+ `leader "${name}": ${key} ignored \u2014 the first useLeader/getLeader call fixes the election timings for this tab.`
236
246
  );
237
247
  }
238
248
  }
@@ -246,7 +256,8 @@ function configureChannel(name, options) {
246
256
  if (before.join() === after.join()) return;
247
257
  if (process.env.NODE_ENV !== "production") {
248
258
  devWarn(
249
- `[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.`
259
+ "UE2004",
260
+ `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.`
250
261
  );
251
262
  }
252
263
  return;
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  usePeers,
13
13
  usePresenceMetadata,
14
14
  warnOnInitialMismatch
15
- } from "./chunk-75TICQ7E.js";
15
+ } from "./chunk-R7PBPLF7.js";
16
16
 
17
17
  // src/use-shared-state.ts
18
18
  import { useCallback, useSyncExternalStore } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-everywhere",
3
- "version": "0.8.0",
3
+ "version": "0.9.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>",
@@ -123,7 +123,7 @@
123
123
  "name": "Inspector (devtools subpath)",
124
124
  "path": "dist/devtools/index.js",
125
125
  "import": "{ Inspector }",
126
- "limit": "6.32 kB"
126
+ "limit": "6.70 kB"
127
127
  },
128
128
  {
129
129
  "name": "MemoryHub (testing subpath)",
@@ -133,7 +133,7 @@
133
133
  }
134
134
  ],
135
135
  "dependencies": {
136
- "@use-everywhere/core": "0.8.0"
136
+ "@use-everywhere/core": "0.9.0"
137
137
  },
138
138
  "peerDependencies": {
139
139
  "react": ">=18"