@buoy-gg/redux 7.0.38 → 7.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/lib/commonjs/index.js +256 -1
  2. package/lib/commonjs/preset.js +90 -1
  3. package/lib/commonjs/redux/components/ReduxActionButton.js +131 -1
  4. package/lib/commonjs/redux/components/ReduxActionDetailContent.js +437 -1
  5. package/lib/commonjs/redux/components/ReduxActionDetailView.js +401 -1
  6. package/lib/commonjs/redux/components/ReduxActionInfoView.js +836 -1
  7. package/lib/commonjs/redux/components/ReduxActionItem.js +334 -1
  8. package/lib/commonjs/redux/components/ReduxAutoInstrument.js +20 -1
  9. package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +50 -1
  10. package/lib/commonjs/redux/components/ReduxIcon.js +18 -1
  11. package/lib/commonjs/redux/components/ReduxModal.js +703 -1
  12. package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +187 -1
  13. package/lib/commonjs/redux/hooks/useReduxActions.js +58 -1
  14. package/lib/commonjs/redux/sync/reduxSyncAdapter.js +191 -1
  15. package/lib/commonjs/redux/utils/activeStoreRegistry.js +104 -1
  16. package/lib/commonjs/redux/utils/autoInstrument.js +231 -1
  17. package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +202 -1
  18. package/lib/commonjs/redux/utils/createReduxHistoryAdapter.js +146 -1
  19. package/lib/commonjs/redux/utils/devtoolsHook.js +152 -1
  20. package/lib/commonjs/redux/utils/reduxActionStore.js +473 -1
  21. package/lib/commonjs/redux/utils/snapshotProvider.js +158 -1
  22. package/lib/commonjs/redux/utils/testIDs.js +75 -1
  23. package/lib/module/index.js +125 -1
  24. package/lib/module/preset.js +87 -1
  25. package/lib/module/redux/components/ReduxActionButton.js +128 -1
  26. package/lib/module/redux/components/ReduxActionDetailContent.js +433 -1
  27. package/lib/module/redux/components/ReduxActionDetailView.js +397 -1
  28. package/lib/module/redux/components/ReduxActionInfoView.js +831 -1
  29. package/lib/module/redux/components/ReduxActionItem.js +330 -1
  30. package/lib/module/redux/components/ReduxAutoInstrument.js +17 -1
  31. package/lib/module/redux/components/ReduxDetailViewToggle.js +45 -1
  32. package/lib/module/redux/components/ReduxIcon.js +8 -1
  33. package/lib/module/redux/components/ReduxModal.js +698 -1
  34. package/lib/module/redux/hooks/useAutoInstrumentRedux.js +183 -1
  35. package/lib/module/redux/hooks/useReduxActions.js +54 -1
  36. package/lib/module/redux/sync/reduxSyncAdapter.js +188 -1
  37. package/lib/module/redux/utils/activeStoreRegistry.js +92 -1
  38. package/lib/module/redux/utils/autoInstrument.js +201 -1
  39. package/lib/module/redux/utils/buoyReduxMiddleware.js +188 -1
  40. package/lib/module/redux/utils/createReduxHistoryAdapter.js +142 -1
  41. package/lib/module/redux/utils/devtoolsHook.js +145 -1
  42. package/lib/module/redux/utils/reduxActionStore.js +465 -1
  43. package/lib/module/redux/utils/snapshotProvider.js +157 -1
  44. package/lib/module/redux/utils/testIDs.js +68 -1
  45. package/lib/typescript/index.d.ts.map +1 -0
  46. package/lib/typescript/preset.d.ts.map +1 -0
  47. package/lib/typescript/redux/components/ReduxActionButton.d.ts.map +1 -0
  48. package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +1 -0
  49. package/lib/typescript/redux/components/ReduxActionDetailView.d.ts.map +1 -0
  50. package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +1 -0
  51. package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +1 -0
  52. package/lib/typescript/redux/components/ReduxAutoInstrument.d.ts.map +1 -0
  53. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +1 -0
  54. package/lib/typescript/redux/components/ReduxIcon.d.ts.map +1 -0
  55. package/lib/typescript/redux/components/ReduxModal.d.ts.map +1 -0
  56. package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts.map +1 -0
  57. package/lib/typescript/redux/hooks/useReduxActions.d.ts.map +1 -0
  58. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +1 -0
  59. package/lib/typescript/redux/types/index.d.ts.map +1 -0
  60. package/lib/typescript/redux/utils/activeStoreRegistry.d.ts.map +1 -0
  61. package/lib/typescript/redux/utils/autoInstrument.d.ts.map +1 -0
  62. package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +1 -0
  63. package/lib/typescript/redux/utils/createReduxHistoryAdapter.d.ts.map +1 -0
  64. package/lib/typescript/redux/utils/devtoolsHook.d.ts.map +1 -0
  65. package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +1 -0
  66. package/lib/typescript/redux/utils/snapshotProvider.d.ts.map +1 -0
  67. package/lib/typescript/redux/utils/testIDs.d.ts.map +1 -0
  68. package/package.json +4 -4
@@ -1 +1,231 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActiveStore=getActiveStore,Object.defineProperty(exports,"getInstrumentationStatus",{enumerable:!0,get:function(){return _activeStoreRegistry.getInstrumentationStatus}}),exports.instrumentStore=instrumentStore,exports.isAutoInstrumentActive=isAutoInstrumentActive,exports.isStoreInstrumented=isStoreInstrumented,Object.defineProperty(exports,"isTimeTravelAvailable",{enumerable:!0,get:function(){return _buoyReduxMiddleware.isTimeTravelAvailable}}),Object.defineProperty(exports,"jumpToState",{enumerable:!0,get:function(){return _buoyReduxMiddleware.jumpToState}}),exports.registerReduxStore=registerReduxStore,Object.defineProperty(exports,"replayAction",{enumerable:!0,get:function(){return _buoyReduxMiddleware.replayAction}}),exports.uninstrumentStore=uninstrumentStore;var _reduxActionStore=require("./reduxActionStore"),_buoyReduxMiddleware=require("./buoyReduxMiddleware"),_activeStoreRegistry=require("./activeStoreRegistry");const INSTRUMENTED_SYMBOL=Symbol.for("@@buoy/instrumented"),ORIGINAL_DISPATCH_SYMBOL=Symbol.for("@@buoy/originalDispatch");function isStoreInstrumented(e){const t=e;return!0===t[INSTRUMENTED_SYMBOL]||!0===t[_activeStoreRegistry.ENHANCER_SYMBOL]}function instrumentStore(e,t={}){const r=e;if(r[_activeStoreRegistry.ENHANCER_SYMBOL])return(0,_activeStoreRegistry.registerActiveStore)(e,"enhancer"),()=>{};if((0,_buoyReduxMiddleware.isMiddlewareActive)())return(0,_activeStoreRegistry.registerActiveStore)(e,"middleware"),()=>{};if(r[INSTRUMENTED_SYMBOL])return(0,_activeStoreRegistry.registerActiveStore)(e,"patch"),()=>{};const{maxActions:i=200,ignoreActions:n=[]}=t;_reduxActionStore.reduxActionStore.setMaxActions(i);const o=e.dispatch.bind(e);return r[ORIGINAL_DISPATCH_SYMBOL]=o,e.dispatch=t=>{if("object"!=typeof t||null===t||"string"!=typeof t.type)return o(t);const r=t.type;if(r.startsWith("@@buoy/"))return o(t);if(r.startsWith("@@redux/"))return o(t);if(n.includes(r))return o(t);const i=performance.now(),u=e.getState(),s=o(t),c=e.getState(),S=performance.now()-i;return _reduxActionStore.reduxActionStore.addAction(t,u,c,S),s},r[INSTRUMENTED_SYMBOL]=!0,(0,_activeStoreRegistry.registerActiveStore)(e,"patch"),()=>{uninstrumentStore(e)}}function uninstrumentStore(e){const t=e;if(t[INSTRUMENTED_SYMBOL]){const r=t[ORIGINAL_DISPATCH_SYMBOL];r&&(e.dispatch=r),delete t[INSTRUMENTED_SYMBOL],delete t[ORIGINAL_DISPATCH_SYMBOL]}(0,_activeStoreRegistry.unregisterActiveStore)(e)}function registerReduxStore(e,t={}){return instrumentStore(e,t)}function getActiveStore(){return(0,_activeStoreRegistry.getActiveStore)()}function isAutoInstrumentActive(){return null!==(0,_activeStoreRegistry.getActiveStore)()}
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getActiveStore = getActiveStore;
7
+ Object.defineProperty(exports, "getInstrumentationStatus", {
8
+ enumerable: true,
9
+ get: function () {
10
+ return _activeStoreRegistry.getInstrumentationStatus;
11
+ }
12
+ });
13
+ exports.instrumentStore = instrumentStore;
14
+ exports.isAutoInstrumentActive = isAutoInstrumentActive;
15
+ exports.isStoreInstrumented = isStoreInstrumented;
16
+ Object.defineProperty(exports, "isTimeTravelAvailable", {
17
+ enumerable: true,
18
+ get: function () {
19
+ return _buoyReduxMiddleware.isTimeTravelAvailable;
20
+ }
21
+ });
22
+ Object.defineProperty(exports, "jumpToState", {
23
+ enumerable: true,
24
+ get: function () {
25
+ return _buoyReduxMiddleware.jumpToState;
26
+ }
27
+ });
28
+ exports.registerReduxStore = registerReduxStore;
29
+ Object.defineProperty(exports, "replayAction", {
30
+ enumerable: true,
31
+ get: function () {
32
+ return _buoyReduxMiddleware.replayAction;
33
+ }
34
+ });
35
+ exports.uninstrumentStore = uninstrumentStore;
36
+ var _reduxActionStore = require("./reduxActionStore");
37
+ var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
38
+ var _activeStoreRegistry = require("./activeStoreRegistry");
39
+ /**
40
+ * Fallback auto-instrumentation for Redux stores.
41
+ *
42
+ * The PRIMARY capture path is the store-creation-time enhancer in
43
+ * devtoolsHook.ts (full capture, real time travel). This module is the
44
+ * fallback for stores created before our module loaded: it patches
45
+ * store.dispatch after the fact.
46
+ *
47
+ * Fallback limitation (inherent to patching, verified against redux 5.x):
48
+ * `middlewareAPI.dispatch` is a separate closure from `store.dispatch`, so
49
+ * actions dispatched from INSIDE middleware/thunks (RTK pending/fulfilled/
50
+ * rejected, RTK Query) bypass the patch. That's why this mechanism reports
51
+ * capture as "top-level-only" — see activeStoreRegistry.getInstrumentationStatus.
52
+ */
53
+
54
+ // Re-exported for non-React callers (sync adapter, app code).
55
+
56
+ // ============================================
57
+ // Store Tracking
58
+ // ============================================
59
+
60
+ /** Symbol to mark stores that have been instrumented */
61
+ const INSTRUMENTED_SYMBOL = Symbol.for("@@buoy/instrumented");
62
+
63
+ /** Symbol to store original dispatch reference */
64
+ const ORIGINAL_DISPATCH_SYMBOL = Symbol.for("@@buoy/originalDispatch");
65
+
66
+ // ============================================
67
+ // Type Definitions
68
+ // ============================================
69
+
70
+ // ============================================
71
+ // Core Instrumentation
72
+ // ============================================
73
+
74
+ /**
75
+ * Check if a store has already been instrumented (either mechanism)
76
+ */
77
+ function isStoreInstrumented(store) {
78
+ const s = store;
79
+ return s[INSTRUMENTED_SYMBOL] === true || s[_activeStoreRegistry.ENHANCER_SYMBOL] === true;
80
+ }
81
+
82
+ /**
83
+ * Instrument a Redux store for action capture (fallback path).
84
+ *
85
+ * Patches store.dispatch to capture top-level dispatches. No-ops (but still
86
+ * registers the store for getState/dispatch) when the enhancer or the
87
+ * explicit middleware is already capturing.
88
+ *
89
+ * @returns Cleanup function to restore original dispatch
90
+ */
91
+ function instrumentStore(store, options = {}) {
92
+ const instrumentedStore = store;
93
+
94
+ // Store was created through the devtools-hook enhancer — full capture is
95
+ // already live; just (re)point the registry at it.
96
+ if (instrumentedStore[_activeStoreRegistry.ENHANCER_SYMBOL]) {
97
+ (0, _activeStoreRegistry.registerActiveStore)(store, "enhancer");
98
+ return () => {};
99
+ }
100
+
101
+ // Skip if middleware is already handling action capture
102
+ if ((0, _buoyReduxMiddleware.isMiddlewareActive)()) {
103
+ (0, _activeStoreRegistry.registerActiveStore)(store, "middleware");
104
+ return () => {};
105
+ }
106
+
107
+ // Skip if already patched — update the registry even so (store swap case)
108
+ if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
109
+ (0, _activeStoreRegistry.registerActiveStore)(store, "patch");
110
+ return () => {};
111
+ }
112
+ const {
113
+ maxActions = 200,
114
+ ignoreActions = []
115
+ } = options;
116
+
117
+ // Configure store
118
+ _reduxActionStore.reduxActionStore.setMaxActions(maxActions);
119
+
120
+ // Store original dispatch
121
+ const originalDispatch = store.dispatch.bind(store);
122
+ instrumentedStore[ORIGINAL_DISPATCH_SYMBOL] = originalDispatch;
123
+
124
+ // Create instrumented dispatch
125
+ const instrumentedDispatch = action => {
126
+ // Skip if action is not a plain object with a type property
127
+ // This filters out thunk functions, promises, and other non-action dispatches
128
+ if (typeof action !== "object" || action === null || typeof action.type !== "string") {
129
+ return originalDispatch(action);
130
+ }
131
+ const actionType = action.type;
132
+
133
+ // Don't record devtools internal actions
134
+ if (actionType.startsWith("@@buoy/")) {
135
+ return originalDispatch(action);
136
+ }
137
+
138
+ // Don't record Redux internal actions
139
+ if (actionType.startsWith("@@redux/")) {
140
+ return originalDispatch(action);
141
+ }
142
+
143
+ // Check ignore list
144
+ if (ignoreActions.includes(actionType)) {
145
+ return originalDispatch(action);
146
+ }
147
+
148
+ // Capture timing and state
149
+ const startTime = performance.now();
150
+ const prevState = store.getState();
151
+ const result = originalDispatch(action);
152
+ const nextState = store.getState();
153
+ const duration = performance.now() - startTime;
154
+
155
+ // Record the action
156
+ _reduxActionStore.reduxActionStore.addAction(action, prevState, nextState, duration);
157
+ return result;
158
+ };
159
+
160
+ // Replace dispatch
161
+ store.dispatch = instrumentedDispatch;
162
+ instrumentedStore[INSTRUMENTED_SYMBOL] = true;
163
+ (0, _activeStoreRegistry.registerActiveStore)(store, "patch");
164
+
165
+ // Return cleanup function
166
+ return () => {
167
+ uninstrumentStore(store);
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Remove dispatch-patch instrumentation from a store.
173
+ *
174
+ * Only restores the patched dispatch. (This used to also replaceReducer with a
175
+ * synthetic reducer that froze the host store permanently — that path is gone.)
176
+ * Enhancer-created stores are left untouched apart from the registry.
177
+ */
178
+ function uninstrumentStore(store) {
179
+ const instrumentedStore = store;
180
+ if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
181
+ const originalDispatch = instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
182
+ if (originalDispatch) {
183
+ store.dispatch = originalDispatch;
184
+ }
185
+ delete instrumentedStore[INSTRUMENTED_SYMBOL];
186
+ delete instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
187
+ }
188
+ (0, _activeStoreRegistry.unregisterActiveStore)(store);
189
+ }
190
+
191
+ /**
192
+ * Manually register a store with Buoy — the escape hatch for apps that can't
193
+ * render FloatingDevTools inside a react-redux Provider (or don't use
194
+ * react-redux at all). Instruments via the fallback dispatch patch.
195
+ *
196
+ * Prefer the zero-config paths; this exists so no app is ever stuck.
197
+ */
198
+ function registerReduxStore(store, options = {}) {
199
+ return instrumentStore(store, options);
200
+ }
201
+
202
+ // ============================================
203
+ // Time-Travel / Introspection
204
+ // ============================================
205
+
206
+ /**
207
+ * Time travel lives in buoyReduxMiddleware.ts and is re-exported here.
208
+ *
209
+ * It used to be reimplemented in this file, and the two copies drifted: this
210
+ * one learned to notice that a jump had been ignored, the one the modal's JUMP
211
+ * button actually calls never did. So the detection existed and no user could
212
+ * ever have seen it. One implementation, one behaviour.
213
+ */
214
+
215
+ /**
216
+ * Get the currently active (instrumented) store.
217
+ *
218
+ * NOTE: on the middleware path this is the middleware `storeApi`
219
+ * ({getState, dispatch}) rather than the full Store object — every consumer
220
+ * in this package only needs those two.
221
+ */
222
+ function getActiveStore() {
223
+ return (0, _activeStoreRegistry.getActiveStore)();
224
+ }
225
+
226
+ /**
227
+ * Check if auto-instrumentation is active
228
+ */
229
+ function isAutoInstrumentActive() {
230
+ return (0, _activeStoreRegistry.getActiveStore)() !== null;
231
+ }
@@ -1 +1,202 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.buoyReduxMiddleware=exports.BUOY_REPLAY_ACTION=exports.BUOY_JUMP_TO_STATE=void 0,exports.createBuoyReduxMiddleware=createBuoyReduxMiddleware,exports.isMiddlewareActive=isMiddlewareActive,Object.defineProperty(exports,"isTimeTravelAvailable",{enumerable:!0,get:function(){return _activeStoreRegistry.isTimeTravelAvailable}}),exports.jumpToState=jumpToState,exports.replayAction=replayAction,exports.withBuoyDevTools=withBuoyDevTools;var _reduxActionStore=require("./reduxActionStore"),_activeStoreRegistry=require("./activeStoreRegistry");let middlewareActive=!1;function isMiddlewareActive(){return middlewareActive}const BUOY_JUMP_TO_STATE=exports.BUOY_JUMP_TO_STATE="@@buoy/JUMP_TO_STATE",BUOY_REPLAY_ACTION=exports.BUOY_REPLAY_ACTION="@@buoy/REPLAY_ACTION";function jumpToState(e){if((0,_reduxActionStore.isEvictedState)(e))return void console.warn("[BuoyRedux] Cannot jump - this action's state was evicted (raw state is retained for recent actions only)");const t=(0,_activeStoreRegistry.getActiveStore)();t?(0,_activeStoreRegistry.isTimeTravelAvailable)()?t.dispatch({type:BUOY_JUMP_TO_STATE,payload:e}):console.warn("[BuoyRedux] Cannot jump to state - the root reducer cannot serve a state jump. Capture and time travel are wired separately: either put `import '@buoy-gg/redux';` first in your app entry (so Buoy is the store enhancer), or wrap your root reducer with withBuoyDevTools()."):console.warn("[BuoyRedux] Cannot jump to state - no store instrumented")}function replayAction(e){const t=(0,_activeStoreRegistry.getActiveStore)();t?t.dispatch(e):console.warn("[BuoyRedux] Cannot replay action - no store instrumented")}function withBuoyDevTools(e){return(0,_activeStoreRegistry.markTimeTravelReducer)(),(t,r)=>r.type===BUOY_JUMP_TO_STATE?r.payload:e(t,r)}const buoyReduxMiddleware=e=>(middlewareActive=!0,(0,_activeStoreRegistry.registerActiveStore)(e,"middleware"),t=>r=>{const o=r,i=o.type;if(i?.startsWith("@@buoy/"))return t(r);const a=performance.now(),n=e.getState(),s=t(r),u=e.getState(),c=performance.now()-a;return _reduxActionStore.reduxActionStore.addAction(o,n,u,c),s});function createBuoyReduxMiddleware(e={}){const{maxActions:t=200,ignoreActions:r=[]}=e;return _reduxActionStore.reduxActionStore.setMaxActions(t),e=>(middlewareActive=!0,(0,_activeStoreRegistry.registerActiveStore)(e,"middleware"),t=>o=>{const i=o,a=i.type||"";if(a?.startsWith("@@buoy/"))return t(o);if(r.includes(a))return t(o);const n=performance.now(),s=e.getState(),u=t(o),c=e.getState(),d=performance.now()-n;return _reduxActionStore.reduxActionStore.addAction(i,s,c,d),u})}exports.buoyReduxMiddleware=buoyReduxMiddleware;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buoyReduxMiddleware = exports.BUOY_REPLAY_ACTION = exports.BUOY_JUMP_TO_STATE = void 0;
7
+ exports.createBuoyReduxMiddleware = createBuoyReduxMiddleware;
8
+ exports.isMiddlewareActive = isMiddlewareActive;
9
+ Object.defineProperty(exports, "isTimeTravelAvailable", {
10
+ enumerable: true,
11
+ get: function () {
12
+ return _activeStoreRegistry.isTimeTravelAvailable;
13
+ }
14
+ });
15
+ exports.jumpToState = jumpToState;
16
+ exports.replayAction = replayAction;
17
+ exports.withBuoyDevTools = withBuoyDevTools;
18
+ var _reduxActionStore = require("./reduxActionStore");
19
+ var _activeStoreRegistry = require("./activeStoreRegistry");
20
+ /**
21
+ * Buoy Redux Middleware for Redux Toolkit
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * import { configureStore } from '@reduxjs/toolkit';
26
+ * import { buoyReduxMiddleware, withBuoyDevTools } from '@buoy-gg/redux';
27
+ *
28
+ * const store = configureStore({
29
+ * reducer: withBuoyDevTools(rootReducer), // Enable time-travel
30
+ * middleware: (getDefaultMiddleware) =>
31
+ * getDefaultMiddleware().concat(buoyReduxMiddleware),
32
+ * });
33
+ * ```
34
+ */
35
+
36
+ // ============================================
37
+ // Middleware Active Tracking
38
+ // ============================================
39
+
40
+ /** Flag to indicate middleware is handling action capture */
41
+ let middlewareActive = false;
42
+
43
+ /**
44
+ * Check if the explicit middleware is active
45
+ * Used by auto-instrument to avoid duplicate capture
46
+ */
47
+ function isMiddlewareActive() {
48
+ return middlewareActive;
49
+ }
50
+
51
+ // ============================================
52
+ // Time-Travel Action Types
53
+ // ============================================
54
+
55
+ const BUOY_JUMP_TO_STATE = exports.BUOY_JUMP_TO_STATE = "@@buoy/JUMP_TO_STATE";
56
+ const BUOY_REPLAY_ACTION = exports.BUOY_REPLAY_ACTION = "@@buoy/REPLAY_ACTION";
57
+ /**
58
+ * Jump to a specific state (time-travel).
59
+ *
60
+ * Registry-backed, so it reaches the store on every capture path — but
61
+ * REACHING the store is not the same as being able to jump, and conflating the
62
+ * two is what made this fail invisibly. A jump is an action like any other: it
63
+ * only does something if a reducer handles it, and the only two things that
64
+ * install such a reducer are the devtools-hook enhancer and `withBuoyDevTools`.
65
+ * An app that captures through `buoyReduxMiddleware` alone dispatches the jump
66
+ * into a store that has never heard of it, gets its own state back, and shows
67
+ * a screen that does not move — with no error, because nothing went wrong.
68
+ *
69
+ * So the capability is checked up front and refused out loud. The UI asks the
70
+ * same question (`isTimeTravelAvailable`) and disables the button, which is the
71
+ * half a warning cannot do.
72
+ */
73
+ function jumpToState(state) {
74
+ if ((0, _reduxActionStore.isEvictedState)(state)) {
75
+ console.warn("[BuoyRedux] Cannot jump - this action's state was evicted (raw state is retained for recent actions only)");
76
+ return;
77
+ }
78
+ const store = (0, _activeStoreRegistry.getActiveStore)();
79
+ if (!store) {
80
+ console.warn("[BuoyRedux] Cannot jump to state - no store instrumented");
81
+ return;
82
+ }
83
+ if (!(0, _activeStoreRegistry.isTimeTravelAvailable)()) {
84
+ console.warn("[BuoyRedux] Cannot jump to state - the root reducer cannot serve a " + "state jump. Capture and time travel are wired separately: either put " + "`import '@buoy-gg/redux';` first in your app entry (so Buoy is the " + "store enhancer), or wrap your root reducer with withBuoyDevTools().");
85
+ return;
86
+ }
87
+ store.dispatch({
88
+ type: BUOY_JUMP_TO_STATE,
89
+ payload: state
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Whether the JUMP affordance can do anything in this app. Re-exported from
95
+ * the registry so hosts have one import for the whole time-travel surface.
96
+ */
97
+
98
+ /**
99
+ * Replay an action (dispatch it again)
100
+ */
101
+ function replayAction(action) {
102
+ const store = (0, _activeStoreRegistry.getActiveStore)();
103
+ if (store) {
104
+ store.dispatch(action);
105
+ } else {
106
+ console.warn("[BuoyRedux] Cannot replay action - no store instrumented");
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Wrap your root reducer to enable time-travel support
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * const store = configureStore({
116
+ * reducer: withBuoyDevTools(rootReducer),
117
+ * middleware: (getDefault) => getDefault().concat(buoyReduxMiddleware),
118
+ * });
119
+ * ```
120
+ */
121
+ function withBuoyDevTools(reducer) {
122
+ // The other half of the time-travel wiring (see jumpToState): this is one of
123
+ // exactly two places that can answer BUOY_JUMP_TO_STATE.
124
+ (0, _activeStoreRegistry.markTimeTravelReducer)();
125
+ return (state, action) => {
126
+ // Handle jump to state action
127
+ if (action.type === BUOY_JUMP_TO_STATE) {
128
+ const jumpAction = action;
129
+ return jumpAction.payload;
130
+ }
131
+
132
+ // Pass through to original reducer
133
+ return reducer(state, action);
134
+ };
135
+ }
136
+
137
+ /**
138
+ * Default middleware - just add to your store!
139
+ */
140
+ const buoyReduxMiddleware = storeApi => {
141
+ // Mark middleware as active so auto-instrument knows to skip
142
+ middlewareActive = true;
143
+
144
+ // Register as the active store so getState/dispatch (MCP, desktop, time
145
+ // travel) work on the middleware path too — storeApi has both getState and
146
+ // dispatch, which is all the registry needs.
147
+ (0, _activeStoreRegistry.registerActiveStore)(storeApi, "middleware");
148
+ return next => action => {
149
+ const actionObj = action;
150
+ const actionType = actionObj.type;
151
+
152
+ // Don't record devtools internal actions
153
+ if (actionType?.startsWith("@@buoy/")) {
154
+ return next(action);
155
+ }
156
+ const startTime = performance.now();
157
+ const prevState = storeApi.getState();
158
+ const result = next(action);
159
+ const nextState = storeApi.getState();
160
+ const duration = performance.now() - startTime;
161
+ _reduxActionStore.reduxActionStore.addAction(actionObj, prevState, nextState, duration);
162
+ return result;
163
+ };
164
+ };
165
+
166
+ /**
167
+ * Create middleware with custom options
168
+ */
169
+ exports.buoyReduxMiddleware = buoyReduxMiddleware;
170
+ function createBuoyReduxMiddleware(options = {}) {
171
+ const {
172
+ maxActions = 200,
173
+ ignoreActions = []
174
+ } = options;
175
+ _reduxActionStore.reduxActionStore.setMaxActions(maxActions);
176
+ return storeApi => {
177
+ // Mark middleware as active so auto-instrument knows to skip
178
+ middlewareActive = true;
179
+
180
+ // Register as the active store (see buoyReduxMiddleware above).
181
+ (0, _activeStoreRegistry.registerActiveStore)(storeApi, "middleware");
182
+ return next => action => {
183
+ const actionObj = action;
184
+ const actionType = actionObj.type || "";
185
+
186
+ // Don't record devtools internal actions
187
+ if (actionType?.startsWith("@@buoy/")) {
188
+ return next(action);
189
+ }
190
+ if (ignoreActions.includes(actionType)) {
191
+ return next(action);
192
+ }
193
+ const startTime = performance.now();
194
+ const prevState = storeApi.getState();
195
+ const result = next(action);
196
+ const nextState = storeApi.getState();
197
+ const duration = performance.now() - startTime;
198
+ _reduxActionStore.reduxActionStore.addAction(actionObj, prevState, nextState, duration);
199
+ return result;
200
+ };
201
+ };
202
+ }
@@ -1 +1,146 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.createReduxHistoryAdapter=createReduxHistoryAdapter,exports.reduxHistoryAdapter=void 0;var _reduxActionStore=require("./reduxActionStore"),_ReduxIcon=require("../components/ReduxIcon");const REDUX_CAPABILITIES={timeTravel:!1,toggle:!1,edit:!1,diff:!0,export:!0,import:!1,reset:!0,recording:!0};function toHistoryEntry(t,e,r){return{id:t.id,label:t.type,timestamp:t.timestamp,data:t.payload??t.action,prevState:t.prevState,nextState:t.nextState,hasStateChange:t.hasStateChange,duration:t.duration,source:"redux",isCurrent:0===e,isInFuture:!1,isSkipped:!1,meta:{fullAction:t.action}}}function createReduxHistoryAdapter(t){const{icon:e=_ReduxIcon.ReduxIcon,color:r="#764ABC",name:o="Redux"}=t??{};let n=0;return{id:"redux",name:o,icon:e,color:r,capabilities:REDUX_CAPABILITIES,subscribe:t=>_reduxActionStore.reduxActionStore.subscribe(()=>{t()}),getEntries:()=>{const t=_reduxActionStore.reduxActionStore.getActions();return t.map((e,r)=>toHistoryEntry(e,r,t.length))},getEntryCount:()=>_reduxActionStore.reduxActionStore.getActions().length,getCurrentState:()=>{const t=_reduxActionStore.reduxActionStore.getActions();if(0!==t.length)return t[0].nextState},getStateAtEntry:t=>{const e=_reduxActionStore.reduxActionStore.getActionById(String(t));return e?.nextState},get currentIndex(){return n},jumpTo:t=>{const e=_reduxActionStore.reduxActionStore.getActions().findIndex(e=>e.id===String(t));-1!==e&&(n=e)},get isRecording(){return _reduxActionStore.reduxActionStore.getEnabled()},setRecording:t=>{_reduxActionStore.reduxActionStore.setEnabled(t)},reset:()=>{_reduxActionStore.reduxActionStore.clearActions(),n=0},exportHistory:()=>{const t=_reduxActionStore.reduxActionStore.getActions();return JSON.stringify(t,null,2)}}}const reduxHistoryAdapter=exports.reduxHistoryAdapter=createReduxHistoryAdapter();
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createReduxHistoryAdapter = createReduxHistoryAdapter;
7
+ exports.reduxHistoryAdapter = void 0;
8
+ var _reduxActionStore = require("./reduxActionStore");
9
+ var _ReduxIcon = require("../components/ReduxIcon");
10
+ /**
11
+ * Redux History Adapter
12
+ *
13
+ * Wraps the existing reduxActionStore to implement the universal HistoryAdapter interface.
14
+ */
15
+
16
+ /**
17
+ * Redux adapter capabilities
18
+ * Note: Full time-travel requires the instrument() pattern from redux-devtools.
19
+ * This adapter provides history viewing with basic navigation.
20
+ */
21
+ const REDUX_CAPABILITIES = {
22
+ timeTravel: false,
23
+ // Would need instrument() for true time travel
24
+ toggle: false,
25
+ // Would need instrument() for skip/toggle
26
+ edit: false,
27
+ diff: true,
28
+ // We have prevState/nextState
29
+ export: true,
30
+ import: false,
31
+ reset: true,
32
+ recording: true
33
+ };
34
+
35
+ /**
36
+ * Convert ReduxAction to HistoryEntry
37
+ */
38
+ function toHistoryEntry(action, index, _total) {
39
+ return {
40
+ id: action.id,
41
+ label: action.type,
42
+ timestamp: action.timestamp,
43
+ data: action.payload ?? action.action,
44
+ prevState: action.prevState,
45
+ nextState: action.nextState,
46
+ hasStateChange: action.hasStateChange,
47
+ duration: action.duration,
48
+ source: "redux",
49
+ isCurrent: index === 0,
50
+ // Most recent is current
51
+ isInFuture: false,
52
+ isSkipped: false,
53
+ meta: {
54
+ fullAction: action.action
55
+ }
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Options for creating a Redux history adapter
61
+ */
62
+
63
+ /**
64
+ * Create a Redux history adapter
65
+ *
66
+ * @param options - Optional configuration
67
+ * @returns HistoryAdapter instance
68
+ */
69
+ function createReduxHistoryAdapter(options) {
70
+ const {
71
+ icon = _ReduxIcon.ReduxIcon,
72
+ color = "#764ABC",
73
+ // Redux purple
74
+ name = "Redux"
75
+ } = options ?? {};
76
+ let currentIndex = 0;
77
+ const adapter = {
78
+ // === Identity ===
79
+ id: "redux",
80
+ name,
81
+ icon,
82
+ color,
83
+ capabilities: REDUX_CAPABILITIES,
84
+ // === Subscription ===
85
+ subscribe: callback => {
86
+ return _reduxActionStore.reduxActionStore.subscribe(() => {
87
+ callback();
88
+ });
89
+ },
90
+ // === Data Access ===
91
+ getEntries: () => {
92
+ const actions = _reduxActionStore.reduxActionStore.getActions();
93
+ return actions.map((action, index) => toHistoryEntry(action, index, actions.length));
94
+ },
95
+ getEntryCount: () => {
96
+ return _reduxActionStore.reduxActionStore.getActions().length;
97
+ },
98
+ getCurrentState: () => {
99
+ const actions = _reduxActionStore.reduxActionStore.getActions();
100
+ if (actions.length === 0) return undefined;
101
+ // Most recent action's nextState is current state
102
+ return actions[0].nextState;
103
+ },
104
+ getStateAtEntry: entryId => {
105
+ const action = _reduxActionStore.reduxActionStore.getActionById(String(entryId));
106
+ return action?.nextState;
107
+ },
108
+ // === Navigation ===
109
+ get currentIndex() {
110
+ return currentIndex;
111
+ },
112
+ jumpTo: entryId => {
113
+ const actions = _reduxActionStore.reduxActionStore.getActions();
114
+ const index = actions.findIndex(a => a.id === String(entryId));
115
+ if (index !== -1) {
116
+ currentIndex = index;
117
+ // Note: This doesn't actually change app state without instrument()
118
+ // It just updates which entry is "selected" for viewing
119
+ }
120
+ },
121
+ // === Recording ===
122
+ get isRecording() {
123
+ return _reduxActionStore.reduxActionStore.getEnabled();
124
+ },
125
+ setRecording: enabled => {
126
+ _reduxActionStore.reduxActionStore.setEnabled(enabled);
127
+ },
128
+ // === Reset ===
129
+ reset: () => {
130
+ _reduxActionStore.reduxActionStore.clearActions();
131
+ currentIndex = 0;
132
+ },
133
+ // === Export ===
134
+ exportHistory: () => {
135
+ const actions = _reduxActionStore.reduxActionStore.getActions();
136
+ return JSON.stringify(actions, null, 2);
137
+ }
138
+ };
139
+ return adapter;
140
+ }
141
+
142
+ /**
143
+ * Default Redux adapter instance
144
+ * Use this if you only have one Redux store
145
+ */
146
+ const reduxHistoryAdapter = exports.reduxHistoryAdapter = createReduxHistoryAdapter();