@buoy-gg/zustand 7.0.33 → 7.0.35

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.
Files changed (45) hide show
  1. package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -1
  2. package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +13 -107
  3. package/lib/commonjs/zustand/components/ZustandEventFilterView.js +24 -288
  4. package/lib/commonjs/zustand/components/ZustandModal.js +20 -85
  5. package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +8 -61
  6. package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +5 -5
  7. package/lib/commonjs/zustand/components/ZustandStateInfoView.js +8 -52
  8. package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +2 -2
  9. package/lib/commonjs/zustand/components/ZustandStoreDetailContent.js +4 -4
  10. package/lib/commonjs/zustand/components/ZustandStoreEditorScreen.js +3 -3
  11. package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +33 -53
  12. package/lib/commonjs/zustand/sync/zustandSyncAdapter.js +24 -78
  13. package/lib/commonjs/zustand/utils/snapshotProvider.js +17 -60
  14. package/lib/commonjs/zustand/utils/zustandStateStore.js +37 -223
  15. package/lib/module/zustand/components/ZustandActionButton.js +2 -2
  16. package/lib/module/zustand/components/ZustandDetailViewToggle.js +15 -109
  17. package/lib/module/zustand/components/ZustandEventFilterView.js +28 -292
  18. package/lib/module/zustand/components/ZustandModal.js +22 -87
  19. package/lib/module/zustand/components/ZustandStateChangeItem.js +10 -63
  20. package/lib/module/zustand/components/ZustandStateDetailContent.js +6 -6
  21. package/lib/module/zustand/components/ZustandStateInfoView.js +7 -51
  22. package/lib/module/zustand/components/ZustandStoreBrowser.js +3 -3
  23. package/lib/module/zustand/components/ZustandStoreDetailContent.js +5 -5
  24. package/lib/module/zustand/components/ZustandStoreEditorScreen.js +4 -4
  25. package/lib/module/zustand/hooks/useZustandStateChanges.js +34 -54
  26. package/lib/module/zustand/sync/zustandSyncAdapter.js +25 -79
  27. package/lib/module/zustand/utils/snapshotProvider.js +18 -61
  28. package/lib/module/zustand/utils/zustandStateStore.js +37 -223
  29. package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts +2 -1
  30. package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +1 -1
  31. package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts +6 -0
  32. package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +1 -1
  33. package/lib/typescript/zustand/components/ZustandModal.d.ts.map +1 -1
  34. package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +1 -1
  35. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +1 -1
  36. package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +1 -1
  37. package/lib/typescript/zustand/components/ZustandStoreDetailContent.d.ts.map +1 -1
  38. package/lib/typescript/zustand/components/ZustandStoreEditorScreen.d.ts.map +1 -1
  39. package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts +2 -1
  40. package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +1 -1
  41. package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts +103 -117
  42. package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts.map +1 -1
  43. package/lib/typescript/zustand/utils/zustandStateStore.d.ts +6 -26
  44. package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +1 -1
  45. package/package.json +3 -3
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.ZustandStateChangeItem = void 0;
7
7
  var _react = require("react");
8
- var _reactNative = require("react-native");
9
8
  var _sharedUi = require("@buoy-gg/shared-ui");
10
9
  var _zustandStateStore = require("../utils/zustandStateStore");
11
10
  var _jsxRuntime = require("react/jsx-runtime");
@@ -103,68 +102,16 @@ const ZustandStateChangeItem = exports.ZustandStateChangeItem = /*#__PURE__*/(0,
103
102
  const sublabel = getSublabel(change);
104
103
  const primaryText = getPrimaryText(change);
105
104
  const badgeText = getBadgeText(change);
106
-
107
- // Always a custom badge, even when there is no icon to hang off it. The
108
- // alternative — letting CompactRow draw `badgeText` itself — leaves the one
109
- // row whose badge matters most (a set that changed NOTHING) as the only row
110
- // in the list with no handle on its badge.
111
- const customBadge = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
112
- style: styles.badgeContainer,
113
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
114
- style: [styles.badgeText, {
115
- color: statusColor
116
- }],
117
- testID: testID ? `${testID}-badge` : undefined,
118
- children: badgeText
119
- }), change.isSlowUpdate ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
120
- style: styles.slowBadge,
121
- testID: testID ? `${testID}-slow` : undefined,
122
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Clock, {
123
- size: 12,
124
- color: _sharedUi.buoyColors.error
125
- })
126
- }) : change.hasStateChange ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
127
- style: styles.changeBadge,
128
- testID: testID ? `${testID}-change` : undefined,
129
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
130
- size: 12,
131
- color: _sharedUi.buoyColors.warning
132
- })
133
- }) : null]
134
- });
135
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CompactRow, {
136
- statusDotColor: statusColor,
105
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChangeRow, {
106
+ statusColor: statusColor,
137
107
  statusLabel: statusLabel,
138
- statusSublabel: sublabel,
108
+ sublabel: sublabel,
139
109
  primaryText: primaryText,
140
- bottomRightText: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RelativeTime, {
141
- timestamp: change.timestamp
142
- }),
143
- customBadge: customBadge,
144
- badgeColor: statusColor,
145
- showChevron: true,
110
+ timestamp: change.timestamp,
111
+ badgeText: badgeText,
112
+ changed: change.hasStateChange,
113
+ slow: change.isSlowUpdate,
146
114
  onPress: () => onPress(change),
147
- testID: testID,
148
- statusTestID: testID ? `${testID}-status` : undefined,
149
- statusSublabelTestID: testID ? `${testID}-sublabel` : undefined,
150
- primaryTestID: testID ? `${testID}-keys` : undefined
115
+ testID: testID
151
116
  });
152
- });
153
- const styles = _reactNative.StyleSheet.create({
154
- badgeContainer: {
155
- flexDirection: "row",
156
- alignItems: "center",
157
- gap: 4
158
- },
159
- badgeText: {
160
- fontSize: 11,
161
- fontWeight: "600",
162
- fontFamily: "monospace"
163
- },
164
- changeBadge: {
165
- paddingHorizontal: 4
166
- },
167
- slowBadge: {
168
- paddingHorizontal: 4
169
- }
170
117
  });
@@ -301,7 +301,7 @@ const styles = _reactNative.StyleSheet.create({
301
301
  valueLabel: {
302
302
  fontSize: 10,
303
303
  color: _sharedUi.night.color.textSecondary,
304
- fontFamily: "monospace",
304
+ fontFamily: _sharedUi.monoFont,
305
305
  letterSpacing: 0.5,
306
306
  fontWeight: "600"
307
307
  },
@@ -318,7 +318,7 @@ const styles = _reactNative.StyleSheet.create({
318
318
  changeText: {
319
319
  fontSize: 9,
320
320
  fontWeight: "700",
321
- fontFamily: "monospace",
321
+ fontFamily: _sharedUi.monoFont,
322
322
  letterSpacing: 0.3
323
323
  },
324
324
  typeBadge: {
@@ -331,7 +331,7 @@ const styles = _reactNative.StyleSheet.create({
331
331
  fontSize: 9,
332
332
  fontWeight: "600",
333
333
  color: _sharedUi.night.color.textTertiary,
334
- fontFamily: "monospace"
334
+ fontFamily: _sharedUi.monoFont
335
335
  },
336
336
  valueBox: {
337
337
  // Inset code box — darker than the card, the night track color.
@@ -344,7 +344,7 @@ const styles = _reactNative.StyleSheet.create({
344
344
  valueText: {
345
345
  fontSize: 12,
346
346
  color: _sharedUi.night.color.text,
347
- fontFamily: "monospace",
347
+ fontFamily: _sharedUi.monoFont,
348
348
  lineHeight: 18
349
349
  },
350
350
  diffContainer: {
@@ -362,7 +362,7 @@ const styles = _reactNative.StyleSheet.create({
362
362
  changeTextSmall: {
363
363
  fontSize: 8,
364
364
  fontWeight: "700",
365
- fontFamily: "monospace"
365
+ fontFamily: _sharedUi.monoFont
366
366
  },
367
367
  dataViewerContainer: {
368
368
  marginTop: -12,
@@ -79,29 +79,6 @@ function SectionHeader({
79
79
  }), badge]
80
80
  });
81
81
  }
82
- function InfoRow({
83
- label,
84
- value,
85
- valueColor,
86
- mono = false,
87
- testID
88
- }) {
89
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
90
- style: styles.infoRow,
91
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
92
- style: styles.infoLabel,
93
- children: label
94
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
95
- testID: testID,
96
- style: [styles.infoValue, mono && styles.infoValueMono, valueColor ? {
97
- color: valueColor
98
- } : undefined],
99
- numberOfLines: 1,
100
- ellipsizeMode: "middle",
101
- children: value
102
- })]
103
- });
104
- }
105
82
  function formatTime(timestamp) {
106
83
  const date = new Date(timestamp);
107
84
  const hours = date.getHours().toString().padStart(2, "0");
@@ -157,13 +134,13 @@ const ZustandStateInfoView = exports.ZustandStateInfoView = /*#__PURE__*/(0, _re
157
134
  title: "CHANGE INFO"
158
135
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
159
136
  style: styles.cardContent,
160
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
137
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InfoRow, {
161
138
  label: "Store",
162
139
  value: change.storeName,
163
140
  mono: true,
164
141
  valueColor: storeColor,
165
142
  testID: `${_testIDs.ZUSTAND_TEST_ID}-info-store`
166
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(InfoRow, {
143
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InfoRow, {
167
144
  label: "Time",
168
145
  value: formatTime(change.timestamp),
169
146
  mono: true,
@@ -360,33 +337,12 @@ const styles = _reactNative.StyleSheet.create({
360
337
  fontWeight: "600",
361
338
  letterSpacing: 0.5,
362
339
  color: _sharedUi.night.color.accent,
363
- fontFamily: "monospace"
340
+ fontFamily: _sharedUi.monoFont
364
341
  },
365
342
  cardContent: {
366
343
  padding: 14,
367
344
  gap: 10
368
345
  },
369
- infoRow: {
370
- flexDirection: "row",
371
- alignItems: "center",
372
- justifyContent: "space-between"
373
- },
374
- infoLabel: {
375
- fontSize: 11,
376
- color: _sharedUi.night.color.textTertiary,
377
- fontWeight: "500"
378
- },
379
- infoValue: {
380
- fontSize: 11,
381
- color: _sharedUi.night.color.text,
382
- fontWeight: "500",
383
- flex: 1,
384
- textAlign: "right",
385
- marginLeft: 12
386
- },
387
- infoValueMono: {
388
- fontFamily: "monospace"
389
- },
390
346
  badgeRow: {
391
347
  flexDirection: "row",
392
348
  flexWrap: "wrap",
@@ -419,7 +375,7 @@ const styles = _reactNative.StyleSheet.create({
419
375
  fontSize: 9,
420
376
  fontWeight: "600",
421
377
  color: _sharedUi.night.color.textSecondary,
422
- fontFamily: "monospace"
378
+ fontFamily: _sharedUi.monoFont
423
379
  },
424
380
  durationTextSlow: {
425
381
  color: _sharedUi.buoyColors.error
@@ -481,7 +437,7 @@ const styles = _reactNative.StyleSheet.create({
481
437
  fontSize: 11,
482
438
  fontWeight: "600",
483
439
  color: _sharedUi.night.color.accent,
484
- fontFamily: "monospace"
440
+ fontFamily: _sharedUi.monoFont
485
441
  },
486
442
  countBadge: {
487
443
  paddingHorizontal: 8,
@@ -493,7 +449,7 @@ const styles = _reactNative.StyleSheet.create({
493
449
  fontSize: 9,
494
450
  fontWeight: "700",
495
451
  color: _sharedUi.night.color.accent,
496
- fontFamily: "monospace"
452
+ fontFamily: _sharedUi.monoFont
497
453
  },
498
454
  typeBadge: {
499
455
  paddingHorizontal: 6,
@@ -505,7 +461,7 @@ const styles = _reactNative.StyleSheet.create({
505
461
  fontSize: 8,
506
462
  fontWeight: "600",
507
463
  color: _sharedUi.night.color.textTertiary,
508
- fontFamily: "monospace"
464
+ fontFamily: _sharedUi.monoFont
509
465
  },
510
466
  dataViewerContainer: {
511
467
  // Inset display panel — darker than the card, the night track color.
@@ -515,7 +471,7 @@ const styles = _reactNative.StyleSheet.create({
515
471
  updaterText: {
516
472
  fontSize: 12,
517
473
  color: _sharedUi.night.color.text,
518
- fontFamily: "monospace"
474
+ fontFamily: _sharedUi.monoFont
519
475
  },
520
476
  updaterSubtext: {
521
477
  fontSize: 11,
@@ -170,7 +170,7 @@ const styles = _reactNative.StyleSheet.create({
170
170
  absCountText: {
171
171
  fontSize: 9,
172
172
  fontWeight: "700",
173
- fontFamily: "monospace"
173
+ fontFamily: _sharedUi.monoFont
174
174
  },
175
175
  scrollContent: {
176
176
  paddingTop: 8,
@@ -199,6 +199,6 @@ const styles = _reactNative.StyleSheet.create({
199
199
  fontSize: 10,
200
200
  fontWeight: "700",
201
201
  color: _sharedUi.night.color.accent,
202
- fontFamily: "monospace"
202
+ fontFamily: _sharedUi.monoFont
203
203
  }
204
204
  });
@@ -396,7 +396,7 @@ const styles = _reactNative.StyleSheet.create({
396
396
  color: _sharedUi.night.color.accent,
397
397
  letterSpacing: 0.5,
398
398
  textTransform: "uppercase",
399
- fontFamily: "monospace"
399
+ fontFamily: _sharedUi.monoFont
400
400
  },
401
401
  rawButton: {
402
402
  flexDirection: "row",
@@ -443,7 +443,7 @@ const styles = _reactNative.StyleSheet.create({
443
443
  borderBottomColor: _sharedUi.night.color.border,
444
444
  letterSpacing: 0.5,
445
445
  textTransform: "uppercase",
446
- fontFamily: "monospace"
446
+ fontFamily: _sharedUi.monoFont
447
447
  },
448
448
  infoBody: {
449
449
  padding: 12,
@@ -459,13 +459,13 @@ const styles = _reactNative.StyleSheet.create({
459
459
  inlineLinkText: {
460
460
  fontSize: 10,
461
461
  fontWeight: "600",
462
- fontFamily: "monospace"
462
+ fontFamily: _sharedUi.monoFont
463
463
  },
464
464
  actionsText: {
465
465
  flex: 1,
466
466
  color: _sharedUi.night.color.textSecondary,
467
467
  fontSize: 10,
468
- fontFamily: "monospace",
468
+ fontFamily: _sharedUi.monoFont,
469
469
  lineHeight: 15
470
470
  }
471
471
  });
@@ -301,7 +301,7 @@ const styles = _reactNative.StyleSheet.create({
301
301
  color: _sharedUi.night.color.text,
302
302
  fontSize: 13,
303
303
  fontWeight: "600",
304
- fontFamily: "monospace"
304
+ fontFamily: _sharedUi.monoFont
305
305
  },
306
306
  headerState: {
307
307
  color: _sharedUi.night.color.textTertiary,
@@ -320,7 +320,7 @@ const styles = _reactNative.StyleSheet.create({
320
320
  metaText: {
321
321
  color: _sharedUi.night.color.textTertiary,
322
322
  fontSize: 11,
323
- fontFamily: "monospace"
323
+ fontFamily: _sharedUi.monoFont
324
324
  },
325
325
  treeScroll: {
326
326
  flex: 1
@@ -345,7 +345,7 @@ const styles = _reactNative.StyleSheet.create({
345
345
  error: {
346
346
  color: _sharedUi.night.color.danger,
347
347
  fontSize: 11,
348
- fontFamily: "monospace",
348
+ fontFamily: _sharedUi.monoFont,
349
349
  paddingHorizontal: 12,
350
350
  paddingBottom: 6
351
351
  },
@@ -5,53 +5,41 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useZustandStateChanges = useZustandStateChanges;
7
7
  var _react = require("react");
8
+ var _sharedUi = require("@buoy-gg/shared-ui");
8
9
  var _zustandStateStore = require("../utils/zustandStateStore");
9
10
  /**
10
11
  * Hook for consuming Zustand state changes from the store
11
12
  *
12
- * Mirrors useReduxActions.ts from @buoy-gg/redux
13
+ * The subscribe / filter / pause / clear plumbing is shared `useChangeLog`;
14
+ * this file owns the zustand-specific filter, stats and store registry.
13
15
  */
14
16
 
17
+ function filterChanges(changes, filter) {
18
+ let filtered = changes;
19
+ if (filter.searchText) {
20
+ const search = filter.searchText.toLowerCase();
21
+ filtered = filtered.filter(c => c.storeName.toLowerCase().includes(search) || c.partialPreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
22
+ }
23
+ if (filter.storeNames && filter.storeNames.length > 0) {
24
+ filtered = filtered.filter(c => filter.storeNames.includes(c.storeName));
25
+ }
26
+ if (filter.onlyWithChanges) {
27
+ filtered = filtered.filter(c => c.hasStateChange);
28
+ }
29
+ return filtered;
30
+ }
31
+ const readStores = () => _zustandStateStore.zustandStateStore.getStores();
32
+ const subscribeToStores = listener => _zustandStateStore.zustandStateStore.subscribeToStores(listener);
15
33
  function useZustandStateChanges() {
16
- const [stateChanges, setStateChanges] = (0, _react.useState)(() => _zustandStateStore.zustandStateStore.getStateChanges());
17
- const [stores, setStores] = (0, _react.useState)(() => _zustandStateStore.zustandStateStore.getStores());
18
- const [filter, setFilter] = (0, _react.useState)({});
19
- const [isEnabled, setIsEnabled] = (0, _react.useState)(() => _zustandStateStore.zustandStateStore.getEnabled());
20
-
21
- // Subscribe to state changes
22
- (0, _react.useEffect)(() => {
23
- const unsubChanges = _zustandStateStore.zustandStateStore.subscribe(newChanges => {
24
- setStateChanges(newChanges);
25
- });
26
- const unsubStores = _zustandStateStore.zustandStateStore.subscribeToStores(newStores => {
27
- setStores(newStores);
28
- });
29
- return () => {
30
- unsubChanges();
31
- unsubStores();
32
- };
33
- }, []);
34
-
35
- // Filter state changes — derive directly from state to avoid stale reads
36
- const filteredChanges = (0, _react.useMemo)(() => {
37
- let filtered = stateChanges;
38
- if (filter.searchText) {
39
- const search = filter.searchText.toLowerCase();
40
- filtered = filtered.filter(c => c.storeName.toLowerCase().includes(search) || c.partialPreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
41
- }
42
- if (filter.storeNames && filter.storeNames.length > 0) {
43
- filtered = filtered.filter(c => filter.storeNames.includes(c.storeName));
44
- }
45
- if (filter.onlyWithChanges) {
46
- filtered = filtered.filter(c => c.hasStateChange);
47
- }
48
- return filtered;
49
- }, [stateChanges, filter]);
34
+ const log = (0, _sharedUi.useChangeLog)(_zustandStateStore.zustandStateStore, {
35
+ filterItems: filterChanges
36
+ });
37
+ const stores = (0, _sharedUi.useSubscribedValue)(readStores, subscribeToStores);
50
38
 
51
39
  // Get stats — derive directly from state
52
40
  const stats = (0, _react.useMemo)(() => {
53
- const total = stateChanges.length;
54
- const withChanges = stateChanges.filter(c => c.hasStateChange).length;
41
+ const total = log.items.length;
42
+ const withChanges = log.items.filter(c => c.hasStateChange).length;
55
43
  return {
56
44
  totalChanges: total,
57
45
  changesWithStateChange: withChanges,
@@ -59,33 +47,25 @@ function useZustandStateChanges() {
59
47
  storeCount: stores.length,
60
48
  averageDuration: 0
61
49
  };
62
- }, [stateChanges, stores]);
50
+ }, [log.items, stores]);
63
51
 
64
52
  // Get unique store names
65
53
  const storeNames = (0, _react.useMemo)(() => {
66
54
  return _zustandStateStore.zustandStateStore.getUniqueStoreNames();
67
55
  }, [stores]);
68
- const clearChanges = (0, _react.useCallback)(() => {
69
- _zustandStateStore.zustandStateStore.clearStateChanges();
70
- }, []);
71
- const toggleCapture = (0, _react.useCallback)(() => {
72
- const newEnabled = !isEnabled;
73
- _zustandStateStore.zustandStateStore.setEnabled(newEnabled);
74
- setIsEnabled(newEnabled);
75
- }, [isEnabled]);
76
56
  const getChangeById = (0, _react.useCallback)(id => {
77
57
  return _zustandStateStore.zustandStateStore.getStateChangeById(id);
78
58
  }, []);
79
59
  return {
80
- stateChanges,
81
- filteredChanges,
82
- filter,
83
- setFilter,
60
+ stateChanges: log.items,
61
+ filteredChanges: log.filteredItems,
62
+ filter: log.filter,
63
+ setFilter: log.setFilter,
84
64
  stats,
85
65
  stores,
86
- clearChanges,
87
- isEnabled,
88
- toggleCapture,
66
+ clearChanges: log.clear,
67
+ isEnabled: log.isEnabled,
68
+ toggleCapture: log.toggleCapture,
89
69
  storeNames,
90
70
  getChangeById
91
71
  };
@@ -11,18 +11,10 @@ var _sharedUi = require("@buoy-gg/shared-ui");
11
11
  * Same hazard as redux: up to 200 changes × 2 full trees on every snapshot
12
12
  * freeze/OOM-killed large apps. Dashboards fetch trees via `getChangeDetail`.
13
13
  */
14
- const STATE_ON_DEVICE = exports.STATE_ON_DEVICE = Object.freeze({
15
- __buoyStateOnDevice: true,
16
- note: "State trees stay on the device — fetched on demand via getChangeDetail / getStoreState."
17
- });
14
+ const STATE_ON_DEVICE = exports.STATE_ON_DEVICE = (0, _sharedUi.onDeviceMarker)("State", "State trees stay on the device — fetched on demand via getChangeDetail / getStoreState.");
18
15
 
19
16
  /** Wire-form sentinel for an oversized `partial` (replace can carry a whole tree). */
20
- const PAYLOAD_ON_DEVICE = exports.PAYLOAD_ON_DEVICE = Object.freeze({
21
- __buoyPayloadOnDevice: true,
22
- note: "Partial stays on the device — fetched on demand via getChangeDetail."
23
- });
24
- const MAX_CHANGE_DETAIL_BYTES = 8 * 1024 * 1024;
25
- const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
17
+ const PAYLOAD_ON_DEVICE = exports.PAYLOAD_ON_DEVICE = (0, _sharedUi.onDeviceMarker)("Payload", "Partial stays on the device — fetched on demand via getChangeDetail.");
26
18
  /** Top-level keys of an object state, for the compact (no-values) view. */
27
19
  function topLevelKeys(state) {
28
20
  return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : undefined;
@@ -33,31 +25,17 @@ function topLevelKeys(state) {
33
25
  * change, so keying on identity is correct and self-invalidating — without it
34
26
  * every `partial` in the list is re-measured on all ~5 snapshots per second.
35
27
  */
36
- const changeCache = new WeakMap();
37
- function toWireChange(change) {
38
- const cached = changeCache.get(change);
39
- if (cached) return cached;
40
- const wire = {
41
- ...change,
42
- prevState: STATE_ON_DEVICE,
43
- nextState: STATE_ON_DEVICE,
44
- partial: toWirePartial(change.partial)
45
- };
46
- changeCache.set(change, wire);
47
- return wire;
48
- }
49
- function toWirePartial(value) {
50
- if (value === undefined) return value;
51
- return (0, _sharedUi.isOverWireBudget)(value, MAX_WIRE_PAYLOAD_BYTES) ? PAYLOAD_ON_DEVICE : value;
52
- }
53
- function toWireState(value) {
54
- return (0, _sharedUi.isOverWireBudget)(value, MAX_WIRE_PAYLOAD_BYTES) ? STATE_ON_DEVICE : value;
55
- }
28
+ const toWireChange = (0, _sharedUi.memoByIdentity)(change => ({
29
+ ...change,
30
+ prevState: STATE_ON_DEVICE,
31
+ nextState: STATE_ON_DEVICE,
32
+ partial: (0, _sharedUi.toWire)(change.partial, PAYLOAD_ON_DEVICE)
33
+ }));
34
+ const toWireState = value => (0, _sharedUi.toWire)(value, STATE_ON_DEVICE);
56
35
 
57
36
  /**
58
37
  * Sync adapter for the zustand tool, consumed by @buoy-gg/external-sync's
59
- * `useExternalSync` (structurally matches its ToolSyncAdapter interface so
60
- * this package doesn't need a dependency on it).
38
+ * `useExternalSync`.
61
39
  *
62
40
  * State changes are captured by the middleware (watchStores/buoyDevTools),
63
41
  * independent of whether a dashboard is watching — subscribing here only
@@ -72,7 +50,7 @@ function toWireState(value) {
72
50
  *
73
51
  * The `setState` action powers remote time-travel/reset from the dashboard.
74
52
  */
75
- const zustandSyncAdapter = exports.zustandSyncAdapter = {
53
+ const zustandSyncAdapter = exports.zustandSyncAdapter = (0, _sharedUi.createSyncAdapter)({
76
54
  version: 2,
77
55
  getSnapshot: () => ({
78
56
  changes: _zustandStateStore.zustandStateStore.getStateChanges().map(toWireChange),
@@ -81,14 +59,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
81
59
  currentState: toWireState(store.currentState)
82
60
  }))
83
61
  }),
84
- subscribe: onChange => {
85
- const unsubscribeChanges = _zustandStateStore.zustandStateStore.subscribe(onChange);
86
- const unsubscribeStores = _zustandStateStore.zustandStateStore.subscribeToStores(onChange);
87
- return () => {
88
- unsubscribeChanges();
89
- unsubscribeStores();
90
- };
91
- },
62
+ subscribe: [onChange => _zustandStateStore.zustandStateStore.subscribe(onChange), onChange => _zustandStateStore.zustandStateStore.subscribeToStores(onChange)],
92
63
  actions: {
93
64
  clearEvents: () => {
94
65
  _zustandStateStore.zustandStateStore.clearStateChanges();
@@ -98,32 +69,11 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
98
69
  * The per-snapshot stream stays tree-free; this is the explicit,
99
70
  * size-guarded channel for the detail pane.
100
71
  */
101
- getChangeDetail: params => {
102
- const {
103
- id
104
- } = params ?? {};
105
- if (!id) return {
106
- found: false,
107
- reason: "missing id"
108
- };
109
- const change = _zustandStateStore.zustandStateStore.getStateChangeById(id);
110
- if (!change) return {
111
- found: false,
112
- reason: "unknown id"
113
- };
114
- const cappedNote = {
115
- __buoyTruncated: true,
116
- note: `Exceeds the ${MAX_CHANGE_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
117
- };
118
- const cap = value => (0, _sharedUi.isOverWireBudget)(value, MAX_CHANGE_DETAIL_BYTES) ? cappedNote : value;
119
- return {
120
- found: true,
121
- id,
122
- prevState: cap(change.prevState),
123
- nextState: cap(change.nextState),
124
- partial: cap(change.partial)
125
- };
126
- },
72
+ getChangeDetail: (0, _sharedUi.detailByIdAction)(id => _zustandStateStore.zustandStateStore.getStateChangeById(id), (change, cap) => ({
73
+ prevState: cap(change.prevState),
74
+ nextState: cap(change.nextState),
75
+ partial: cap(change.partial)
76
+ })),
127
77
  /**
128
78
  * On-demand current state for one store. Wire-form snapshots replace
129
79
  * oversized / unserializable `currentState` with STATE_ON_DEVICE.
@@ -131,7 +81,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
131
81
  getStoreState: params => {
132
82
  const {
133
83
  storeName
134
- } = params ?? {};
84
+ } = (0, _sharedUi.readParams)(params);
135
85
  if (!storeName) return {
136
86
  found: false,
137
87
  reason: "missing storeName"
@@ -147,11 +97,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
147
97
  } catch {
148
98
  currentState = undefined;
149
99
  }
150
- const cappedNote = {
151
- __buoyTruncated: true,
152
- note: `Exceeds the ${MAX_CHANGE_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
153
- };
154
- if (!(0, _sharedUi.isOverWireBudget)(currentState, MAX_CHANGE_DETAIL_BYTES)) {
100
+ if (!(0, _sharedUi.isOverWireBudget)(currentState, _sharedUi.WIRE_DETAIL_LIMIT_BYTES)) {
155
101
  return {
156
102
  found: true,
157
103
  storeName,
@@ -161,7 +107,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
161
107
  return {
162
108
  found: true,
163
109
  storeName,
164
- currentState: (0, _sharedUi.isJsonSerializable)(currentState) ? cappedNote : STATE_ON_DEVICE
110
+ currentState: (0, _sharedUi.isJsonSerializable)(currentState) ? (0, _sharedUi.truncatedNote)(_sharedUi.WIRE_DETAIL_LIMIT_BYTES) : STATE_ON_DEVICE
165
111
  };
166
112
  },
167
113
  /**
@@ -174,7 +120,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
174
120
  const {
175
121
  includeValues = false,
176
122
  limit
177
- } = params ?? {};
123
+ } = (0, _sharedUi.readParams)(params);
178
124
  const all = _zustandStateStore.zustandStateStore.getStoreSnapshots();
179
125
  const capped = typeof limit === "number" && limit > 0 ? all.slice(0, limit) : all;
180
126
  const stores = capped.map(s => ({
@@ -201,7 +147,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
201
147
  storeName,
202
148
  state,
203
149
  replace
204
- } = params ?? {};
150
+ } = (0, _sharedUi.readParams)(params);
205
151
  if (!storeName) return {
206
152
  ok: false,
207
153
  error: "Missing storeName."
@@ -240,7 +186,7 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
240
186
  rehydrate: async params => {
241
187
  const {
242
188
  storeName
243
- } = params ?? {};
189
+ } = (0, _sharedUi.readParams)(params);
244
190
  if (!storeName) return {
245
191
  ok: false,
246
192
  error: "Missing storeName."
@@ -270,4 +216,4 @@ const zustandSyncAdapter = exports.zustandSyncAdapter = {
270
216
  };
271
217
  }
272
218
  }
273
- };
219
+ });