@buoy-gg/redux 7.0.4 → 7.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/lib/commonjs/index.js +44 -0
- package/lib/commonjs/redux/components/ReduxAutoInstrument.js +20 -0
- package/lib/commonjs/redux/components/ReduxModal.js +123 -68
- package/lib/commonjs/redux/components/index.js +8 -1
- package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +32 -42
- package/lib/commonjs/redux/sync/reduxSyncAdapter.js +27 -8
- package/lib/commonjs/redux/utils/activeStoreRegistry.js +73 -0
- package/lib/commonjs/redux/utils/autoInstrument.js +88 -114
- package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +20 -15
- package/lib/commonjs/redux/utils/devtoolsHook.js +137 -0
- package/lib/commonjs/redux/utils/index.js +32 -1
- package/lib/module/index.js +29 -4
- package/lib/module/redux/components/ReduxAutoInstrument.js +17 -0
- package/lib/module/redux/components/ReduxModal.js +122 -68
- package/lib/module/redux/components/index.js +2 -1
- package/lib/module/redux/hooks/useAutoInstrumentRedux.js +33 -43
- package/lib/module/redux/sync/reduxSyncAdapter.js +28 -9
- package/lib/module/redux/utils/activeStoreRegistry.js +63 -0
- package/lib/module/redux/utils/autoInstrument.js +82 -113
- package/lib/module/redux/utils/buoyReduxMiddleware.js +20 -15
- package/lib/module/redux/utils/devtoolsHook.js +130 -0
- package/lib/module/redux/utils/index.js +3 -1
- package/lib/typescript/index.d.ts +19 -5
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/redux/components/ReduxAutoInstrument.d.ts +11 -0
- package/lib/typescript/redux/components/ReduxAutoInstrument.d.ts.map +1 -0
- package/lib/typescript/redux/components/ReduxModal.d.ts +15 -1
- package/lib/typescript/redux/components/ReduxModal.d.ts.map +1 -1
- package/lib/typescript/redux/components/index.d.ts +1 -0
- package/lib/typescript/redux/components/index.d.ts.map +1 -1
- package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts +16 -28
- package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts.map +1 -1
- package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts +18 -4
- package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/redux/utils/activeStoreRegistry.d.ts +47 -0
- package/lib/typescript/redux/utils/activeStoreRegistry.d.ts.map +1 -0
- package/lib/typescript/redux/utils/autoInstrument.d.ts +46 -23
- package/lib/typescript/redux/utils/autoInstrument.d.ts.map +1 -1
- package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts +5 -1
- package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +1 -1
- package/lib/typescript/redux/utils/devtoolsHook.d.ts +45 -0
- package/lib/typescript/redux/utils/devtoolsHook.d.ts.map +1 -0
- package/lib/typescript/redux/utils/index.d.ts +3 -2
- package/lib/typescript/redux/utils/index.d.ts.map +1 -1
- package/package.json +8 -5
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ENHANCER_SYMBOL = void 0;
|
|
7
|
+
exports.getActiveStore = getActiveStore;
|
|
8
|
+
exports.getCaptureMechanism = getCaptureMechanism;
|
|
9
|
+
exports.getInstrumentationStatus = getInstrumentationStatus;
|
|
10
|
+
exports.registerActiveStore = registerActiveStore;
|
|
11
|
+
exports.setReduxDiagnostic = setReduxDiagnostic;
|
|
12
|
+
exports.unregisterActiveStore = unregisterActiveStore;
|
|
13
|
+
/**
|
|
14
|
+
* Shared registry for the one live Redux store Buoy is bound to, plus how it
|
|
15
|
+
* got bound (the capture mechanism) and why binding failed when it did.
|
|
16
|
+
*
|
|
17
|
+
* Lives in its own module so the three capture paths (devtools-hook enhancer,
|
|
18
|
+
* explicit middleware, dispatch-patch fallback) and the sync adapter can all
|
|
19
|
+
* import it without cycles.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/** The minimum store surface Buoy needs (middleware storeApi satisfies this). */
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* How the store got instrumented:
|
|
26
|
+
* - `enhancer` — store-creation-time enhancer via the Redux DevTools global.
|
|
27
|
+
* FULL capture (thunk-internal & RTK Query actions included).
|
|
28
|
+
* - `middleware` — user-added buoyReduxMiddleware. FULL capture.
|
|
29
|
+
* - `patch` — store.dispatch patched after creation (auto-instrumentation
|
|
30
|
+
* fallback). TOP-LEVEL-ONLY capture: actions dispatched from
|
|
31
|
+
* inside middleware/thunks bypass the patch.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** Why there is no bound store, when there isn't one. */
|
|
35
|
+
|
|
36
|
+
/** Marks stores created through the Buoy devtools-hook enhancer. */
|
|
37
|
+
const ENHANCER_SYMBOL = exports.ENHANCER_SYMBOL = Symbol.for("@@buoy/enhancerInstrumented");
|
|
38
|
+
let activeStore = null;
|
|
39
|
+
let mechanism = null;
|
|
40
|
+
let diagnostic = null;
|
|
41
|
+
function registerActiveStore(store, mech) {
|
|
42
|
+
activeStore = store;
|
|
43
|
+
mechanism = mech;
|
|
44
|
+
diagnostic = null;
|
|
45
|
+
}
|
|
46
|
+
function unregisterActiveStore(store) {
|
|
47
|
+
if (activeStore === store) {
|
|
48
|
+
activeStore = null;
|
|
49
|
+
mechanism = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function getActiveStore() {
|
|
53
|
+
return activeStore;
|
|
54
|
+
}
|
|
55
|
+
function getCaptureMechanism() {
|
|
56
|
+
return mechanism;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Recorded by the auto-instrument hook so non-React surfaces (sync adapter,
|
|
61
|
+
* MCP) can report WHY there's no store instead of a generic failure.
|
|
62
|
+
*/
|
|
63
|
+
function setReduxDiagnostic(d) {
|
|
64
|
+
diagnostic = d;
|
|
65
|
+
}
|
|
66
|
+
function getInstrumentationStatus() {
|
|
67
|
+
return {
|
|
68
|
+
mechanism,
|
|
69
|
+
capture: mechanism === "enhancer" || mechanism === "middleware" ? "full" : mechanism === "patch" ? "top-level-only" : "none",
|
|
70
|
+
hasStore: activeStore !== null,
|
|
71
|
+
diagnostic
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -4,26 +4,39 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getActiveStore = getActiveStore;
|
|
7
|
+
Object.defineProperty(exports, "getInstrumentationStatus", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return _activeStoreRegistry.getInstrumentationStatus;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
7
13
|
exports.instrumentStore = instrumentStore;
|
|
8
14
|
exports.isAutoInstrumentActive = isAutoInstrumentActive;
|
|
9
15
|
exports.isStoreInstrumented = isStoreInstrumented;
|
|
10
|
-
exports.isTimeTravelEnabled = isTimeTravelEnabled;
|
|
11
16
|
exports.jumpToState = jumpToState;
|
|
17
|
+
exports.registerReduxStore = registerReduxStore;
|
|
12
18
|
exports.replayAction = replayAction;
|
|
13
19
|
exports.uninstrumentStore = uninstrumentStore;
|
|
14
20
|
var _reduxActionStore = require("./reduxActionStore");
|
|
15
21
|
var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
|
|
22
|
+
var _activeStoreRegistry = require("./activeStoreRegistry");
|
|
16
23
|
/**
|
|
17
|
-
*
|
|
24
|
+
* Fallback auto-instrumentation for Redux stores.
|
|
18
25
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
26
|
+
* The PRIMARY capture path is the store-creation-time enhancer in
|
|
27
|
+
* devtoolsHook.ts (full capture, real time travel). This module is the
|
|
28
|
+
* fallback for stores created before our module loaded: it patches
|
|
29
|
+
* store.dispatch after the fact.
|
|
21
30
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* middleware
|
|
31
|
+
* Fallback limitation (inherent to patching, verified against redux 5.x):
|
|
32
|
+
* `middlewareAPI.dispatch` is a separate closure from `store.dispatch`, so
|
|
33
|
+
* actions dispatched from INSIDE middleware/thunks (RTK pending/fulfilled/
|
|
34
|
+
* rejected, RTK Query) bypass the patch. That's why this mechanism reports
|
|
35
|
+
* capture as "top-level-only" — see activeStoreRegistry.getInstrumentationStatus.
|
|
25
36
|
*/
|
|
26
37
|
|
|
38
|
+
// Re-exported for non-React callers (sync adapter, app code).
|
|
39
|
+
|
|
27
40
|
// ============================================
|
|
28
41
|
// Store Tracking
|
|
29
42
|
// ============================================
|
|
@@ -34,15 +47,6 @@ const INSTRUMENTED_SYMBOL = Symbol.for("@@buoy/instrumented");
|
|
|
34
47
|
/** Symbol to store original dispatch reference */
|
|
35
48
|
const ORIGINAL_DISPATCH_SYMBOL = Symbol.for("@@buoy/originalDispatch");
|
|
36
49
|
|
|
37
|
-
/** Symbol to store original reducer reference */
|
|
38
|
-
const ORIGINAL_REDUCER_SYMBOL = Symbol.for("@@buoy/originalReducer");
|
|
39
|
-
|
|
40
|
-
/** Reference to the active store for time-travel operations */
|
|
41
|
-
let activeStore = null;
|
|
42
|
-
|
|
43
|
-
/** Track if store has been enhanced for time-travel */
|
|
44
|
-
let timeTravelEnabled = false;
|
|
45
|
-
|
|
46
50
|
// ============================================
|
|
47
51
|
// Type Definitions
|
|
48
52
|
// ============================================
|
|
@@ -52,40 +56,44 @@ let timeTravelEnabled = false;
|
|
|
52
56
|
// ============================================
|
|
53
57
|
|
|
54
58
|
/**
|
|
55
|
-
* Check if a store has already been instrumented
|
|
59
|
+
* Check if a store has already been instrumented (either mechanism)
|
|
56
60
|
*/
|
|
57
61
|
function isStoreInstrumented(store) {
|
|
58
|
-
|
|
62
|
+
const s = store;
|
|
63
|
+
return s[INSTRUMENTED_SYMBOL] === true || s[_activeStoreRegistry.ENHANCER_SYMBOL] === true;
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
|
-
* Instrument a Redux store for action capture
|
|
67
|
+
* Instrument a Redux store for action capture (fallback path).
|
|
63
68
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
69
|
+
* Patches store.dispatch to capture top-level dispatches. No-ops (but still
|
|
70
|
+
* registers the store for getState/dispatch) when the enhancer or the
|
|
71
|
+
* explicit middleware is already capturing.
|
|
66
72
|
*
|
|
67
|
-
* @param store - The Redux store to instrument
|
|
68
|
-
* @param options - Configuration options
|
|
69
73
|
* @returns Cleanup function to restore original dispatch
|
|
70
74
|
*/
|
|
71
75
|
function instrumentStore(store, options = {}) {
|
|
72
76
|
const instrumentedStore = store;
|
|
73
77
|
|
|
78
|
+
// Store was created through the devtools-hook enhancer — full capture is
|
|
79
|
+
// already live; just (re)point the registry at it.
|
|
80
|
+
if (instrumentedStore[_activeStoreRegistry.ENHANCER_SYMBOL]) {
|
|
81
|
+
(0, _activeStoreRegistry.registerActiveStore)(store, "enhancer");
|
|
82
|
+
return () => {};
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
// Skip if middleware is already handling action capture
|
|
75
|
-
// This prevents duplicate recording when both mechanisms are active
|
|
76
86
|
if ((0, _buoyReduxMiddleware.isMiddlewareActive)()) {
|
|
77
|
-
|
|
87
|
+
(0, _activeStoreRegistry.registerActiveStore)(store, "middleware");
|
|
78
88
|
return () => {};
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
// Skip if already
|
|
91
|
+
// Skip if already patched — update the registry even so (store swap case)
|
|
82
92
|
if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
|
|
83
|
-
|
|
84
|
-
activeStore = store;
|
|
93
|
+
(0, _activeStoreRegistry.registerActiveStore)(store, "patch");
|
|
85
94
|
return () => {};
|
|
86
95
|
}
|
|
87
96
|
const {
|
|
88
|
-
enableTimeTravel = false,
|
|
89
97
|
maxActions = 200,
|
|
90
98
|
ignoreActions = []
|
|
91
99
|
} = options;
|
|
@@ -136,12 +144,7 @@ function instrumentStore(store, options = {}) {
|
|
|
136
144
|
// Replace dispatch
|
|
137
145
|
store.dispatch = instrumentedDispatch;
|
|
138
146
|
instrumentedStore[INSTRUMENTED_SYMBOL] = true;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// Enable time-travel if requested and store supports replaceReducer
|
|
142
|
-
if (enableTimeTravel && typeof store.replaceReducer === "function") {
|
|
143
|
-
enableTimeTravelSupport(store);
|
|
144
|
-
}
|
|
147
|
+
(0, _activeStoreRegistry.registerActiveStore)(store, "patch");
|
|
145
148
|
|
|
146
149
|
// Return cleanup function
|
|
147
150
|
return () => {
|
|
@@ -150,90 +153,63 @@ function instrumentStore(store, options = {}) {
|
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
/**
|
|
153
|
-
* Remove instrumentation from a store
|
|
156
|
+
* Remove dispatch-patch instrumentation from a store.
|
|
157
|
+
*
|
|
158
|
+
* Only restores the patched dispatch. (This used to also replaceReducer with a
|
|
159
|
+
* synthetic reducer that froze the host store permanently — that path is gone.)
|
|
160
|
+
* Enhancer-created stores are left untouched apart from the registry.
|
|
154
161
|
*/
|
|
155
162
|
function uninstrumentStore(store) {
|
|
156
163
|
const instrumentedStore = store;
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
store.dispatch = originalDispatch;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Restore original reducer if time-travel was enabled
|
|
168
|
-
const originalReducer = instrumentedStore[ORIGINAL_REDUCER_SYMBOL];
|
|
169
|
-
if (originalReducer && typeof store.replaceReducer === "function") {
|
|
170
|
-
store.replaceReducer(originalReducer);
|
|
171
|
-
timeTravelEnabled = false;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Clean up symbols
|
|
175
|
-
delete instrumentedStore[INSTRUMENTED_SYMBOL];
|
|
176
|
-
delete instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
|
|
177
|
-
delete instrumentedStore[ORIGINAL_REDUCER_SYMBOL];
|
|
178
|
-
|
|
179
|
-
// Clear active store reference
|
|
180
|
-
if (activeStore === store) {
|
|
181
|
-
activeStore = null;
|
|
164
|
+
if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
|
|
165
|
+
const originalDispatch = instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
|
|
166
|
+
if (originalDispatch) {
|
|
167
|
+
store.dispatch = originalDispatch;
|
|
168
|
+
}
|
|
169
|
+
delete instrumentedStore[INSTRUMENTED_SYMBOL];
|
|
170
|
+
delete instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
|
|
182
171
|
}
|
|
172
|
+
(0, _activeStoreRegistry.unregisterActiveStore)(store);
|
|
183
173
|
}
|
|
184
174
|
|
|
185
|
-
// ============================================
|
|
186
|
-
// Time-Travel Support
|
|
187
|
-
// ============================================
|
|
188
|
-
|
|
189
175
|
/**
|
|
190
|
-
*
|
|
176
|
+
* Manually register a store with Buoy — the escape hatch for apps that can't
|
|
177
|
+
* render FloatingDevTools inside a react-redux Provider (or don't use
|
|
178
|
+
* react-redux at all). Instruments via the fallback dispatch patch.
|
|
191
179
|
*
|
|
192
|
-
*
|
|
193
|
-
* dynamically, without requiring users to manually wrap their reducer.
|
|
180
|
+
* Prefer the zero-config paths; this exists so no app is ever stuck.
|
|
194
181
|
*/
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// Get current reducer by dispatching a probe action
|
|
199
|
-
// Note: This is a workaround since there's no store.getReducer()
|
|
200
|
-
// We'll wrap whatever reducer is currently active
|
|
201
|
-
const currentState = store.getState();
|
|
202
|
-
|
|
203
|
-
// Create a time-travel enabled reducer wrapper
|
|
204
|
-
const timeTravelReducer = (state = currentState, action) => {
|
|
205
|
-
// Handle jump to state action
|
|
206
|
-
if (action.type === _buoyReduxMiddleware.BUOY_JUMP_TO_STATE) {
|
|
207
|
-
return action.payload;
|
|
208
|
-
}
|
|
182
|
+
function registerReduxStore(store, options = {}) {
|
|
183
|
+
return instrumentStore(store, options);
|
|
184
|
+
}
|
|
209
185
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return state;
|
|
214
|
-
};
|
|
186
|
+
// ============================================
|
|
187
|
+
// Time-Travel / Introspection
|
|
188
|
+
// ============================================
|
|
215
189
|
|
|
216
|
-
|
|
217
|
-
// This approach has limitations - full time-travel works better with
|
|
218
|
-
// the explicit withBuoyDevTools wrapper
|
|
219
|
-
instrumentedStore[ORIGINAL_REDUCER_SYMBOL] = timeTravelReducer;
|
|
220
|
-
timeTravelEnabled = true;
|
|
221
|
-
}
|
|
190
|
+
let warnedJumpUnsupported = false;
|
|
222
191
|
|
|
223
192
|
/**
|
|
224
|
-
* Jump to a specific state (time-travel)
|
|
193
|
+
* Jump to a specific state (time-travel).
|
|
225
194
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
195
|
+
* Real on the enhancer path (reducer is wrapped from creation) and on the
|
|
196
|
+
* middleware path when the app used `withBuoyDevTools`. On the bare patch
|
|
197
|
+
* path there's no reducer wrapper, so the jump action is ignored — we detect
|
|
198
|
+
* that and warn once instead of pretending it worked.
|
|
228
199
|
*/
|
|
229
200
|
function jumpToState(state) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
type: _buoyReduxMiddleware.BUOY_JUMP_TO_STATE,
|
|
233
|
-
payload: state
|
|
234
|
-
});
|
|
235
|
-
} else {
|
|
201
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
202
|
+
if (!store) {
|
|
236
203
|
console.warn("[BuoyRedux] Cannot jump to state - no store instrumented");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
store.dispatch({
|
|
207
|
+
type: _buoyReduxMiddleware.BUOY_JUMP_TO_STATE,
|
|
208
|
+
payload: state
|
|
209
|
+
});
|
|
210
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && !warnedJumpUnsupported && store.getState() !== state) {
|
|
211
|
+
warnedJumpUnsupported = true;
|
|
212
|
+
console.warn("[BuoyRedux] Time-travel jump was ignored by the store. Wrap your root " + "reducer with withBuoyDevTools(), or ensure @buoy-gg/redux is imported " + "before your store is created (enhancer path) for real time travel.");
|
|
237
213
|
}
|
|
238
214
|
}
|
|
239
215
|
|
|
@@ -241,30 +217,28 @@ function jumpToState(state) {
|
|
|
241
217
|
* Replay an action (dispatch it again)
|
|
242
218
|
*/
|
|
243
219
|
function replayAction(action) {
|
|
244
|
-
|
|
245
|
-
|
|
220
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
221
|
+
if (store) {
|
|
222
|
+
store.dispatch(action);
|
|
246
223
|
} else {
|
|
247
224
|
console.warn("[BuoyRedux] Cannot replay action - no store instrumented");
|
|
248
225
|
}
|
|
249
226
|
}
|
|
250
227
|
|
|
251
228
|
/**
|
|
252
|
-
* Get the currently active (instrumented) store
|
|
229
|
+
* Get the currently active (instrumented) store.
|
|
230
|
+
*
|
|
231
|
+
* NOTE: on the middleware path this is the middleware `storeApi`
|
|
232
|
+
* ({getState, dispatch}) rather than the full Store object — every consumer
|
|
233
|
+
* in this package only needs those two.
|
|
253
234
|
*/
|
|
254
235
|
function getActiveStore() {
|
|
255
|
-
return
|
|
236
|
+
return (0, _activeStoreRegistry.getActiveStore)();
|
|
256
237
|
}
|
|
257
238
|
|
|
258
239
|
/**
|
|
259
240
|
* Check if auto-instrumentation is active
|
|
260
241
|
*/
|
|
261
242
|
function isAutoInstrumentActive() {
|
|
262
|
-
return
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Check if time-travel support is enabled
|
|
267
|
-
*/
|
|
268
|
-
function isTimeTravelEnabled() {
|
|
269
|
-
return timeTravelEnabled;
|
|
243
|
+
return (0, _activeStoreRegistry.getActiveStore)() !== null;
|
|
270
244
|
}
|
|
@@ -10,6 +10,7 @@ exports.jumpToState = jumpToState;
|
|
|
10
10
|
exports.replayAction = replayAction;
|
|
11
11
|
exports.withBuoyDevTools = withBuoyDevTools;
|
|
12
12
|
var _reduxActionStore = require("./reduxActionStore");
|
|
13
|
+
var _activeStoreRegistry = require("./activeStoreRegistry");
|
|
13
14
|
/**
|
|
14
15
|
* Buoy Redux Middleware for Redux Toolkit
|
|
15
16
|
*
|
|
@@ -47,20 +48,22 @@ function isMiddlewareActive() {
|
|
|
47
48
|
|
|
48
49
|
const BUOY_JUMP_TO_STATE = exports.BUOY_JUMP_TO_STATE = "@@buoy/JUMP_TO_STATE";
|
|
49
50
|
const BUOY_REPLAY_ACTION = exports.BUOY_REPLAY_ACTION = "@@buoy/REPLAY_ACTION";
|
|
50
|
-
// Store reference to dispatch for time-travel
|
|
51
|
-
let storeDispatch = null;
|
|
52
|
-
|
|
53
51
|
/**
|
|
54
|
-
* Jump to a specific state (time-travel)
|
|
52
|
+
* Jump to a specific state (time-travel).
|
|
53
|
+
*
|
|
54
|
+
* Registry-backed, so it works on EVERY capture path (enhancer, middleware,
|
|
55
|
+
* patch) — previously this only worked when the middleware was installed,
|
|
56
|
+
* which silently broke the modal's Jump button on the zero-config path.
|
|
55
57
|
*/
|
|
56
58
|
function jumpToState(state) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
60
|
+
if (store) {
|
|
61
|
+
store.dispatch({
|
|
59
62
|
type: BUOY_JUMP_TO_STATE,
|
|
60
63
|
payload: state
|
|
61
64
|
});
|
|
62
65
|
} else {
|
|
63
|
-
console.warn("[BuoyRedux] Cannot jump to state -
|
|
66
|
+
console.warn("[BuoyRedux] Cannot jump to state - no store instrumented");
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -68,11 +71,11 @@ function jumpToState(state) {
|
|
|
68
71
|
* Replay an action (dispatch it again)
|
|
69
72
|
*/
|
|
70
73
|
function replayAction(action) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
const store = (0, _activeStoreRegistry.getActiveStore)();
|
|
75
|
+
if (store) {
|
|
76
|
+
store.dispatch(action);
|
|
74
77
|
} else {
|
|
75
|
-
console.warn("[BuoyRedux] Cannot replay action -
|
|
78
|
+
console.warn("[BuoyRedux] Cannot replay action - no store instrumented");
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -107,8 +110,10 @@ const buoyReduxMiddleware = storeApi => {
|
|
|
107
110
|
// Mark middleware as active so auto-instrument knows to skip
|
|
108
111
|
middlewareActive = true;
|
|
109
112
|
|
|
110
|
-
//
|
|
111
|
-
|
|
113
|
+
// Register as the active store so getState/dispatch (MCP, desktop, time
|
|
114
|
+
// travel) work on the middleware path too — storeApi has both getState and
|
|
115
|
+
// dispatch, which is all the registry needs.
|
|
116
|
+
(0, _activeStoreRegistry.registerActiveStore)(storeApi, "middleware");
|
|
112
117
|
return next => action => {
|
|
113
118
|
const actionObj = action;
|
|
114
119
|
const actionType = actionObj.type;
|
|
@@ -141,8 +146,8 @@ function createBuoyReduxMiddleware(options = {}) {
|
|
|
141
146
|
// Mark middleware as active so auto-instrument knows to skip
|
|
142
147
|
middlewareActive = true;
|
|
143
148
|
|
|
144
|
-
//
|
|
145
|
-
|
|
149
|
+
// Register as the active store (see buoyReduxMiddleware above).
|
|
150
|
+
(0, _activeStoreRegistry.registerActiveStore)(storeApi, "middleware");
|
|
146
151
|
return next => action => {
|
|
147
152
|
const actionObj = action;
|
|
148
153
|
const actionType = actionObj.type || "";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buoyDevtoolsEnhancer = void 0;
|
|
7
|
+
exports.claimDevtoolsHook = claimDevtoolsHook;
|
|
8
|
+
exports.isDevtoolsHookClaimed = isDevtoolsHookClaimed;
|
|
9
|
+
var _reduxActionStore = require("./reduxActionStore");
|
|
10
|
+
var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
|
|
11
|
+
var _activeStoreRegistry = require("./activeStoreRegistry");
|
|
12
|
+
/**
|
|
13
|
+
* Store-creation-time capture via the OFFICIAL Redux DevTools hook.
|
|
14
|
+
*
|
|
15
|
+
* `configureStore` defaults `devTools: true` and, at RTK module-evaluation
|
|
16
|
+
* time, snapshots `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` if defined.
|
|
17
|
+
* That global doesn't exist in React Native — so Buoy claims it (dev builds
|
|
18
|
+
* only, and only when nothing else has). This makes Buoy a real store
|
|
19
|
+
* ENHANCER, present from the store's birth:
|
|
20
|
+
*
|
|
21
|
+
* - FULL capture: actions dispatched from inside middleware/thunks (RTK
|
|
22
|
+
* `pending`/`fulfilled`/`rejected`, RTK Query) hit our dispatch because the
|
|
23
|
+
* enhancer sits BELOW applyMiddleware in the compose chain.
|
|
24
|
+
* - Real time travel: we wrap the reducer, so BUOY_JUMP_TO_STATE actually
|
|
25
|
+
* jumps — and replaceReducer is re-wrapped, so hosts that inject reducers
|
|
26
|
+
* dynamically keep working.
|
|
27
|
+
* - Nothing is monkey-patched: this is the integration point Redux itself
|
|
28
|
+
* ships for devtools (same one react-native-debugger used).
|
|
29
|
+
*
|
|
30
|
+
* Race note: RTK snapshots the global when ITS module first evaluates, so this
|
|
31
|
+
* module must load before `@reduxjs/toolkit` does. `@buoy-gg/core` requires us
|
|
32
|
+
* at module scope, which usually wins; when it doesn't, the dispatch-patch
|
|
33
|
+
* fallback in autoInstrument.ts still binds at first mount. Apps that want a
|
|
34
|
+
* guarantee put `import '@buoy-gg/redux';` as the FIRST import of their entry.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/** Standard redux compose. */
|
|
38
|
+
function composeFns(...funcs) {
|
|
39
|
+
if (funcs.length === 0) return arg => arg;
|
|
40
|
+
if (funcs.length === 1) return funcs[0];
|
|
41
|
+
return funcs.reduce((a, b) => (...args) => a(b(...args)));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Wrap a reducer so BUOY_JUMP_TO_STATE performs a real state jump. */
|
|
45
|
+
function wrapReducer(reducer) {
|
|
46
|
+
return (state, action) => {
|
|
47
|
+
if (action && action.type === _buoyReduxMiddleware.BUOY_JUMP_TO_STATE) {
|
|
48
|
+
return action.payload;
|
|
49
|
+
}
|
|
50
|
+
return reducer(state, action);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Should this action be recorded? (Mirrors autoInstrument's filters.) */
|
|
55
|
+
function isRecordable(action) {
|
|
56
|
+
if (typeof action !== "object" || action === null) return false;
|
|
57
|
+
const type = action.type;
|
|
58
|
+
if (typeof type !== "string") return false;
|
|
59
|
+
return !type.startsWith("@@buoy/") && !type.startsWith("@@redux/");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The Buoy store enhancer. Innermost in the compose chain (below
|
|
64
|
+
* applyMiddleware), so every action that reaches the reducer — including ones
|
|
65
|
+
* dispatched from inside middleware and thunks — passes through here.
|
|
66
|
+
*/
|
|
67
|
+
const buoyDevtoolsEnhancer = createStore => (reducer, preloadedState) => {
|
|
68
|
+
const store = createStore(wrapReducer(reducer), preloadedState);
|
|
69
|
+
const dispatch = action => {
|
|
70
|
+
// If the explicit middleware is installed it already records — the
|
|
71
|
+
// enhancer stays passive to avoid double capture.
|
|
72
|
+
if (!isRecordable(action) || (0, _buoyReduxMiddleware.isMiddlewareActive)()) {
|
|
73
|
+
return store.dispatch(action);
|
|
74
|
+
}
|
|
75
|
+
const startTime = performance.now();
|
|
76
|
+
const prevState = store.getState();
|
|
77
|
+
const result = store.dispatch(action);
|
|
78
|
+
_reduxActionStore.reduxActionStore.addAction(action, prevState, store.getState(), performance.now() - startTime);
|
|
79
|
+
return result;
|
|
80
|
+
};
|
|
81
|
+
const enhanced = {
|
|
82
|
+
...store,
|
|
83
|
+
dispatch,
|
|
84
|
+
// Keep time travel alive across dynamic reducer injection.
|
|
85
|
+
replaceReducer: next => store.replaceReducer(wrapReducer(next)),
|
|
86
|
+
[_activeStoreRegistry.ENHANCER_SYMBOL]: true
|
|
87
|
+
};
|
|
88
|
+
(0, _activeStoreRegistry.registerActiveStore)(enhanced, "enhancer");
|
|
89
|
+
return enhanced;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Implements the `__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` calling conventions:
|
|
94
|
+
* - `compose(options)(...enhancers)` — what RTK's configureStore does
|
|
95
|
+
* - `compose(...enhancers)` — classic createStore usage from the Redux docs
|
|
96
|
+
*/
|
|
97
|
+
exports.buoyDevtoolsEnhancer = buoyDevtoolsEnhancer;
|
|
98
|
+
function buoyComposeWithDevTools(...args) {
|
|
99
|
+
if (args.length === 0) {
|
|
100
|
+
return composeFns(buoyDevtoolsEnhancer);
|
|
101
|
+
}
|
|
102
|
+
if (args.length === 1 && typeof args[0] === "object" && args[0] !== null) {
|
|
103
|
+
// Options form → return a compose function.
|
|
104
|
+
return (...enhancers) => composeFns(...enhancers, buoyDevtoolsEnhancer);
|
|
105
|
+
}
|
|
106
|
+
return composeFns(...args, buoyDevtoolsEnhancer);
|
|
107
|
+
}
|
|
108
|
+
let claimed = false;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Claim the DevTools globals — dev builds only, and never over an existing
|
|
112
|
+
* claimant (e.g. the real browser extension on web).
|
|
113
|
+
*
|
|
114
|
+
* Idempotent; runs at module scope so simply importing `@buoy-gg/redux` (which
|
|
115
|
+
* `@buoy-gg/core` does at module scope) performs the claim as early as
|
|
116
|
+
* possible.
|
|
117
|
+
*/
|
|
118
|
+
function claimDevtoolsHook() {
|
|
119
|
+
const isDev = typeof __DEV__ !== "undefined" && __DEV__;
|
|
120
|
+
if (!isDev) return false;
|
|
121
|
+
const g = globalThis;
|
|
122
|
+
if (!g.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
|
|
123
|
+
g.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = buoyComposeWithDevTools;
|
|
124
|
+
claimed = true;
|
|
125
|
+
}
|
|
126
|
+
if (!g.__REDUX_DEVTOOLS_EXTENSION__) {
|
|
127
|
+
// Legacy single-enhancer form: `createStore(reducer, __REDUX_DEVTOOLS_EXTENSION__())`
|
|
128
|
+
g.__REDUX_DEVTOOLS_EXTENSION__ = () => buoyDevtoolsEnhancer;
|
|
129
|
+
}
|
|
130
|
+
return claimed;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Whether Buoy owns the DevTools compose global (i.e. won the load race). */
|
|
134
|
+
function isDevtoolsHookClaimed() {
|
|
135
|
+
return claimed;
|
|
136
|
+
}
|
|
137
|
+
claimDevtoolsHook();
|
|
@@ -27,12 +27,24 @@ Object.defineProperty(exports, "autoReplayAction", {
|
|
|
27
27
|
return _autoInstrument.replayAction;
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
+
Object.defineProperty(exports, "buoyDevtoolsEnhancer", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get: function () {
|
|
33
|
+
return _devtoolsHook.buoyDevtoolsEnhancer;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
30
36
|
Object.defineProperty(exports, "buoyReduxMiddleware", {
|
|
31
37
|
enumerable: true,
|
|
32
38
|
get: function () {
|
|
33
39
|
return _buoyReduxMiddleware.buoyReduxMiddleware;
|
|
34
40
|
}
|
|
35
41
|
});
|
|
42
|
+
Object.defineProperty(exports, "claimDevtoolsHook", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
get: function () {
|
|
45
|
+
return _devtoolsHook.claimDevtoolsHook;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
36
48
|
Object.defineProperty(exports, "createBuoyReduxMiddleware", {
|
|
37
49
|
enumerable: true,
|
|
38
50
|
get: function () {
|
|
@@ -51,6 +63,12 @@ Object.defineProperty(exports, "getActiveStore", {
|
|
|
51
63
|
return _autoInstrument.getActiveStore;
|
|
52
64
|
}
|
|
53
65
|
});
|
|
66
|
+
Object.defineProperty(exports, "getInstrumentationStatus", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function () {
|
|
69
|
+
return _autoInstrument.getInstrumentationStatus;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
54
72
|
Object.defineProperty(exports, "instrumentStore", {
|
|
55
73
|
enumerable: true,
|
|
56
74
|
get: function () {
|
|
@@ -63,6 +81,12 @@ Object.defineProperty(exports, "isAutoInstrumentActive", {
|
|
|
63
81
|
return _autoInstrument.isAutoInstrumentActive;
|
|
64
82
|
}
|
|
65
83
|
});
|
|
84
|
+
Object.defineProperty(exports, "isDevtoolsHookClaimed", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: function () {
|
|
87
|
+
return _devtoolsHook.isDevtoolsHookClaimed;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
66
90
|
Object.defineProperty(exports, "isStoreInstrumented", {
|
|
67
91
|
enumerable: true,
|
|
68
92
|
get: function () {
|
|
@@ -87,6 +111,12 @@ Object.defineProperty(exports, "reduxHistoryAdapter", {
|
|
|
87
111
|
return _createReduxHistoryAdapter.reduxHistoryAdapter;
|
|
88
112
|
}
|
|
89
113
|
});
|
|
114
|
+
Object.defineProperty(exports, "registerReduxStore", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: function () {
|
|
117
|
+
return _autoInstrument.registerReduxStore;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
90
120
|
Object.defineProperty(exports, "replayAction", {
|
|
91
121
|
enumerable: true,
|
|
92
122
|
get: function () {
|
|
@@ -108,4 +138,5 @@ Object.defineProperty(exports, "withBuoyDevTools", {
|
|
|
108
138
|
var _reduxActionStore = require("./reduxActionStore");
|
|
109
139
|
var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
|
|
110
140
|
var _createReduxHistoryAdapter = require("./createReduxHistoryAdapter");
|
|
111
|
-
var _autoInstrument = require("./autoInstrument");
|
|
141
|
+
var _autoInstrument = require("./autoInstrument");
|
|
142
|
+
var _devtoolsHook = require("./devtoolsHook");
|