@buoy-gg/redux 7.0.17 → 7.0.19

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.
@@ -82,10 +82,11 @@ function ReduxActionDetailContent({
82
82
  actions,
83
83
  selectedIndex,
84
84
  onIndexChange,
85
- disableInternalFooter = false
85
+ disableInternalFooter = false,
86
+ initialView = "action"
86
87
  }) {
87
- // View state
88
- const [activeView, setActiveView] = (0, _react.useState)("action");
88
+ // View state (initialView seeds it once; taps own it afterwards)
89
+ const [activeView, setActiveView] = (0, _react.useState)(initialView);
89
90
  const [diffMode, setDiffMode] = (0, _react.useState)("tree");
90
91
  const totalActions = actions.length;
91
92
  const actionColor = getActionColor(action.type);
@@ -49,7 +49,9 @@ function ReduxModal({
49
49
  onMinimize,
50
50
  enableSharedModalDimensions = false,
51
51
  inline = false,
52
- onActionSelect
52
+ onActionSelect,
53
+ initialSelectedActionId,
54
+ initialDetailView
53
55
  }) {
54
56
  const isPro = (0, _license.useIsPro)();
55
57
  const [showProModal, setShowProModal] = (0, _react.useState)(false);
@@ -105,6 +107,19 @@ function ReduxModal({
105
107
  lastNotifiedSelectionRef.current = id;
106
108
  onActionSelect(selectedAction);
107
109
  }, [selectedAction, onActionSelect]);
110
+
111
+ // Deep link into an action's detail (see initialSelectedActionId). Applied
112
+ // at most once per id, so the list re-rendering — or the user pressing Back
113
+ // — never re-opens it.
114
+ const appliedInitialSelectionRef = (0, _react.useRef)(null);
115
+ (0, _react.useEffect)(() => {
116
+ if (!initialSelectedActionId) return;
117
+ if (appliedInitialSelectionRef.current === initialSelectedActionId) return;
118
+ const index = visibleActions.findIndex(a => a.id === initialSelectedActionId);
119
+ if (index < 0) return;
120
+ appliedInitialSelectionRef.current = initialSelectedActionId;
121
+ setSelectedActionIndex(index);
122
+ }, [initialSelectedActionId, visibleActions]);
108
123
  const [searchText, setSearchText] = (0, _react.useState)("");
109
124
  const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
110
125
  const searchInputRef = (0, _react.useRef)(null);
@@ -307,7 +322,8 @@ function ReduxModal({
307
322
  actions: visibleActions,
308
323
  selectedIndex: selectedActionIndex,
309
324
  onIndexChange: handleIndexChange,
310
- disableInternalFooter: true
325
+ disableInternalFooter: true,
326
+ initialView: initialDetailView
311
327
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
312
328
  children: [instrumentError && !isInstrumented && !_reduxActionStore.reduxActionStore.isCaptureSuppressed() && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
313
329
  style: styles.errorBanner,
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.reduxSyncAdapter = void 0;
7
7
  var _reduxActionStore = require("../utils/reduxActionStore");
8
+ var _sharedUi = require("@buoy-gg/shared-ui");
8
9
  var _autoInstrument = require("../utils/autoInstrument");
9
10
  /**
10
11
  * Upper bound for one on-demand action-detail response. Generous — this is a
@@ -13,8 +14,49 @@ var _autoInstrument = require("../utils/autoInstrument");
13
14
  */
14
15
  const MAX_ACTION_DETAIL_BYTES = 8 * 1024 * 1024;
15
16
 
16
- /** Per-action budget for `payload`/`action` on the snapshot wire. */
17
+ /** Per-action budget for `payload`/`action`/`meta.arg`/`error` on the snapshot wire. */
17
18
  const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
19
+ function toWireValue(value) {
20
+ if (value === undefined) return value;
21
+ return (0, _sharedUi.isOverWireBudget)(value, MAX_WIRE_PAYLOAD_BYTES) ? _reduxActionStore.PAYLOAD_ON_DEVICE : value;
22
+ }
23
+
24
+ /**
25
+ * Per-action wire cache. `reduxActionStore` never mutates an action in place —
26
+ * it prepends, and `replaceActions` maps to fresh objects — so keying on
27
+ * identity is correct and self-invalidating. Without it, every payload,
28
+ * `action`, `meta.arg` and `error` in the list is re-measured on all ~5
29
+ * snapshots per second for as long as it stays in the list.
30
+ */
31
+ const wireCache = new WeakMap();
32
+ function toWireAction(a) {
33
+ const cached = wireCache.get(a);
34
+ if (cached) return cached;
35
+ const wire = (0, _reduxActionStore.isEvictedState)(a.prevState) ? {
36
+ ...a
37
+ } : {
38
+ ...a,
39
+ prevState: _reduxActionStore.STATE_ON_DEVICE,
40
+ nextState: _reduxActionStore.STATE_ON_DEVICE
41
+ };
42
+ // A dispatched payload can itself be huge (rehydration actions carry whole
43
+ // state trees). The list view renders payloadPreview anyway — oversized /
44
+ // unserializable values stay on-device behind markers. `meta.arg` is a
45
+ // leftover: RTK copies the thunk argument there, and capping `action` still
46
+ // left the extracted copy on the wire.
47
+ wire.payload = toWireValue(wire.payload);
48
+ wire.action = toWireValue(wire.action);
49
+ wire.error = toWireValue(wire.error);
50
+ if (wire.meta) {
51
+ const arg = toWireValue(wire.meta.arg);
52
+ if (arg !== wire.meta.arg) wire.meta = {
53
+ ...wire.meta,
54
+ arg
55
+ };
56
+ }
57
+ wireCache.set(a, wire);
58
+ return wire;
59
+ }
18
60
  /** Top-level slice names of the state tree, for the compact (no-values) view. */
19
61
  function topLevelKeys(state) {
20
62
  return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : [];
@@ -62,25 +104,7 @@ const reduxSyncAdapter = exports.reduxSyncAdapter = {
62
104
  * trees on demand via `getActionDetail`; already-evicted entries keep the
63
105
  * eviction sentinel so consumers can distinguish "fetchable" from "gone".
64
106
  */
65
- getSnapshot: () => _reduxActionStore.reduxActionStore.getActions().map(a => {
66
- const wire = (0, _reduxActionStore.isEvictedState)(a.prevState) ? {
67
- ...a
68
- } : {
69
- ...a,
70
- prevState: _reduxActionStore.STATE_ON_DEVICE,
71
- nextState: _reduxActionStore.STATE_ON_DEVICE
72
- };
73
- // A dispatched payload can itself be huge (rehydration actions carry
74
- // whole state trees). The list view renders payloadPreview anyway —
75
- // oversized payload/action values stay on-device behind markers.
76
- if ((0, _reduxActionStore.jsonLongerThan)(wire.payload, MAX_WIRE_PAYLOAD_BYTES)) {
77
- wire.payload = _reduxActionStore.PAYLOAD_ON_DEVICE;
78
- }
79
- if ((0, _reduxActionStore.jsonLongerThan)(wire.action, MAX_WIRE_PAYLOAD_BYTES)) {
80
- wire.action = _reduxActionStore.PAYLOAD_ON_DEVICE;
81
- }
82
- return wire;
83
- }),
107
+ getSnapshot: () => _reduxActionStore.reduxActionStore.getActions().map(toWireAction),
84
108
  subscribe: onChange => _reduxActionStore.reduxActionStore.subscribe(onChange),
85
109
  actions: {
86
110
  clearEvents: () => {
@@ -118,14 +142,24 @@ const reduxSyncAdapter = exports.reduxSyncAdapter = {
118
142
  __buoyTruncated: true,
119
143
  note: `Exceeds the ${MAX_ACTION_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
120
144
  };
121
- const cap = value => (0, _reduxActionStore.jsonLongerThan)(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
145
+ const cap = value => (0, _sharedUi.isOverWireBudget)(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
146
+ const capMeta = meta => {
147
+ if (!meta) return meta;
148
+ const arg = cap(meta.arg);
149
+ return arg === meta.arg ? meta : {
150
+ ...meta,
151
+ arg
152
+ };
153
+ };
122
154
  return {
123
155
  found: true,
124
156
  id,
125
157
  prevState: cap(action.prevState),
126
158
  nextState: cap(action.nextState),
127
159
  payload: cap(action.payload),
128
- action: cap(action.action)
160
+ action: cap(action.action),
161
+ meta: capMeta(action.meta),
162
+ error: cap(action.error)
129
163
  };
130
164
  },
131
165
  /**
@@ -5,8 +5,10 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.STATE_RETENTION_LIMIT = exports.STATE_ON_DEVICE = exports.STATE_EVICTED = exports.PAYLOAD_ON_DEVICE = void 0;
7
7
  exports.isEvictedState = isEvictedState;
8
+ exports.isPayloadOnDevice = isPayloadOnDevice;
8
9
  exports.jsonLongerThan = jsonLongerThan;
9
10
  exports.reduxActionStore = void 0;
11
+ var _sharedUi = require("@buoy-gg/shared-ui");
10
12
  /**
11
13
  * Redux action store - captures and stores Redux Toolkit actions
12
14
  */
@@ -52,6 +54,11 @@ const PAYLOAD_ON_DEVICE = exports.PAYLOAD_ON_DEVICE = Object.freeze({
52
54
  note: "Payload stays on the device — fetched on demand via getActionDetail."
53
55
  });
54
56
 
57
+ /** True when a wire-form value is the payload-on-device sentinel. */
58
+ function isPayloadOnDevice(value) {
59
+ return typeof value === "object" && value !== null && value.__buoyPayloadOnDevice === true;
60
+ }
61
+
55
62
  /**
56
63
  * True when a value is one of the state sentinels (evicted or on-device) —
57
64
  * guards time travel and any consumer that needs a REAL tree.
@@ -123,28 +130,7 @@ function parseActionType(type) {
123
130
  * JS thread at enterprise state sizes.
124
131
  */
125
132
  function jsonLongerThan(value, limit) {
126
- let bytes = 0;
127
- const stack = [value];
128
- const seen = new Set();
129
- while (stack.length > 0) {
130
- if (bytes > limit) return true;
131
- const v = stack.pop();
132
- if (v === null || v === undefined) bytes += 4;else if (typeof v === "string") bytes += v.length + 2;else if (typeof v === "number") bytes += 8;else if (typeof v === "boolean") bytes += 5;else if (typeof v === "object") {
133
- if (seen.has(v)) continue;
134
- seen.add(v);
135
- if (Array.isArray(v)) {
136
- bytes += 2 + v.length;
137
- for (let i = 0; i < v.length; i++) stack.push(v[i]);
138
- } else {
139
- bytes += 2;
140
- for (const k in v) {
141
- bytes += k.length + 4;
142
- stack.push(v[k]);
143
- }
144
- }
145
- } else bytes += 4;
146
- }
147
- return bytes > limit;
133
+ return (0, _sharedUi.approxJsonSize)(value, limit).bytes > limit;
148
134
  }
149
135
 
150
136
  /**
@@ -399,11 +385,16 @@ class ReduxActionStore {
399
385
  }
400
386
  this.actions = actions.slice(0, this.maxActions).map(a => {
401
387
  const h = hydrated.get(a.id);
402
- return h && isEvictedState(a.prevState) ? {
388
+ if (!h || !isEvictedState(a.prevState)) return a;
389
+ return {
403
390
  ...a,
404
391
  prevState: h.prevState,
405
- nextState: h.nextState
406
- } : a;
392
+ nextState: h.nextState,
393
+ payload: isPayloadOnDevice(a.payload) ? h.payload : a.payload,
394
+ action: isPayloadOnDevice(a.action) ? h.action : a.action,
395
+ meta: isPayloadOnDevice(a.meta?.arg) ? h.meta : a.meta,
396
+ error: isPayloadOnDevice(a.error) ? h.error : a.error
397
+ };
407
398
  });
408
399
  this.notifyListeners();
409
400
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  var _sharedUi = require("@buoy-gg/shared-ui");
4
+ var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
4
5
  var _activeStoreRegistry = require("./activeStoreRegistry");
5
6
  var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
6
7
  /**
@@ -60,7 +61,7 @@ async function capture() {
60
61
  warnings: []
61
62
  };
62
63
  }
63
- async function restore(data) {
64
+ async function restore(data, options) {
64
65
  const snapshot = data;
65
66
  const store = (0, _activeStoreRegistry.getActiveStore)();
66
67
  if (!store || snapshot === undefined || snapshot === null) {
@@ -88,7 +89,21 @@ async function restore(data) {
88
89
  warnings: []
89
90
  };
90
91
  }
91
- const payload = snapshot.state;
92
+ // Untouched slices keep their LIVE values inside the jump payload, so the
93
+ // full-state replace leaves them alone. A slice is untouched when it's
94
+ // explicitly excluded OR outside the includeKeys scope.
95
+ const excludedSet = new Set(options?.excludeKeys ?? []);
96
+ const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
97
+ const excluded = Object.keys(snapshot.state ?? {}).filter(k => excludedSet.has(k) || includedSet && !includedSet.has(k));
98
+ let payload = snapshot.state;
99
+ if (excluded.length > 0) {
100
+ const live = store.getState();
101
+ const merged = {
102
+ ...snapshot.state
103
+ };
104
+ for (const slice of excluded) merged[slice] = live?.[slice];
105
+ payload = merged;
106
+ }
92
107
  store.dispatch({
93
108
  type: _buoyReduxMiddleware.BUOY_JUMP_TO_STATE,
94
109
  payload
@@ -97,11 +112,12 @@ async function restore(data) {
97
112
  // identity proves the jump was actually handled (and not silently ignored
98
113
  // by an unwrapped middleware-path reducer).
99
114
  if (store.getState() === payload) {
115
+ const warnings = excluded.length > 0 ? [`${excluded.length} slice(s) excluded by you`] : [];
100
116
  return {
101
117
  ok: true,
102
118
  applied: 1,
103
119
  skipped: [],
104
- warnings: []
120
+ warnings
105
121
  };
106
122
  }
107
123
  return {
@@ -114,10 +130,92 @@ async function restore(data) {
114
130
  warnings: []
115
131
  };
116
132
  }
133
+ const ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
134
+
135
+ /** Per-slice restore preview. `base` omitted = compare against the live store. */
136
+ async function previewItems(target, base) {
137
+ const targetState = target?.state;
138
+ const liveMode = base === undefined;
139
+ const store = (0, _activeStoreRegistry.getActiveStore)();
140
+ const baseState = liveMode ? store?.getState() : base?.state;
141
+ const items = [];
142
+ const notes = [];
143
+
144
+ // Restore-capability gate — a diff restore can't apply must say so, not
145
+ // render as a green change.
146
+ let wontApplyReason;
147
+ if (liveMode) {
148
+ const {
149
+ mechanism,
150
+ hasStore
151
+ } = (0, _activeStoreRegistry.getInstrumentationStatus)();
152
+ if (!hasStore) wontApplyReason = "no Redux store instrumented";else if (mechanism === "patch") {
153
+ wontApplyReason = `auto-instrumented store cannot be restored — ${ENABLE_HINT}`;
154
+ }
155
+ }
156
+ const oldObj = baseState ?? {};
157
+ const newObj = targetState ?? {};
158
+ const keys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
159
+ for (const key of keys) {
160
+ const inOld = Object.prototype.hasOwnProperty.call(oldObj, key);
161
+ const inNew = Object.prototype.hasOwnProperty.call(newObj, key);
162
+ let item;
163
+ if (!inNew) {
164
+ item = {
165
+ key,
166
+ label: key,
167
+ verdict: "removed",
168
+ oldValue: oldObj[key]
169
+ };
170
+ } else if (!inOld) {
171
+ item = {
172
+ key,
173
+ label: key,
174
+ verdict: "added",
175
+ newValue: newObj[key]
176
+ };
177
+ } else if ((0, _dataViewer.boundedDeepEqual)(oldObj[key], newObj[key])) {
178
+ item = {
179
+ key,
180
+ label: key,
181
+ verdict: "unchanged"
182
+ };
183
+ } else if ((0, _dataViewer.jsonLikelyLargerThan)(oldObj[key], ITEM_RENDER_BYTE_LIMIT) || (0, _dataViewer.jsonLikelyLargerThan)(newObj[key], ITEM_RENDER_BYTE_LIMIT)) {
184
+ item = {
185
+ key,
186
+ label: key,
187
+ verdict: "too-large",
188
+ reason: "slice too large to render a diff — restore still applies it"
189
+ };
190
+ } else {
191
+ item = {
192
+ key,
193
+ label: key,
194
+ verdict: "changed",
195
+ oldValue: oldObj[key],
196
+ newValue: newObj[key]
197
+ };
198
+ }
199
+ if (wontApplyReason && item.verdict !== "unchanged") {
200
+ item = {
201
+ ...item,
202
+ verdict: "wont-apply",
203
+ reason: wontApplyReason
204
+ };
205
+ }
206
+ items.push(item);
207
+ }
208
+ if (wontApplyReason) notes.push(`Redux restore unavailable: ${wontApplyReason}`);
209
+ return {
210
+ items,
211
+ notes
212
+ };
213
+ }
117
214
  (0, _sharedUi.registerStateSnapshotProvider)({
118
215
  id: "redux",
119
216
  label: "Redux",
120
217
  status,
121
218
  capture,
122
- restore
219
+ restore,
220
+ previewItems
123
221
  });
@@ -78,10 +78,11 @@ export function ReduxActionDetailContent({
78
78
  actions,
79
79
  selectedIndex,
80
80
  onIndexChange,
81
- disableInternalFooter = false
81
+ disableInternalFooter = false,
82
+ initialView = "action"
82
83
  }) {
83
- // View state
84
- const [activeView, setActiveView] = useState("action");
84
+ // View state (initialView seeds it once; taps own it afterwards)
85
+ const [activeView, setActiveView] = useState(initialView);
85
86
  const [diffMode, setDiffMode] = useState("tree");
86
87
  const totalActions = actions.length;
87
88
  const actionColor = getActionColor(action.type);
@@ -43,7 +43,9 @@ export function ReduxModal({
43
43
  onMinimize,
44
44
  enableSharedModalDimensions = false,
45
45
  inline = false,
46
- onActionSelect
46
+ onActionSelect,
47
+ initialSelectedActionId,
48
+ initialDetailView
47
49
  }) {
48
50
  const isPro = useIsPro();
49
51
  const [showProModal, setShowProModal] = useState(false);
@@ -99,6 +101,19 @@ export function ReduxModal({
99
101
  lastNotifiedSelectionRef.current = id;
100
102
  onActionSelect(selectedAction);
101
103
  }, [selectedAction, onActionSelect]);
104
+
105
+ // Deep link into an action's detail (see initialSelectedActionId). Applied
106
+ // at most once per id, so the list re-rendering — or the user pressing Back
107
+ // — never re-opens it.
108
+ const appliedInitialSelectionRef = useRef(null);
109
+ useEffect(() => {
110
+ if (!initialSelectedActionId) return;
111
+ if (appliedInitialSelectionRef.current === initialSelectedActionId) return;
112
+ const index = visibleActions.findIndex(a => a.id === initialSelectedActionId);
113
+ if (index < 0) return;
114
+ appliedInitialSelectionRef.current = initialSelectedActionId;
115
+ setSelectedActionIndex(index);
116
+ }, [initialSelectedActionId, visibleActions]);
102
117
  const [searchText, setSearchText] = useState("");
103
118
  const [isSearchActive, setIsSearchActive] = useState(false);
104
119
  const searchInputRef = useRef(null);
@@ -301,7 +316,8 @@ export function ReduxModal({
301
316
  actions: visibleActions,
302
317
  selectedIndex: selectedActionIndex,
303
318
  onIndexChange: handleIndexChange,
304
- disableInternalFooter: true
319
+ disableInternalFooter: true,
320
+ initialView: initialDetailView
305
321
  }) : /*#__PURE__*/_jsxs(_Fragment, {
306
322
  children: [instrumentError && !isInstrumented && !reduxActionStore.isCaptureSuppressed() && /*#__PURE__*/_jsxs(View, {
307
323
  style: styles.errorBanner,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
 
3
- import { reduxActionStore, STATE_ON_DEVICE, PAYLOAD_ON_DEVICE, isEvictedState, jsonLongerThan } from "../utils/reduxActionStore";
3
+ import { reduxActionStore, STATE_ON_DEVICE, PAYLOAD_ON_DEVICE, isEvictedState } from "../utils/reduxActionStore";
4
+ import { isOverWireBudget } from "@buoy-gg/shared-ui";
4
5
  import { getActiveStore, getInstrumentationStatus } from "../utils/autoInstrument";
5
6
 
6
7
  /**
@@ -10,8 +11,49 @@ import { getActiveStore, getInstrumentationStatus } from "../utils/autoInstrumen
10
11
  */
11
12
  const MAX_ACTION_DETAIL_BYTES = 8 * 1024 * 1024;
12
13
 
13
- /** Per-action budget for `payload`/`action` on the snapshot wire. */
14
+ /** Per-action budget for `payload`/`action`/`meta.arg`/`error` on the snapshot wire. */
14
15
  const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
16
+ function toWireValue(value) {
17
+ if (value === undefined) return value;
18
+ return isOverWireBudget(value, MAX_WIRE_PAYLOAD_BYTES) ? PAYLOAD_ON_DEVICE : value;
19
+ }
20
+
21
+ /**
22
+ * Per-action wire cache. `reduxActionStore` never mutates an action in place —
23
+ * it prepends, and `replaceActions` maps to fresh objects — so keying on
24
+ * identity is correct and self-invalidating. Without it, every payload,
25
+ * `action`, `meta.arg` and `error` in the list is re-measured on all ~5
26
+ * snapshots per second for as long as it stays in the list.
27
+ */
28
+ const wireCache = new WeakMap();
29
+ function toWireAction(a) {
30
+ const cached = wireCache.get(a);
31
+ if (cached) return cached;
32
+ const wire = isEvictedState(a.prevState) ? {
33
+ ...a
34
+ } : {
35
+ ...a,
36
+ prevState: STATE_ON_DEVICE,
37
+ nextState: STATE_ON_DEVICE
38
+ };
39
+ // A dispatched payload can itself be huge (rehydration actions carry whole
40
+ // state trees). The list view renders payloadPreview anyway — oversized /
41
+ // unserializable values stay on-device behind markers. `meta.arg` is a
42
+ // leftover: RTK copies the thunk argument there, and capping `action` still
43
+ // left the extracted copy on the wire.
44
+ wire.payload = toWireValue(wire.payload);
45
+ wire.action = toWireValue(wire.action);
46
+ wire.error = toWireValue(wire.error);
47
+ if (wire.meta) {
48
+ const arg = toWireValue(wire.meta.arg);
49
+ if (arg !== wire.meta.arg) wire.meta = {
50
+ ...wire.meta,
51
+ arg
52
+ };
53
+ }
54
+ wireCache.set(a, wire);
55
+ return wire;
56
+ }
15
57
  /** Top-level slice names of the state tree, for the compact (no-values) view. */
16
58
  function topLevelKeys(state) {
17
59
  return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : [];
@@ -59,25 +101,7 @@ export const reduxSyncAdapter = {
59
101
  * trees on demand via `getActionDetail`; already-evicted entries keep the
60
102
  * eviction sentinel so consumers can distinguish "fetchable" from "gone".
61
103
  */
62
- getSnapshot: () => reduxActionStore.getActions().map(a => {
63
- const wire = isEvictedState(a.prevState) ? {
64
- ...a
65
- } : {
66
- ...a,
67
- prevState: STATE_ON_DEVICE,
68
- nextState: STATE_ON_DEVICE
69
- };
70
- // A dispatched payload can itself be huge (rehydration actions carry
71
- // whole state trees). The list view renders payloadPreview anyway —
72
- // oversized payload/action values stay on-device behind markers.
73
- if (jsonLongerThan(wire.payload, MAX_WIRE_PAYLOAD_BYTES)) {
74
- wire.payload = PAYLOAD_ON_DEVICE;
75
- }
76
- if (jsonLongerThan(wire.action, MAX_WIRE_PAYLOAD_BYTES)) {
77
- wire.action = PAYLOAD_ON_DEVICE;
78
- }
79
- return wire;
80
- }),
104
+ getSnapshot: () => reduxActionStore.getActions().map(toWireAction),
81
105
  subscribe: onChange => reduxActionStore.subscribe(onChange),
82
106
  actions: {
83
107
  clearEvents: () => {
@@ -115,14 +139,24 @@ export const reduxSyncAdapter = {
115
139
  __buoyTruncated: true,
116
140
  note: `Exceeds the ${MAX_ACTION_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
117
141
  };
118
- const cap = value => jsonLongerThan(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
142
+ const cap = value => isOverWireBudget(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
143
+ const capMeta = meta => {
144
+ if (!meta) return meta;
145
+ const arg = cap(meta.arg);
146
+ return arg === meta.arg ? meta : {
147
+ ...meta,
148
+ arg
149
+ };
150
+ };
119
151
  return {
120
152
  found: true,
121
153
  id,
122
154
  prevState: cap(action.prevState),
123
155
  nextState: cap(action.nextState),
124
156
  payload: cap(action.payload),
125
- action: cap(action.action)
157
+ action: cap(action.action),
158
+ meta: capMeta(action.meta),
159
+ error: cap(action.error)
126
160
  };
127
161
  },
128
162
  /**
@@ -4,6 +4,7 @@
4
4
  * Redux action store - captures and stores Redux Toolkit actions
5
5
  */
6
6
 
7
+ import { approxJsonSize } from "@buoy-gg/shared-ui";
7
8
  /**
8
9
  * How many of the most-recent actions keep their raw prevState/nextState
9
10
  * trees. Older actions keep all diff metadata but their trees are replaced
@@ -45,6 +46,11 @@ export const PAYLOAD_ON_DEVICE = Object.freeze({
45
46
  note: "Payload stays on the device — fetched on demand via getActionDetail."
46
47
  });
47
48
 
49
+ /** True when a wire-form value is the payload-on-device sentinel. */
50
+ export function isPayloadOnDevice(value) {
51
+ return typeof value === "object" && value !== null && value.__buoyPayloadOnDevice === true;
52
+ }
53
+
48
54
  /**
49
55
  * True when a value is one of the state sentinels (evicted or on-device) —
50
56
  * guards time travel and any consumer that needs a REAL tree.
@@ -116,28 +122,7 @@ function parseActionType(type) {
116
122
  * JS thread at enterprise state sizes.
117
123
  */
118
124
  export function jsonLongerThan(value, limit) {
119
- let bytes = 0;
120
- const stack = [value];
121
- const seen = new Set();
122
- while (stack.length > 0) {
123
- if (bytes > limit) return true;
124
- const v = stack.pop();
125
- if (v === null || v === undefined) bytes += 4;else if (typeof v === "string") bytes += v.length + 2;else if (typeof v === "number") bytes += 8;else if (typeof v === "boolean") bytes += 5;else if (typeof v === "object") {
126
- if (seen.has(v)) continue;
127
- seen.add(v);
128
- if (Array.isArray(v)) {
129
- bytes += 2 + v.length;
130
- for (let i = 0; i < v.length; i++) stack.push(v[i]);
131
- } else {
132
- bytes += 2;
133
- for (const k in v) {
134
- bytes += k.length + 4;
135
- stack.push(v[k]);
136
- }
137
- }
138
- } else bytes += 4;
139
- }
140
- return bytes > limit;
125
+ return approxJsonSize(value, limit).bytes > limit;
141
126
  }
142
127
 
143
128
  /**
@@ -392,11 +377,16 @@ class ReduxActionStore {
392
377
  }
393
378
  this.actions = actions.slice(0, this.maxActions).map(a => {
394
379
  const h = hydrated.get(a.id);
395
- return h && isEvictedState(a.prevState) ? {
380
+ if (!h || !isEvictedState(a.prevState)) return a;
381
+ return {
396
382
  ...a,
397
383
  prevState: h.prevState,
398
- nextState: h.nextState
399
- } : a;
384
+ nextState: h.nextState,
385
+ payload: isPayloadOnDevice(a.payload) ? h.payload : a.payload,
386
+ action: isPayloadOnDevice(a.action) ? h.action : a.action,
387
+ meta: isPayloadOnDevice(a.meta?.arg) ? h.meta : a.meta,
388
+ error: isPayloadOnDevice(a.error) ? h.error : a.error
389
+ };
400
390
  });
401
391
  this.notifyListeners();
402
392
  }
@@ -15,6 +15,7 @@
15
15
  * - patch path: never handled; restore is reported as unavailable up front.
16
16
  */
17
17
  import { registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
18
+ import { boundedDeepEqual, jsonLikelyLargerThan } from "@buoy-gg/shared-ui/dataViewer";
18
19
  import { getActiveStore, getInstrumentationStatus } from "./activeStoreRegistry";
19
20
  import { BUOY_JUMP_TO_STATE } from "./buoyReduxMiddleware";
20
21
  const ENABLE_HINT = "enable restore by using the Buoy enhancer or wrapping your root reducer in withBuoyDevTools()";
@@ -59,7 +60,7 @@ async function capture() {
59
60
  warnings: []
60
61
  };
61
62
  }
62
- async function restore(data) {
63
+ async function restore(data, options) {
63
64
  const snapshot = data;
64
65
  const store = getActiveStore();
65
66
  if (!store || snapshot === undefined || snapshot === null) {
@@ -87,7 +88,21 @@ async function restore(data) {
87
88
  warnings: []
88
89
  };
89
90
  }
90
- const payload = snapshot.state;
91
+ // Untouched slices keep their LIVE values inside the jump payload, so the
92
+ // full-state replace leaves them alone. A slice is untouched when it's
93
+ // explicitly excluded OR outside the includeKeys scope.
94
+ const excludedSet = new Set(options?.excludeKeys ?? []);
95
+ const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
96
+ const excluded = Object.keys(snapshot.state ?? {}).filter(k => excludedSet.has(k) || includedSet && !includedSet.has(k));
97
+ let payload = snapshot.state;
98
+ if (excluded.length > 0) {
99
+ const live = store.getState();
100
+ const merged = {
101
+ ...snapshot.state
102
+ };
103
+ for (const slice of excluded) merged[slice] = live?.[slice];
104
+ payload = merged;
105
+ }
91
106
  store.dispatch({
92
107
  type: BUOY_JUMP_TO_STATE,
93
108
  payload
@@ -96,11 +111,12 @@ async function restore(data) {
96
111
  // identity proves the jump was actually handled (and not silently ignored
97
112
  // by an unwrapped middleware-path reducer).
98
113
  if (store.getState() === payload) {
114
+ const warnings = excluded.length > 0 ? [`${excluded.length} slice(s) excluded by you`] : [];
99
115
  return {
100
116
  ok: true,
101
117
  applied: 1,
102
118
  skipped: [],
103
- warnings: []
119
+ warnings
104
120
  };
105
121
  }
106
122
  return {
@@ -113,10 +129,92 @@ async function restore(data) {
113
129
  warnings: []
114
130
  };
115
131
  }
132
+ const ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
133
+
134
+ /** Per-slice restore preview. `base` omitted = compare against the live store. */
135
+ async function previewItems(target, base) {
136
+ const targetState = target?.state;
137
+ const liveMode = base === undefined;
138
+ const store = getActiveStore();
139
+ const baseState = liveMode ? store?.getState() : base?.state;
140
+ const items = [];
141
+ const notes = [];
142
+
143
+ // Restore-capability gate — a diff restore can't apply must say so, not
144
+ // render as a green change.
145
+ let wontApplyReason;
146
+ if (liveMode) {
147
+ const {
148
+ mechanism,
149
+ hasStore
150
+ } = getInstrumentationStatus();
151
+ if (!hasStore) wontApplyReason = "no Redux store instrumented";else if (mechanism === "patch") {
152
+ wontApplyReason = `auto-instrumented store cannot be restored — ${ENABLE_HINT}`;
153
+ }
154
+ }
155
+ const oldObj = baseState ?? {};
156
+ const newObj = targetState ?? {};
157
+ const keys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
158
+ for (const key of keys) {
159
+ const inOld = Object.prototype.hasOwnProperty.call(oldObj, key);
160
+ const inNew = Object.prototype.hasOwnProperty.call(newObj, key);
161
+ let item;
162
+ if (!inNew) {
163
+ item = {
164
+ key,
165
+ label: key,
166
+ verdict: "removed",
167
+ oldValue: oldObj[key]
168
+ };
169
+ } else if (!inOld) {
170
+ item = {
171
+ key,
172
+ label: key,
173
+ verdict: "added",
174
+ newValue: newObj[key]
175
+ };
176
+ } else if (boundedDeepEqual(oldObj[key], newObj[key])) {
177
+ item = {
178
+ key,
179
+ label: key,
180
+ verdict: "unchanged"
181
+ };
182
+ } else if (jsonLikelyLargerThan(oldObj[key], ITEM_RENDER_BYTE_LIMIT) || jsonLikelyLargerThan(newObj[key], ITEM_RENDER_BYTE_LIMIT)) {
183
+ item = {
184
+ key,
185
+ label: key,
186
+ verdict: "too-large",
187
+ reason: "slice too large to render a diff — restore still applies it"
188
+ };
189
+ } else {
190
+ item = {
191
+ key,
192
+ label: key,
193
+ verdict: "changed",
194
+ oldValue: oldObj[key],
195
+ newValue: newObj[key]
196
+ };
197
+ }
198
+ if (wontApplyReason && item.verdict !== "unchanged") {
199
+ item = {
200
+ ...item,
201
+ verdict: "wont-apply",
202
+ reason: wontApplyReason
203
+ };
204
+ }
205
+ items.push(item);
206
+ }
207
+ if (wontApplyReason) notes.push(`Redux restore unavailable: ${wontApplyReason}`);
208
+ return {
209
+ items,
210
+ notes
211
+ };
212
+ }
116
213
  registerStateSnapshotProvider({
117
214
  id: "redux",
118
215
  label: "Redux",
119
216
  status,
120
217
  capture,
121
- restore
218
+ restore,
219
+ previewItems
122
220
  });
@@ -7,6 +7,7 @@
7
7
  * - DIFF: Compare prev vs next state
8
8
  */
9
9
  import type { ReduxAction } from "../types";
10
+ import { type ReduxDetailViewMode } from "./ReduxDetailViewToggle";
10
11
  interface ReduxActionDetailContentProps {
11
12
  /** The selected action to display */
12
13
  action: ReduxAction;
@@ -18,8 +19,15 @@ interface ReduxActionDetailContentProps {
18
19
  onIndexChange: (index: number) => void;
19
20
  /** If true, do not render the internal footer */
20
21
  disableInternalFooter?: boolean;
22
+ /**
23
+ * Which of the three views the detail opens on (default "action"). Hosts
24
+ * that deep-link into a specific view — the docs live tour, and any future
25
+ * "show me the diff for this action" entry point — set it at mount; the
26
+ * user's own toggle taps take over from there.
27
+ */
28
+ initialView?: ReduxDetailViewMode;
21
29
  }
22
- export declare function ReduxActionDetailContent({ action, actions, selectedIndex, onIndexChange, disableInternalFooter, }: ReduxActionDetailContentProps): import("react").JSX.Element;
30
+ export declare function ReduxActionDetailContent({ action, actions, selectedIndex, onIndexChange, disableInternalFooter, initialView, }: ReduxActionDetailContentProps): import("react").JSX.Element;
23
31
  /**
24
32
  * External footer component for modal use
25
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ReduxActionDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/redux/components/ReduxActionDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQ5C,UAAU,6BAA6B;IACrC,qCAAqC;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,iCAAiC;IACjC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,iDAAiD;IACjD,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAwDD,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,GAC9B,EAAE,6BAA6B,+BA0M/B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,sCAiBA"}
1
+ {"version":3,"file":"ReduxActionDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/redux/components/ReduxActionDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAIjC,UAAU,6BAA6B;IACrC,qCAAqC;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,iCAAiC;IACjC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,qCAAqC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,iDAAiD;IACjD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAwDD,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,EAC7B,WAAsB,GACvB,EAAE,6BAA6B,+BA0M/B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,sCAiBA"}
@@ -4,6 +4,7 @@
4
4
  * This component auto-instruments the Redux store when it mounts,
5
5
  * providing zero-config action capture for the DevTools.
6
6
  */
7
+ import type { ReduxDetailViewMode } from "./ReduxDetailViewToggle";
7
8
  import type { ReduxAction } from "../types";
8
9
  /** Free tier limit for action history */
9
10
  export declare const FREE_TIER_ACTION_LIMIT = 25;
@@ -26,8 +27,19 @@ interface ReduxModalProps {
26
27
  * on demand — wire-form snapshots carry markers instead of trees.
27
28
  */
28
29
  onActionSelect?: (action: ReduxAction) => void;
30
+ /**
31
+ * Open straight into this action's detail view, by action id. Applied once
32
+ * the id appears in the visible list (the list can arrive after mount when
33
+ * actions are mirrored in), then left alone — Back closes the detail and it
34
+ * stays closed. The counterpart to `onActionSelect`, for hosts that deep-link
35
+ * INTO an action (the docs live tour; a future MCP/desktop "show me this
36
+ * action" jump). Null/undefined means "don't touch the selection".
37
+ */
38
+ initialSelectedActionId?: string | null;
39
+ /** Which detail view that deep-link opens on. See ReduxActionDetailContent. */
40
+ initialDetailView?: ReduxDetailViewMode;
29
41
  }
30
- export declare function ReduxModal({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, inline, onActionSelect, }: ReduxModalProps): import("react").JSX.Element | null;
42
+ export declare function ReduxModal({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, inline, onActionSelect, initialSelectedActionId, initialDetailView, }: ReduxModalProps): import("react").JSX.Element | null;
31
43
  /**
32
44
  * The same Redux tool as `ReduxModal`, rendered INLINE for hosts that place
33
45
  * tools in a panel rather than a modal. Exists so there is exactly one
@@ -1 +1 @@
1
- {"version":3,"file":"ReduxModal.d.ts","sourceRoot":"","sources":["../../../../src/redux/components/ReduxModal.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyCH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,yCAAyC;AACzC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,UAAU,eAAe;IACvB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CAChD;AAgBD,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,MAAc,EACd,cAAc,GACf,EAAE,eAAe,sCAmbjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,GAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAGpE"}
1
+ {"version":3,"file":"ReduxModal.d.ts","sourceRoot":"","sources":["../../../../src/redux/components/ReduxModal.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyCH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,yCAAyC;AACzC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,UAAU,eAAe;IACvB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,mBAAmB,CAAC;CACzC;AAgBD,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,MAAc,EACd,cAAc,EACd,uBAAuB,EACvB,iBAAiB,GAClB,EAAE,eAAe,sCAmcjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,GAAE,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAGpE"}
@@ -1,3 +1,4 @@
1
+ import type { ActionMeta, ReduxAction } from "../types";
1
2
  /**
2
3
  * Sync adapter for the redux tool, consumed by @buoy-gg/external-sync's
3
4
  * `useExternalSync` (structurally matches its ToolSyncAdapter interface so
@@ -25,26 +26,7 @@ export declare const reduxSyncAdapter: {
25
26
  * trees on demand via `getActionDetail`; already-evicted entries keep the
26
27
  * eviction sentinel so consumers can distinguish "fetchable" from "gone".
27
28
  */
28
- getSnapshot: () => {
29
- id: string;
30
- type: string;
31
- payload?: unknown;
32
- action: Record<string, unknown>;
33
- timestamp: number;
34
- prevState: unknown;
35
- nextState: unknown;
36
- duration?: number;
37
- hasStateChange: boolean;
38
- category: import("../types").ActionCategory;
39
- sliceName: string | null;
40
- actionName: string;
41
- meta?: import("../types").ActionMeta;
42
- error?: unknown;
43
- payloadPreview: string;
44
- diffSummary: string;
45
- changedKeysCount: number;
46
- isSlowAction: boolean;
47
- }[];
29
+ getSnapshot: () => ReduxAction[];
48
30
  subscribe: (onChange: () => void) => () => void;
49
31
  actions: {
50
32
  clearEvents: () => void;
@@ -53,7 +35,7 @@ export declare const reduxSyncAdapter: {
53
35
  * The per-snapshot stream stays tree-free; this is the explicit,
54
36
  * size-guarded channel for the detail pane.
55
37
  */
56
- getActionDetail: (params: unknown) => {
38
+ getActionDetail: (params?: unknown) => {
57
39
  found: false;
58
40
  reason: string;
59
41
  id?: undefined;
@@ -62,6 +44,8 @@ export declare const reduxSyncAdapter: {
62
44
  nextState?: undefined;
63
45
  payload?: undefined;
64
46
  action?: undefined;
47
+ meta?: undefined;
48
+ error?: undefined;
65
49
  } | {
66
50
  found: true;
67
51
  id: string;
@@ -71,6 +55,8 @@ export declare const reduxSyncAdapter: {
71
55
  nextState?: undefined;
72
56
  payload?: undefined;
73
57
  action?: undefined;
58
+ meta?: undefined;
59
+ error?: undefined;
74
60
  } | {
75
61
  found: true;
76
62
  id: string;
@@ -78,6 +64,8 @@ export declare const reduxSyncAdapter: {
78
64
  nextState: unknown;
79
65
  payload: unknown;
80
66
  action: unknown;
67
+ meta: ActionMeta | undefined;
68
+ error: unknown;
81
69
  reason?: undefined;
82
70
  evicted?: undefined;
83
71
  };
@@ -85,7 +73,7 @@ export declare const reduxSyncAdapter: {
85
73
  * Compact current-state reader: the instrumented store's slice names, and
86
74
  * the full state tree only when `includeValues` is set (heavy).
87
75
  */
88
- getState: (params: unknown) => {
76
+ getState: (params?: unknown) => {
89
77
  available: false;
90
78
  slices: string[];
91
79
  /** 'no-react-redux' | 'no-provider' | 'not-instrumented' */
@@ -99,7 +87,7 @@ export declare const reduxSyncAdapter: {
99
87
  mechanism: import("../..").CaptureMechanism | null;
100
88
  };
101
89
  /** Dispatch a plain action object to the instrumented store. */
102
- dispatch: (params: unknown) => {
90
+ dispatch: (params?: unknown) => {
103
91
  available: false;
104
92
  slices: string[];
105
93
  /** 'no-react-redux' | 'no-provider' | 'not-instrumented' */
@@ -1 +1 @@
1
- {"version":3,"file":"reduxSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/redux/sync/reduxSyncAdapter.ts"],"names":[],"mappings":"AAsDA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB;;IAE3B;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;0BAiBmB,MAAM,IAAI;;;QAK9B;;;;WAIG;kCACuB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8BjC;;;WAGG;2BACgB,OAAO;;oBA7FZ,MAAM,EAAE;YACtB,4DAA4D;;;;;;YAwGxD,wEAAwE;;;;QAM5E,gEAAgE;2BAC7C,OAAO;;oBAhHZ,MAAM,EAAE;YACtB,4DAA4D;;;;;;;;;CAyH/D,CAAC"}
1
+ {"version":3,"file":"reduxSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/redux/sync/reduxSyncAdapter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAwFxD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB;;IAE3B;;;;;;;;OAQG;;0BAEmB,MAAM,IAAI;;;QAK9B;;;;WAIG;mCACwB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqClC;;;WAGG;4BACiB,OAAO;;oBArFb,MAAM,EAAE;YACtB,4DAA4D;;;;;;YAgGxD,wEAAwE;;;;QAM5E,gEAAgE;4BAC5C,OAAO;;oBAxGb,MAAM,EAAE;YACtB,4DAA4D;;;;;;;;;CAiH/D,CAAC"}
@@ -39,6 +39,8 @@ export declare const PAYLOAD_ON_DEVICE: Readonly<{
39
39
  __buoyPayloadOnDevice: true;
40
40
  note: "Payload stays on the device — fetched on demand via getActionDetail.";
41
41
  }>;
42
+ /** True when a wire-form value is the payload-on-device sentinel. */
43
+ export declare function isPayloadOnDevice(value: unknown): boolean;
42
44
  /**
43
45
  * True when a value is one of the state sentinels (evicted or on-device) —
44
46
  * guards time travel and any consumer that needs a REAL tree.
@@ -1 +1 @@
1
- {"version":3,"file":"reduxActionStore.d.ts","sourceRoot":"","sources":["../../../../src/redux/utils/reduxActionStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAA8B,MAAM,UAAU,CAAC;AAErF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,aAAa;;;EAGxB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;EAG1B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;EAG5B,CAAC;AAEH;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAItD;AAwDD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CA2BrE;AA8HD,cAAM,gBAAgB;IACpB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,SAAS,CAAoD;IACrE,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAElC,SAAS,CACP,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,OAAO,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI;IAyEP,UAAU,IAAI,WAAW,EAAE;IAI3B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIlD,YAAY,IAAI,IAAI;IAYpB;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAWzC;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAuB5C;;;;OAIG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAW5D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC,UAAU,IAAI,OAAO;IAIrB,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI;IAOjE,OAAO,CAAC,eAAe;IAKvB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhC,QAAQ;;;;;;;IAuBR,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE;IAmBjD,oBAAoB,IAAI,MAAM,EAAE;IAIhC;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAMnD;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE;IAKpD;;OAEG;IACH,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAOlD;;OAEG;IACH,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAe3D;;OAEG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAWvC;;;OAGG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;CAkBlD;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
1
+ {"version":3,"file":"reduxActionStore.d.ts","sourceRoot":"","sources":["../../../../src/redux/utils/reduxActionStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAA8B,MAAM,UAAU,CAAC;AAErF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,aAAa;;;EAGxB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;EAG1B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;EAG5B,CAAC;AAEH,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAMzD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAItD;AAwDD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErE;AA8HD,cAAM,gBAAgB;IACpB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,SAAS,CAAoD;IACrE,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAElC,SAAS,CACP,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,OAAO,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI;IAyEP,UAAU,IAAI,WAAW,EAAE;IAI3B,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIlD,YAAY,IAAI,IAAI;IAYpB;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAWzC;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAItB;;;;OAIG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IA8B5C;;;;OAIG;IACH,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAW5D,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC,UAAU,IAAI,OAAO;IAIrB,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI;IAOjE,OAAO,CAAC,eAAe;IAKvB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhC,QAAQ;;;;;;;IAuBR,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE;IAmBjD,oBAAoB,IAAI,MAAM,EAAE;IAIhC;;OAEG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE;IAMnD;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,EAAE;IAKpD;;OAEG;IACH,qBAAqB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAOlD;;OAEG;IACH,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAe3D;;OAEG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAWvC;;;OAGG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;CAkBlD;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/redux",
3
- "version": "7.0.17",
3
+ "version": "7.0.19",
4
4
  "description": "Redux DevTools inside your React Native app — action log, state diffs & time travel with zero store changes. Part of Buoy devtools.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -31,9 +31,9 @@
31
31
  "**/snapshotProvider.ts"
32
32
  ],
33
33
  "dependencies": {
34
- "@buoy-gg/shared-ui": "^7.0.17",
35
- "@buoy-gg/license": "^7.0.17",
36
- "@buoy-gg/floating-tools-core": "^7.0.17"
34
+ "@buoy-gg/shared-ui": "^7.0.19",
35
+ "@buoy-gg/license": "^7.0.19",
36
+ "@buoy-gg/floating-tools-core": "^7.0.19"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@reduxjs/toolkit": ">=1.9.0",
@@ -64,7 +64,12 @@
64
64
  "sourceMaps": false
65
65
  }
66
66
  ],
67
- "typescript"
67
+ [
68
+ "typescript",
69
+ {
70
+ "project": "tsconfig.build.json"
71
+ }
72
+ ]
68
73
  ]
69
74
  },
70
75
  "repository": {