@buoy-gg/redux 7.0.16 → 7.0.18
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/lib/commonjs/index.js +1 -0
- package/lib/commonjs/redux/components/ReduxActionDetailContent.js +4 -3
- package/lib/commonjs/redux/components/ReduxModal.js +18 -2
- package/lib/commonjs/redux/sync/reduxSyncAdapter.js +43 -9
- package/lib/commonjs/redux/utils/reduxActionStore.js +14 -3
- package/lib/commonjs/redux/utils/snapshotProvider.js +221 -0
- package/lib/module/index.js +3 -0
- package/lib/module/redux/components/ReduxActionDetailContent.js +4 -3
- package/lib/module/redux/components/ReduxModal.js +18 -2
- package/lib/module/redux/sync/reduxSyncAdapter.js +43 -9
- package/lib/module/redux/utils/reduxActionStore.js +13 -3
- package/lib/module/redux/utils/snapshotProvider.js +220 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts +9 -1
- package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +1 -1
- package/lib/typescript/redux/components/ReduxModal.d.ts +13 -1
- package/lib/typescript/redux/components/ReduxModal.d.ts.map +1 -1
- package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts +8 -1
- package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/redux/utils/reduxActionStore.d.ts +2 -0
- package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +1 -1
- package/lib/typescript/redux/utils/snapshotProvider.d.ts +2 -0
- package/lib/typescript/redux/utils/snapshotProvider.d.ts.map +1 -0
- package/package.json +7 -5
package/lib/commonjs/index.js
CHANGED
|
@@ -213,6 +213,7 @@ Object.defineProperty(exports, "withBuoyDevTools", {
|
|
|
213
213
|
return _buoyReduxMiddleware.withBuoyDevTools;
|
|
214
214
|
}
|
|
215
215
|
});
|
|
216
|
+
require("./redux/utils/snapshotProvider");
|
|
216
217
|
var _devtoolsHook = require("./redux/utils/devtoolsHook");
|
|
217
218
|
var _preset = require("./preset");
|
|
218
219
|
var _buoyReduxMiddleware = require("./redux/utils/buoyReduxMiddleware");
|
|
@@ -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)(
|
|
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,
|
|
@@ -13,8 +13,17 @@ var _autoInstrument = require("../utils/autoInstrument");
|
|
|
13
13
|
*/
|
|
14
14
|
const MAX_ACTION_DETAIL_BYTES = 8 * 1024 * 1024;
|
|
15
15
|
|
|
16
|
-
/** Per-action budget for `payload`/`action` on the snapshot wire. */
|
|
16
|
+
/** Per-action budget for `payload`/`action`/`meta.arg`/`error` on the snapshot wire. */
|
|
17
17
|
const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
|
|
18
|
+
function toWireValue(value) {
|
|
19
|
+
if (value === undefined) return value;
|
|
20
|
+
try {
|
|
21
|
+
JSON.stringify(value);
|
|
22
|
+
} catch {
|
|
23
|
+
return _reduxActionStore.PAYLOAD_ON_DEVICE;
|
|
24
|
+
}
|
|
25
|
+
return (0, _reduxActionStore.jsonLongerThan)(value, MAX_WIRE_PAYLOAD_BYTES) ? _reduxActionStore.PAYLOAD_ON_DEVICE : value;
|
|
26
|
+
}
|
|
18
27
|
/** Top-level slice names of the state tree, for the compact (no-values) view. */
|
|
19
28
|
function topLevelKeys(state) {
|
|
20
29
|
return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : [];
|
|
@@ -72,12 +81,20 @@ const reduxSyncAdapter = exports.reduxSyncAdapter = {
|
|
|
72
81
|
};
|
|
73
82
|
// A dispatched payload can itself be huge (rehydration actions carry
|
|
74
83
|
// whole state trees). The list view renders payloadPreview anyway —
|
|
75
|
-
// oversized
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
// oversized / unserializable values stay on-device behind markers.
|
|
85
|
+
// `meta.arg` is a leftover: RTK copies the thunk argument there, and
|
|
86
|
+
// capping `action` still left the extracted copy on the wire.
|
|
87
|
+
wire.payload = toWireValue(wire.payload);
|
|
88
|
+
wire.action = toWireValue(wire.action);
|
|
89
|
+
wire.error = toWireValue(wire.error);
|
|
90
|
+
if (wire.meta) {
|
|
91
|
+
const arg = toWireValue(wire.meta.arg);
|
|
92
|
+
if (arg !== wire.meta.arg) {
|
|
93
|
+
wire.meta = {
|
|
94
|
+
...wire.meta,
|
|
95
|
+
arg
|
|
96
|
+
};
|
|
97
|
+
}
|
|
81
98
|
}
|
|
82
99
|
return wire;
|
|
83
100
|
}),
|
|
@@ -118,14 +135,31 @@ const reduxSyncAdapter = exports.reduxSyncAdapter = {
|
|
|
118
135
|
__buoyTruncated: true,
|
|
119
136
|
note: `Exceeds the ${MAX_ACTION_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
|
|
120
137
|
};
|
|
121
|
-
const cap = value =>
|
|
138
|
+
const cap = value => {
|
|
139
|
+
try {
|
|
140
|
+
JSON.stringify(value);
|
|
141
|
+
} catch {
|
|
142
|
+
return cappedNote;
|
|
143
|
+
}
|
|
144
|
+
return (0, _reduxActionStore.jsonLongerThan)(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
|
|
145
|
+
};
|
|
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,6 +5,7 @@ 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;
|
|
10
11
|
/**
|
|
@@ -52,6 +53,11 @@ const PAYLOAD_ON_DEVICE = exports.PAYLOAD_ON_DEVICE = Object.freeze({
|
|
|
52
53
|
note: "Payload stays on the device — fetched on demand via getActionDetail."
|
|
53
54
|
});
|
|
54
55
|
|
|
56
|
+
/** True when a wire-form value is the payload-on-device sentinel. */
|
|
57
|
+
function isPayloadOnDevice(value) {
|
|
58
|
+
return typeof value === "object" && value !== null && value.__buoyPayloadOnDevice === true;
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
/**
|
|
56
62
|
* True when a value is one of the state sentinels (evicted or on-device) —
|
|
57
63
|
* guards time travel and any consumer that needs a REAL tree.
|
|
@@ -399,11 +405,16 @@ class ReduxActionStore {
|
|
|
399
405
|
}
|
|
400
406
|
this.actions = actions.slice(0, this.maxActions).map(a => {
|
|
401
407
|
const h = hydrated.get(a.id);
|
|
402
|
-
|
|
408
|
+
if (!h || !isEvictedState(a.prevState)) return a;
|
|
409
|
+
return {
|
|
403
410
|
...a,
|
|
404
411
|
prevState: h.prevState,
|
|
405
|
-
nextState: h.nextState
|
|
406
|
-
|
|
412
|
+
nextState: h.nextState,
|
|
413
|
+
payload: isPayloadOnDevice(a.payload) ? h.payload : a.payload,
|
|
414
|
+
action: isPayloadOnDevice(a.action) ? h.action : a.action,
|
|
415
|
+
meta: isPayloadOnDevice(a.meta?.arg) ? h.meta : a.meta,
|
|
416
|
+
error: isPayloadOnDevice(a.error) ? h.error : a.error
|
|
417
|
+
};
|
|
407
418
|
});
|
|
408
419
|
this.notifyListeners();
|
|
409
420
|
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
4
|
+
var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
|
|
5
|
+
var _activeStoreRegistry = require("./activeStoreRegistry");
|
|
6
|
+
var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
|
|
7
|
+
/**
|
|
8
|
+
* Time Machine snapshot provider for Redux.
|
|
9
|
+
*
|
|
10
|
+
* Registered into @buoy-gg/shared-ui's stateSnapshotRegistry at module init;
|
|
11
|
+
* consumed by @buoy-gg/time-machine.
|
|
12
|
+
*
|
|
13
|
+
* Restore rides the existing `@@buoy/JUMP_TO_STATE` full-state-replace action:
|
|
14
|
+
* - enhancer path: always handled (wrapReducer).
|
|
15
|
+
* - middleware path: handled ONLY when the app wrapped its root reducer in
|
|
16
|
+
* `withBuoyDevTools()` — unverifiable statically, so restore verifies by
|
|
17
|
+
* reference identity after dispatch (the wrapped reducer returns the payload
|
|
18
|
+
* object itself).
|
|
19
|
+
* - patch path: never handled; restore is reported as unavailable up front.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const ENABLE_HINT = "enable restore by using the Buoy enhancer or wrapping your root reducer in withBuoyDevTools()";
|
|
23
|
+
function status() {
|
|
24
|
+
const {
|
|
25
|
+
mechanism,
|
|
26
|
+
hasStore,
|
|
27
|
+
diagnostic
|
|
28
|
+
} = (0, _activeStoreRegistry.getInstrumentationStatus)();
|
|
29
|
+
if (!hasStore) {
|
|
30
|
+
return {
|
|
31
|
+
canCapture: false,
|
|
32
|
+
canRestore: false,
|
|
33
|
+
detail: diagnostic === "no-react-redux" ? "react-redux not installed" : diagnostic === "no-provider" ? "No Redux <Provider> found" : "No Redux store instrumented"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (mechanism === "patch") {
|
|
37
|
+
return {
|
|
38
|
+
canCapture: true,
|
|
39
|
+
canRestore: false,
|
|
40
|
+
detail: `Auto-instrumented store cannot be restored — ${ENABLE_HINT}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
canCapture: true,
|
|
45
|
+
canRestore: true,
|
|
46
|
+
detail: mechanism === "middleware" ? "Restore requires withBuoyDevTools(rootReducer) — verified at restore time" : undefined
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function capture() {
|
|
50
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
51
|
+
if (!store) {
|
|
52
|
+
return {
|
|
53
|
+
data: null,
|
|
54
|
+
warnings: ["no Redux store instrumented"]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
data: {
|
|
59
|
+
state: store.getState()
|
|
60
|
+
},
|
|
61
|
+
warnings: []
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function restore(data, options) {
|
|
65
|
+
const snapshot = data;
|
|
66
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
67
|
+
if (!store || snapshot === undefined || snapshot === null) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
applied: 0,
|
|
71
|
+
skipped: [{
|
|
72
|
+
id: "redux",
|
|
73
|
+
reason: store ? "empty redux snapshot" : "no Redux store instrumented"
|
|
74
|
+
}],
|
|
75
|
+
warnings: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const {
|
|
79
|
+
mechanism
|
|
80
|
+
} = (0, _activeStoreRegistry.getInstrumentationStatus)();
|
|
81
|
+
if (mechanism === "patch") {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
applied: 0,
|
|
85
|
+
skipped: [{
|
|
86
|
+
id: "redux",
|
|
87
|
+
reason: `restore unavailable on auto-instrumented stores — ${ENABLE_HINT}`
|
|
88
|
+
}],
|
|
89
|
+
warnings: []
|
|
90
|
+
};
|
|
91
|
+
}
|
|
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
|
+
}
|
|
107
|
+
store.dispatch({
|
|
108
|
+
type: _buoyReduxMiddleware.BUOY_JUMP_TO_STATE,
|
|
109
|
+
payload
|
|
110
|
+
});
|
|
111
|
+
// The wrapped reducer returns the payload object itself, so reference
|
|
112
|
+
// identity proves the jump was actually handled (and not silently ignored
|
|
113
|
+
// by an unwrapped middleware-path reducer).
|
|
114
|
+
if (store.getState() === payload) {
|
|
115
|
+
const warnings = excluded.length > 0 ? [`${excluded.length} slice(s) excluded by you`] : [];
|
|
116
|
+
return {
|
|
117
|
+
ok: true,
|
|
118
|
+
applied: 1,
|
|
119
|
+
skipped: [],
|
|
120
|
+
warnings
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
applied: 0,
|
|
126
|
+
skipped: [{
|
|
127
|
+
id: "redux",
|
|
128
|
+
reason: `store ignored the state jump — ${ENABLE_HINT}`
|
|
129
|
+
}],
|
|
130
|
+
warnings: []
|
|
131
|
+
};
|
|
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
|
+
}
|
|
214
|
+
(0, _sharedUi.registerStateSnapshotProvider)({
|
|
215
|
+
id: "redux",
|
|
216
|
+
label: "Redux",
|
|
217
|
+
status,
|
|
218
|
+
capture,
|
|
219
|
+
restore,
|
|
220
|
+
previewItems
|
|
221
|
+
});
|
package/lib/module/index.js
CHANGED
|
@@ -53,6 +53,9 @@
|
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
|
+
// Side-effect: registers the Time Machine snapshot provider
|
|
57
|
+
import "./redux/utils/snapshotProvider";
|
|
58
|
+
|
|
56
59
|
// SIDE EFFECT (must stay first): claim the Redux DevTools global so stores
|
|
57
60
|
// created after this module loads get the full-capture enhancer. Dev-only,
|
|
58
61
|
// never overrides an existing claimant. See ./redux/utils/devtoolsHook.ts.
|
|
@@ -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(
|
|
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,
|
|
@@ -10,8 +10,17 @@ import { getActiveStore, getInstrumentationStatus } from "../utils/autoInstrumen
|
|
|
10
10
|
*/
|
|
11
11
|
const MAX_ACTION_DETAIL_BYTES = 8 * 1024 * 1024;
|
|
12
12
|
|
|
13
|
-
/** Per-action budget for `payload`/`action` on the snapshot wire. */
|
|
13
|
+
/** Per-action budget for `payload`/`action`/`meta.arg`/`error` on the snapshot wire. */
|
|
14
14
|
const MAX_WIRE_PAYLOAD_BYTES = 16 * 1024;
|
|
15
|
+
function toWireValue(value) {
|
|
16
|
+
if (value === undefined) return value;
|
|
17
|
+
try {
|
|
18
|
+
JSON.stringify(value);
|
|
19
|
+
} catch {
|
|
20
|
+
return PAYLOAD_ON_DEVICE;
|
|
21
|
+
}
|
|
22
|
+
return jsonLongerThan(value, MAX_WIRE_PAYLOAD_BYTES) ? PAYLOAD_ON_DEVICE : value;
|
|
23
|
+
}
|
|
15
24
|
/** Top-level slice names of the state tree, for the compact (no-values) view. */
|
|
16
25
|
function topLevelKeys(state) {
|
|
17
26
|
return state && typeof state === "object" && !Array.isArray(state) ? Object.keys(state) : [];
|
|
@@ -69,12 +78,20 @@ export const reduxSyncAdapter = {
|
|
|
69
78
|
};
|
|
70
79
|
// A dispatched payload can itself be huge (rehydration actions carry
|
|
71
80
|
// whole state trees). The list view renders payloadPreview anyway —
|
|
72
|
-
// oversized
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
// oversized / unserializable values stay on-device behind markers.
|
|
82
|
+
// `meta.arg` is a leftover: RTK copies the thunk argument there, and
|
|
83
|
+
// capping `action` still left the extracted copy on the wire.
|
|
84
|
+
wire.payload = toWireValue(wire.payload);
|
|
85
|
+
wire.action = toWireValue(wire.action);
|
|
86
|
+
wire.error = toWireValue(wire.error);
|
|
87
|
+
if (wire.meta) {
|
|
88
|
+
const arg = toWireValue(wire.meta.arg);
|
|
89
|
+
if (arg !== wire.meta.arg) {
|
|
90
|
+
wire.meta = {
|
|
91
|
+
...wire.meta,
|
|
92
|
+
arg
|
|
93
|
+
};
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
96
|
return wire;
|
|
80
97
|
}),
|
|
@@ -115,14 +132,31 @@ export const reduxSyncAdapter = {
|
|
|
115
132
|
__buoyTruncated: true,
|
|
116
133
|
note: `Exceeds the ${MAX_ACTION_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
|
|
117
134
|
};
|
|
118
|
-
const cap = value =>
|
|
135
|
+
const cap = value => {
|
|
136
|
+
try {
|
|
137
|
+
JSON.stringify(value);
|
|
138
|
+
} catch {
|
|
139
|
+
return cappedNote;
|
|
140
|
+
}
|
|
141
|
+
return jsonLongerThan(value, MAX_ACTION_DETAIL_BYTES) ? cappedNote : value;
|
|
142
|
+
};
|
|
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
|
/**
|
|
@@ -45,6 +45,11 @@ export const PAYLOAD_ON_DEVICE = Object.freeze({
|
|
|
45
45
|
note: "Payload stays on the device — fetched on demand via getActionDetail."
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
/** True when a wire-form value is the payload-on-device sentinel. */
|
|
49
|
+
export function isPayloadOnDevice(value) {
|
|
50
|
+
return typeof value === "object" && value !== null && value.__buoyPayloadOnDevice === true;
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
/**
|
|
49
54
|
* True when a value is one of the state sentinels (evicted or on-device) —
|
|
50
55
|
* guards time travel and any consumer that needs a REAL tree.
|
|
@@ -392,11 +397,16 @@ class ReduxActionStore {
|
|
|
392
397
|
}
|
|
393
398
|
this.actions = actions.slice(0, this.maxActions).map(a => {
|
|
394
399
|
const h = hydrated.get(a.id);
|
|
395
|
-
|
|
400
|
+
if (!h || !isEvictedState(a.prevState)) return a;
|
|
401
|
+
return {
|
|
396
402
|
...a,
|
|
397
403
|
prevState: h.prevState,
|
|
398
|
-
nextState: h.nextState
|
|
399
|
-
|
|
404
|
+
nextState: h.nextState,
|
|
405
|
+
payload: isPayloadOnDevice(a.payload) ? h.payload : a.payload,
|
|
406
|
+
action: isPayloadOnDevice(a.action) ? h.action : a.action,
|
|
407
|
+
meta: isPayloadOnDevice(a.meta?.arg) ? h.meta : a.meta,
|
|
408
|
+
error: isPayloadOnDevice(a.error) ? h.error : a.error
|
|
409
|
+
};
|
|
400
410
|
});
|
|
401
411
|
this.notifyListeners();
|
|
402
412
|
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Time Machine snapshot provider for Redux.
|
|
5
|
+
*
|
|
6
|
+
* Registered into @buoy-gg/shared-ui's stateSnapshotRegistry at module init;
|
|
7
|
+
* consumed by @buoy-gg/time-machine.
|
|
8
|
+
*
|
|
9
|
+
* Restore rides the existing `@@buoy/JUMP_TO_STATE` full-state-replace action:
|
|
10
|
+
* - enhancer path: always handled (wrapReducer).
|
|
11
|
+
* - middleware path: handled ONLY when the app wrapped its root reducer in
|
|
12
|
+
* `withBuoyDevTools()` — unverifiable statically, so restore verifies by
|
|
13
|
+
* reference identity after dispatch (the wrapped reducer returns the payload
|
|
14
|
+
* object itself).
|
|
15
|
+
* - patch path: never handled; restore is reported as unavailable up front.
|
|
16
|
+
*/
|
|
17
|
+
import { registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
|
|
18
|
+
import { boundedDeepEqual, jsonLikelyLargerThan } from "@buoy-gg/shared-ui/dataViewer";
|
|
19
|
+
import { getActiveStore, getInstrumentationStatus } from "./activeStoreRegistry";
|
|
20
|
+
import { BUOY_JUMP_TO_STATE } from "./buoyReduxMiddleware";
|
|
21
|
+
const ENABLE_HINT = "enable restore by using the Buoy enhancer or wrapping your root reducer in withBuoyDevTools()";
|
|
22
|
+
function status() {
|
|
23
|
+
const {
|
|
24
|
+
mechanism,
|
|
25
|
+
hasStore,
|
|
26
|
+
diagnostic
|
|
27
|
+
} = getInstrumentationStatus();
|
|
28
|
+
if (!hasStore) {
|
|
29
|
+
return {
|
|
30
|
+
canCapture: false,
|
|
31
|
+
canRestore: false,
|
|
32
|
+
detail: diagnostic === "no-react-redux" ? "react-redux not installed" : diagnostic === "no-provider" ? "No Redux <Provider> found" : "No Redux store instrumented"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (mechanism === "patch") {
|
|
36
|
+
return {
|
|
37
|
+
canCapture: true,
|
|
38
|
+
canRestore: false,
|
|
39
|
+
detail: `Auto-instrumented store cannot be restored — ${ENABLE_HINT}`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
canCapture: true,
|
|
44
|
+
canRestore: true,
|
|
45
|
+
detail: mechanism === "middleware" ? "Restore requires withBuoyDevTools(rootReducer) — verified at restore time" : undefined
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function capture() {
|
|
49
|
+
const store = getActiveStore();
|
|
50
|
+
if (!store) {
|
|
51
|
+
return {
|
|
52
|
+
data: null,
|
|
53
|
+
warnings: ["no Redux store instrumented"]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
data: {
|
|
58
|
+
state: store.getState()
|
|
59
|
+
},
|
|
60
|
+
warnings: []
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function restore(data, options) {
|
|
64
|
+
const snapshot = data;
|
|
65
|
+
const store = getActiveStore();
|
|
66
|
+
if (!store || snapshot === undefined || snapshot === null) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
applied: 0,
|
|
70
|
+
skipped: [{
|
|
71
|
+
id: "redux",
|
|
72
|
+
reason: store ? "empty redux snapshot" : "no Redux store instrumented"
|
|
73
|
+
}],
|
|
74
|
+
warnings: []
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const {
|
|
78
|
+
mechanism
|
|
79
|
+
} = getInstrumentationStatus();
|
|
80
|
+
if (mechanism === "patch") {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
applied: 0,
|
|
84
|
+
skipped: [{
|
|
85
|
+
id: "redux",
|
|
86
|
+
reason: `restore unavailable on auto-instrumented stores — ${ENABLE_HINT}`
|
|
87
|
+
}],
|
|
88
|
+
warnings: []
|
|
89
|
+
};
|
|
90
|
+
}
|
|
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
|
+
}
|
|
106
|
+
store.dispatch({
|
|
107
|
+
type: BUOY_JUMP_TO_STATE,
|
|
108
|
+
payload
|
|
109
|
+
});
|
|
110
|
+
// The wrapped reducer returns the payload object itself, so reference
|
|
111
|
+
// identity proves the jump was actually handled (and not silently ignored
|
|
112
|
+
// by an unwrapped middleware-path reducer).
|
|
113
|
+
if (store.getState() === payload) {
|
|
114
|
+
const warnings = excluded.length > 0 ? [`${excluded.length} slice(s) excluded by you`] : [];
|
|
115
|
+
return {
|
|
116
|
+
ok: true,
|
|
117
|
+
applied: 1,
|
|
118
|
+
skipped: [],
|
|
119
|
+
warnings
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
applied: 0,
|
|
125
|
+
skipped: [{
|
|
126
|
+
id: "redux",
|
|
127
|
+
reason: `store ignored the state jump — ${ENABLE_HINT}`
|
|
128
|
+
}],
|
|
129
|
+
warnings: []
|
|
130
|
+
};
|
|
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
|
+
}
|
|
213
|
+
registerStateSnapshotProvider({
|
|
214
|
+
id: "redux",
|
|
215
|
+
label: "Redux",
|
|
216
|
+
status,
|
|
217
|
+
capture,
|
|
218
|
+
restore,
|
|
219
|
+
previewItems
|
|
220
|
+
});
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
* });
|
|
51
51
|
* ```
|
|
52
52
|
*/
|
|
53
|
+
import "./redux/utils/snapshotProvider";
|
|
53
54
|
import "./redux/utils/devtoolsHook";
|
|
54
55
|
export { reduxToolPreset, createReduxTool } from "./preset";
|
|
55
56
|
export { buoyReduxMiddleware, createBuoyReduxMiddleware, withBuoyDevTools, jumpToState, replayAction, BUOY_JUMP_TO_STATE, BUOY_REPLAY_ACTION, } from "./redux/utils/buoyReduxMiddleware";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,gCAAgC,CAAC;AAKxC,OAAO,4BAA4B,CAAC;AAKpC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK5D,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mCAAmC,CAAC;AAK3C,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,8BAA8B,CAAC;AAItC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AAKpC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAC;AAKjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAG3E,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,sCAAsC,CAAC;AAO9C,OAAO,EACL,UAAU,EACV,aAAa,EACb,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EACL,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAK7E,YAAY,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,WAAW,EACX,qBAAqB,EACrB,iBAAiB,EACjB,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAKvB,gBAAgB;AAChB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -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;
|
|
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;
|
|
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 } 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
|
|
@@ -38,7 +39,7 @@ export declare const reduxSyncAdapter: {
|
|
|
38
39
|
category: import("../types").ActionCategory;
|
|
39
40
|
sliceName: string | null;
|
|
40
41
|
actionName: string;
|
|
41
|
-
meta?:
|
|
42
|
+
meta?: ActionMeta;
|
|
42
43
|
error?: unknown;
|
|
43
44
|
payloadPreview: string;
|
|
44
45
|
diffSummary: string;
|
|
@@ -62,6 +63,8 @@ export declare const reduxSyncAdapter: {
|
|
|
62
63
|
nextState?: undefined;
|
|
63
64
|
payload?: undefined;
|
|
64
65
|
action?: undefined;
|
|
66
|
+
meta?: undefined;
|
|
67
|
+
error?: undefined;
|
|
65
68
|
} | {
|
|
66
69
|
found: true;
|
|
67
70
|
id: string;
|
|
@@ -71,6 +74,8 @@ export declare const reduxSyncAdapter: {
|
|
|
71
74
|
nextState?: undefined;
|
|
72
75
|
payload?: undefined;
|
|
73
76
|
action?: undefined;
|
|
77
|
+
meta?: undefined;
|
|
78
|
+
error?: undefined;
|
|
74
79
|
} | {
|
|
75
80
|
found: true;
|
|
76
81
|
id: string;
|
|
@@ -78,6 +83,8 @@ export declare const reduxSyncAdapter: {
|
|
|
78
83
|
nextState: unknown;
|
|
79
84
|
payload: unknown;
|
|
80
85
|
action: unknown;
|
|
86
|
+
meta: ActionMeta | undefined;
|
|
87
|
+
error: unknown;
|
|
81
88
|
reason?: undefined;
|
|
82
89
|
evicted?: undefined;
|
|
83
90
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reduxSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/redux/sync/reduxSyncAdapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"reduxSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/redux/sync/reduxSyncAdapter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AA4D3C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB;;IAE3B;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;0BAsBmB,MAAM,IAAI;;;QAK9B;;;;WAIG;kCACuB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA6CjC;;;WAGG;2BACgB,OAAO;;oBAjHZ,MAAM,EAAE;YACtB,4DAA4D;;;;;;YA4HxD,wEAAwE;;;;QAM5E,gEAAgE;2BAC7C,OAAO;;oBApIZ,MAAM,EAAE;YACtB,4DAA4D;;;;;;;;;CA6I/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;
|
|
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,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,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;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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotProvider.d.ts","sourceRoot":"","sources":["../../../../src/redux/utils/snapshotProvider.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/redux",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.18",
|
|
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",
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": [
|
|
28
28
|
"**/devtoolsHook.js",
|
|
29
|
-
"**/devtoolsHook.ts"
|
|
29
|
+
"**/devtoolsHook.ts",
|
|
30
|
+
"**/snapshotProvider.js",
|
|
31
|
+
"**/snapshotProvider.ts"
|
|
30
32
|
],
|
|
31
33
|
"dependencies": {
|
|
32
|
-
"@buoy-gg/shared-ui": "^7.0.
|
|
33
|
-
"@buoy-gg/
|
|
34
|
-
"@buoy-gg/
|
|
34
|
+
"@buoy-gg/shared-ui": "^7.0.18",
|
|
35
|
+
"@buoy-gg/license": "^7.0.18",
|
|
36
|
+
"@buoy-gg/floating-tools-core": "^7.0.18"
|
|
35
37
|
},
|
|
36
38
|
"peerDependencies": {
|
|
37
39
|
"@reduxjs/toolkit": ">=1.9.0",
|