@buoy-gg/redux 7.0.41 → 7.0.42

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 (78) hide show
  1. package/README.md +11 -106
  2. package/lib/commonjs/index.js +1 -256
  3. package/lib/commonjs/preset.js +1 -90
  4. package/lib/commonjs/redux/components/ReduxActionButton.js +1 -131
  5. package/lib/commonjs/redux/components/ReduxActionDetailContent.js +1 -437
  6. package/lib/commonjs/redux/components/ReduxActionDetailView.js +1 -401
  7. package/lib/commonjs/redux/components/ReduxActionInfoView.js +1 -836
  8. package/lib/commonjs/redux/components/ReduxActionItem.js +1 -337
  9. package/lib/commonjs/redux/components/ReduxAutoInstrument.js +1 -20
  10. package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +1 -50
  11. package/lib/commonjs/redux/components/ReduxIcon.js +1 -18
  12. package/lib/commonjs/redux/components/ReduxModal.js +1 -703
  13. package/lib/commonjs/redux/hooks/reduxPeer.js +1 -17
  14. package/lib/commonjs/redux/hooks/reduxPeer.web.js +1 -10
  15. package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +1 -177
  16. package/lib/commonjs/redux/hooks/useReduxActions.js +1 -58
  17. package/lib/commonjs/redux/sync/reduxSyncAdapter.js +1 -191
  18. package/lib/commonjs/redux/types/index.js +1 -1
  19. package/lib/commonjs/redux/utils/activeStoreRegistry.js +1 -104
  20. package/lib/commonjs/redux/utils/autoInstrument.js +1 -231
  21. package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +1 -202
  22. package/lib/commonjs/redux/utils/createReduxHistoryAdapter.js +1 -146
  23. package/lib/commonjs/redux/utils/devtoolsHook.js +1 -152
  24. package/lib/commonjs/redux/utils/reduxActionStore.js +1 -473
  25. package/lib/commonjs/redux/utils/snapshotProvider.js +1 -158
  26. package/lib/commonjs/redux/utils/testIDs.js +1 -75
  27. package/lib/module/index.js +1 -125
  28. package/lib/module/preset.js +1 -87
  29. package/lib/module/redux/components/ReduxActionButton.js +1 -128
  30. package/lib/module/redux/components/ReduxActionDetailContent.js +1 -433
  31. package/lib/module/redux/components/ReduxActionDetailView.js +1 -397
  32. package/lib/module/redux/components/ReduxActionInfoView.js +1 -831
  33. package/lib/module/redux/components/ReduxActionItem.js +1 -333
  34. package/lib/module/redux/components/ReduxAutoInstrument.js +1 -17
  35. package/lib/module/redux/components/ReduxDetailViewToggle.js +1 -45
  36. package/lib/module/redux/components/ReduxIcon.js +1 -8
  37. package/lib/module/redux/components/ReduxModal.js +1 -698
  38. package/lib/module/redux/hooks/reduxPeer.js +1 -13
  39. package/lib/module/redux/hooks/reduxPeer.web.js +1 -6
  40. package/lib/module/redux/hooks/useAutoInstrumentRedux.js +1 -173
  41. package/lib/module/redux/hooks/useReduxActions.js +1 -54
  42. package/lib/module/redux/sync/reduxSyncAdapter.js +1 -188
  43. package/lib/module/redux/types/index.js +1 -1
  44. package/lib/module/redux/utils/activeStoreRegistry.js +1 -92
  45. package/lib/module/redux/utils/autoInstrument.js +1 -201
  46. package/lib/module/redux/utils/buoyReduxMiddleware.js +1 -188
  47. package/lib/module/redux/utils/createReduxHistoryAdapter.js +1 -142
  48. package/lib/module/redux/utils/devtoolsHook.js +1 -145
  49. package/lib/module/redux/utils/reduxActionStore.js +1 -465
  50. package/lib/module/redux/utils/snapshotProvider.js +1 -157
  51. package/lib/module/redux/utils/testIDs.js +1 -68
  52. package/lib/web/index.mjs +1 -2
  53. package/package.json +4 -4
  54. package/lib/typescript/index.d.ts.map +0 -1
  55. package/lib/typescript/preset.d.ts.map +0 -1
  56. package/lib/typescript/redux/components/ReduxActionButton.d.ts.map +0 -1
  57. package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +0 -1
  58. package/lib/typescript/redux/components/ReduxActionDetailView.d.ts.map +0 -1
  59. package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +0 -1
  60. package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +0 -1
  61. package/lib/typescript/redux/components/ReduxAutoInstrument.d.ts.map +0 -1
  62. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +0 -1
  63. package/lib/typescript/redux/components/ReduxIcon.d.ts.map +0 -1
  64. package/lib/typescript/redux/components/ReduxModal.d.ts.map +0 -1
  65. package/lib/typescript/redux/hooks/reduxPeer.d.ts.map +0 -1
  66. package/lib/typescript/redux/hooks/reduxPeer.web.d.ts.map +0 -1
  67. package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts.map +0 -1
  68. package/lib/typescript/redux/hooks/useReduxActions.d.ts.map +0 -1
  69. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +0 -1
  70. package/lib/typescript/redux/types/index.d.ts.map +0 -1
  71. package/lib/typescript/redux/utils/activeStoreRegistry.d.ts.map +0 -1
  72. package/lib/typescript/redux/utils/autoInstrument.d.ts.map +0 -1
  73. package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +0 -1
  74. package/lib/typescript/redux/utils/createReduxHistoryAdapter.d.ts.map +0 -1
  75. package/lib/typescript/redux/utils/devtoolsHook.d.ts.map +0 -1
  76. package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +0 -1
  77. package/lib/typescript/redux/utils/snapshotProvider.d.ts.map +0 -1
  78. package/lib/typescript/redux/utils/testIDs.d.ts.map +0 -1
@@ -1,201 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Fallback auto-instrumentation for Redux stores.
5
- *
6
- * The PRIMARY capture path is the store-creation-time enhancer in
7
- * devtoolsHook.ts (full capture, real time travel). This module is the
8
- * fallback for stores created before our module loaded: it patches
9
- * store.dispatch after the fact.
10
- *
11
- * Fallback limitation (inherent to patching, verified against redux 5.x):
12
- * `middlewareAPI.dispatch` is a separate closure from `store.dispatch`, so
13
- * actions dispatched from INSIDE middleware/thunks (RTK pending/fulfilled/
14
- * rejected, RTK Query) bypass the patch. That's why this mechanism reports
15
- * capture as "top-level-only" — see activeStoreRegistry.getInstrumentationStatus.
16
- */
17
-
18
- import { reduxActionStore } from "./reduxActionStore";
19
- import { isMiddlewareActive } from "./buoyReduxMiddleware";
20
- import { registerActiveStore, unregisterActiveStore, getActiveStore as getRegisteredStore, ENHANCER_SYMBOL } from "./activeStoreRegistry";
21
-
22
- // Re-exported for non-React callers (sync adapter, app code).
23
- export { getInstrumentationStatus } from "./activeStoreRegistry";
24
-
25
- // ============================================
26
- // Store Tracking
27
- // ============================================
28
-
29
- /** Symbol to mark stores that have been instrumented */
30
- const INSTRUMENTED_SYMBOL = Symbol.for("@@buoy/instrumented");
31
-
32
- /** Symbol to store original dispatch reference */
33
- const ORIGINAL_DISPATCH_SYMBOL = Symbol.for("@@buoy/originalDispatch");
34
-
35
- // ============================================
36
- // Type Definitions
37
- // ============================================
38
-
39
- // ============================================
40
- // Core Instrumentation
41
- // ============================================
42
-
43
- /**
44
- * Check if a store has already been instrumented (either mechanism)
45
- */
46
- export function isStoreInstrumented(store) {
47
- const s = store;
48
- return s[INSTRUMENTED_SYMBOL] === true || s[ENHANCER_SYMBOL] === true;
49
- }
50
-
51
- /**
52
- * Instrument a Redux store for action capture (fallback path).
53
- *
54
- * Patches store.dispatch to capture top-level dispatches. No-ops (but still
55
- * registers the store for getState/dispatch) when the enhancer or the
56
- * explicit middleware is already capturing.
57
- *
58
- * @returns Cleanup function to restore original dispatch
59
- */
60
- export function instrumentStore(store, options = {}) {
61
- const instrumentedStore = store;
62
-
63
- // Store was created through the devtools-hook enhancer — full capture is
64
- // already live; just (re)point the registry at it.
65
- if (instrumentedStore[ENHANCER_SYMBOL]) {
66
- registerActiveStore(store, "enhancer");
67
- return () => {};
68
- }
69
-
70
- // Skip if middleware is already handling action capture
71
- if (isMiddlewareActive()) {
72
- registerActiveStore(store, "middleware");
73
- return () => {};
74
- }
75
-
76
- // Skip if already patched — update the registry even so (store swap case)
77
- if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
78
- registerActiveStore(store, "patch");
79
- return () => {};
80
- }
81
- const {
82
- maxActions = 200,
83
- ignoreActions = []
84
- } = options;
85
-
86
- // Configure store
87
- reduxActionStore.setMaxActions(maxActions);
88
-
89
- // Store original dispatch
90
- const originalDispatch = store.dispatch.bind(store);
91
- instrumentedStore[ORIGINAL_DISPATCH_SYMBOL] = originalDispatch;
92
-
93
- // Create instrumented dispatch
94
- const instrumentedDispatch = action => {
95
- // Skip if action is not a plain object with a type property
96
- // This filters out thunk functions, promises, and other non-action dispatches
97
- if (typeof action !== "object" || action === null || typeof action.type !== "string") {
98
- return originalDispatch(action);
99
- }
100
- const actionType = action.type;
101
-
102
- // Don't record devtools internal actions
103
- if (actionType.startsWith("@@buoy/")) {
104
- return originalDispatch(action);
105
- }
106
-
107
- // Don't record Redux internal actions
108
- if (actionType.startsWith("@@redux/")) {
109
- return originalDispatch(action);
110
- }
111
-
112
- // Check ignore list
113
- if (ignoreActions.includes(actionType)) {
114
- return originalDispatch(action);
115
- }
116
-
117
- // Capture timing and state
118
- const startTime = performance.now();
119
- const prevState = store.getState();
120
- const result = originalDispatch(action);
121
- const nextState = store.getState();
122
- const duration = performance.now() - startTime;
123
-
124
- // Record the action
125
- reduxActionStore.addAction(action, prevState, nextState, duration);
126
- return result;
127
- };
128
-
129
- // Replace dispatch
130
- store.dispatch = instrumentedDispatch;
131
- instrumentedStore[INSTRUMENTED_SYMBOL] = true;
132
- registerActiveStore(store, "patch");
133
-
134
- // Return cleanup function
135
- return () => {
136
- uninstrumentStore(store);
137
- };
138
- }
139
-
140
- /**
141
- * Remove dispatch-patch instrumentation from a store.
142
- *
143
- * Only restores the patched dispatch. (This used to also replaceReducer with a
144
- * synthetic reducer that froze the host store permanently — that path is gone.)
145
- * Enhancer-created stores are left untouched apart from the registry.
146
- */
147
- export function uninstrumentStore(store) {
148
- const instrumentedStore = store;
149
- if (instrumentedStore[INSTRUMENTED_SYMBOL]) {
150
- const originalDispatch = instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
151
- if (originalDispatch) {
152
- store.dispatch = originalDispatch;
153
- }
154
- delete instrumentedStore[INSTRUMENTED_SYMBOL];
155
- delete instrumentedStore[ORIGINAL_DISPATCH_SYMBOL];
156
- }
157
- unregisterActiveStore(store);
158
- }
159
-
160
- /**
161
- * Manually register a store with Buoy — the escape hatch for apps that can't
162
- * render FloatingDevTools inside a react-redux Provider (or don't use
163
- * react-redux at all). Instruments via the fallback dispatch patch.
164
- *
165
- * Prefer the zero-config paths; this exists so no app is ever stuck.
166
- */
167
- export function registerReduxStore(store, options = {}) {
168
- return instrumentStore(store, options);
169
- }
170
-
171
- // ============================================
172
- // Time-Travel / Introspection
173
- // ============================================
174
-
175
- /**
176
- * Time travel lives in buoyReduxMiddleware.ts and is re-exported here.
177
- *
178
- * It used to be reimplemented in this file, and the two copies drifted: this
179
- * one learned to notice that a jump had been ignored, the one the modal's JUMP
180
- * button actually calls never did. So the detection existed and no user could
181
- * ever have seen it. One implementation, one behaviour.
182
- */
183
- export { jumpToState, replayAction, isTimeTravelAvailable } from "./buoyReduxMiddleware";
184
-
185
- /**
186
- * Get the currently active (instrumented) store.
187
- *
188
- * NOTE: on the middleware path this is the middleware `storeApi`
189
- * ({getState, dispatch}) rather than the full Store object — every consumer
190
- * in this package only needs those two.
191
- */
192
- export function getActiveStore() {
193
- return getRegisteredStore();
194
- }
195
-
196
- /**
197
- * Check if auto-instrumentation is active
198
- */
199
- export function isAutoInstrumentActive() {
200
- return getRegisteredStore() !== null;
201
- }
1
+ "use strict";import{reduxActionStore as p}from"./reduxActionStore";import{isMiddlewareActive as y}from"./buoyReduxMiddleware";import{registerActiveStore as u,unregisterActiveStore as v,getActiveStore as a,ENHANCER_SYMBOL as f}from"./activeStoreRegistry";export{getInstrumentationStatus}from"./activeStoreRegistry";const o=Symbol.for("@@buoy/instrumented");const c=Symbol.for("@@buoy/originalDispatch");export function isStoreInstrumented(t){const e=t;return e[o]===true||e[f]===true}export function instrumentStore(t,e={}){const n=t;if(n[f]){u(t,"enhancer");return()=>{}}if(y()){u(t,"middleware");return()=>{}}if(n[o]){u(t,"patch");return()=>{}}const{maxActions:m=200,ignoreActions:S=[]}=e;p.setMaxActions(m);const i=t.dispatch.bind(t);n[c]=i;const d=r=>{if(typeof r!=="object"||r===null||typeof r.type!=="string"){return i(r)}const s=r.type;if(s.startsWith("@@buoy/")){return i(r)}if(s.startsWith("@@redux/")){return i(r)}if(S.includes(s)){return i(r)}const l=performance.now();const A=t.getState();const g=i(r);const x=t.getState();const h=performance.now()-l;p.addAction(r,A,x,h);return g};t.dispatch=d;n[o]=true;u(t,"patch");return()=>{uninstrumentStore(t)}}export function uninstrumentStore(t){const e=t;if(e[o]){const n=e[c];if(n){t.dispatch=n}delete e[o];delete e[c]}v(t)}export function registerReduxStore(t,e={}){return instrumentStore(t,e)}export{jumpToState,replayAction,isTimeTravelAvailable}from"./buoyReduxMiddleware";export function getActiveStore(){return a()}export function isAutoInstrumentActive(){return a()!==null}
@@ -1,188 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Buoy Redux Middleware for Redux Toolkit
5
- *
6
- * @example
7
- * ```tsx
8
- * import { configureStore } from '@reduxjs/toolkit';
9
- * import { buoyReduxMiddleware, withBuoyDevTools } from '@buoy-gg/redux';
10
- *
11
- * const store = configureStore({
12
- * reducer: withBuoyDevTools(rootReducer), // Enable time-travel
13
- * middleware: (getDefaultMiddleware) =>
14
- * getDefaultMiddleware().concat(buoyReduxMiddleware),
15
- * });
16
- * ```
17
- */
18
-
19
- import { reduxActionStore, isEvictedState } from "./reduxActionStore";
20
- import { registerActiveStore, getActiveStore, markTimeTravelReducer, isTimeTravelAvailable } from "./activeStoreRegistry";
21
-
22
- // ============================================
23
- // Middleware Active Tracking
24
- // ============================================
25
-
26
- /** Flag to indicate middleware is handling action capture */
27
- let middlewareActive = false;
28
-
29
- /**
30
- * Check if the explicit middleware is active
31
- * Used by auto-instrument to avoid duplicate capture
32
- */
33
- export function isMiddlewareActive() {
34
- return middlewareActive;
35
- }
36
-
37
- // ============================================
38
- // Time-Travel Action Types
39
- // ============================================
40
-
41
- export const BUOY_JUMP_TO_STATE = "@@buoy/JUMP_TO_STATE";
42
- export const BUOY_REPLAY_ACTION = "@@buoy/REPLAY_ACTION";
43
- /**
44
- * Jump to a specific state (time-travel).
45
- *
46
- * Registry-backed, so it reaches the store on every capture path — but
47
- * REACHING the store is not the same as being able to jump, and conflating the
48
- * two is what made this fail invisibly. A jump is an action like any other: it
49
- * only does something if a reducer handles it, and the only two things that
50
- * install such a reducer are the devtools-hook enhancer and `withBuoyDevTools`.
51
- * An app that captures through `buoyReduxMiddleware` alone dispatches the jump
52
- * into a store that has never heard of it, gets its own state back, and shows
53
- * a screen that does not move — with no error, because nothing went wrong.
54
- *
55
- * So the capability is checked up front and refused out loud. The UI asks the
56
- * same question (`isTimeTravelAvailable`) and disables the button, which is the
57
- * half a warning cannot do.
58
- */
59
- export function jumpToState(state) {
60
- if (isEvictedState(state)) {
61
- console.warn("[BuoyRedux] Cannot jump - this action's state was evicted (raw state is retained for recent actions only)");
62
- return;
63
- }
64
- const store = getActiveStore();
65
- if (!store) {
66
- console.warn("[BuoyRedux] Cannot jump to state - no store instrumented");
67
- return;
68
- }
69
- if (!isTimeTravelAvailable()) {
70
- 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().");
71
- return;
72
- }
73
- store.dispatch({
74
- type: BUOY_JUMP_TO_STATE,
75
- payload: state
76
- });
77
- }
78
-
79
- /**
80
- * Whether the JUMP affordance can do anything in this app. Re-exported from
81
- * the registry so hosts have one import for the whole time-travel surface.
82
- */
83
- export { isTimeTravelAvailable } from "./activeStoreRegistry";
84
-
85
- /**
86
- * Replay an action (dispatch it again)
87
- */
88
- export function replayAction(action) {
89
- const store = getActiveStore();
90
- if (store) {
91
- store.dispatch(action);
92
- } else {
93
- console.warn("[BuoyRedux] Cannot replay action - no store instrumented");
94
- }
95
- }
96
-
97
- /**
98
- * Wrap your root reducer to enable time-travel support
99
- *
100
- * @example
101
- * ```tsx
102
- * const store = configureStore({
103
- * reducer: withBuoyDevTools(rootReducer),
104
- * middleware: (getDefault) => getDefault().concat(buoyReduxMiddleware),
105
- * });
106
- * ```
107
- */
108
- export function withBuoyDevTools(reducer) {
109
- // The other half of the time-travel wiring (see jumpToState): this is one of
110
- // exactly two places that can answer BUOY_JUMP_TO_STATE.
111
- markTimeTravelReducer();
112
- return (state, action) => {
113
- // Handle jump to state action
114
- if (action.type === BUOY_JUMP_TO_STATE) {
115
- const jumpAction = action;
116
- return jumpAction.payload;
117
- }
118
-
119
- // Pass through to original reducer
120
- return reducer(state, action);
121
- };
122
- }
123
-
124
- /**
125
- * Default middleware - just add to your store!
126
- */
127
- export const buoyReduxMiddleware = storeApi => {
128
- // Mark middleware as active so auto-instrument knows to skip
129
- middlewareActive = true;
130
-
131
- // Register as the active store so getState/dispatch (MCP, desktop, time
132
- // travel) work on the middleware path too — storeApi has both getState and
133
- // dispatch, which is all the registry needs.
134
- registerActiveStore(storeApi, "middleware");
135
- return next => action => {
136
- const actionObj = action;
137
- const actionType = actionObj.type;
138
-
139
- // Don't record devtools internal actions
140
- if (actionType?.startsWith("@@buoy/")) {
141
- return next(action);
142
- }
143
- const startTime = performance.now();
144
- const prevState = storeApi.getState();
145
- const result = next(action);
146
- const nextState = storeApi.getState();
147
- const duration = performance.now() - startTime;
148
- reduxActionStore.addAction(actionObj, prevState, nextState, duration);
149
- return result;
150
- };
151
- };
152
-
153
- /**
154
- * Create middleware with custom options
155
- */
156
- export function createBuoyReduxMiddleware(options = {}) {
157
- const {
158
- maxActions = 200,
159
- ignoreActions = []
160
- } = options;
161
- reduxActionStore.setMaxActions(maxActions);
162
- return storeApi => {
163
- // Mark middleware as active so auto-instrument knows to skip
164
- middlewareActive = true;
165
-
166
- // Register as the active store (see buoyReduxMiddleware above).
167
- registerActiveStore(storeApi, "middleware");
168
- return next => action => {
169
- const actionObj = action;
170
- const actionType = actionObj.type || "";
171
-
172
- // Don't record devtools internal actions
173
- if (actionType?.startsWith("@@buoy/")) {
174
- return next(action);
175
- }
176
- if (ignoreActions.includes(actionType)) {
177
- return next(action);
178
- }
179
- const startTime = performance.now();
180
- const prevState = storeApi.getState();
181
- const result = next(action);
182
- const nextState = storeApi.getState();
183
- const duration = performance.now() - startTime;
184
- reduxActionStore.addAction(actionObj, prevState, nextState, duration);
185
- return result;
186
- };
187
- };
188
- }
1
+ "use strict";import{reduxActionStore as d,isEvictedState as T}from"./reduxActionStore";import{registerActiveStore as l,getActiveStore as m,markTimeTravelReducer as x,isTimeTravelAvailable as v}from"./activeStoreRegistry";let p=false;export function isMiddlewareActive(){return p}export const BUOY_JUMP_TO_STATE="@@buoy/JUMP_TO_STATE";export const BUOY_REPLAY_ACTION="@@buoy/REPLAY_ACTION";export function jumpToState(t){if(T(t)){console.warn("[BuoyRedux] Cannot jump - this action's state was evicted (raw state is retained for recent actions only)");return}const e=m();if(!e){console.warn("[BuoyRedux] Cannot jump to state - no store instrumented");return}if(!v()){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().");return}e.dispatch({type:BUOY_JUMP_TO_STATE,payload:t})}export{isTimeTravelAvailable}from"./activeStoreRegistry";export function replayAction(t){const e=m();if(e){e.dispatch(t)}else{console.warn("[BuoyRedux] Cannot replay action - no store instrumented")}}export function withBuoyDevTools(t){x();return(e,r)=>{if(r.type===BUOY_JUMP_TO_STATE){const o=r;return o.payload}return t(e,r)}}export const buoyReduxMiddleware=t=>{p=true;l(t,"middleware");return e=>r=>{const o=r;const a=o.type;if(a?.startsWith("@@buoy/")){return e(r)}const n=performance.now();const s=t.getState();const i=e(r);const u=t.getState();const c=performance.now()-n;d.addAction(o,s,u,c);return i}};export function createBuoyReduxMiddleware(t={}){const{maxActions:e=200,ignoreActions:r=[]}=t;d.setMaxActions(e);return o=>{p=true;l(o,"middleware");return a=>n=>{const s=n;const i=s.type||"";if(i?.startsWith("@@buoy/")){return a(n)}if(r.includes(i)){return a(n)}const u=performance.now();const c=o.getState();const y=a(n);const f=o.getState();const w=performance.now()-u;d.addAction(s,c,f,w);return y}}}
@@ -1,142 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Redux History Adapter
5
- *
6
- * Wraps the existing reduxActionStore to implement the universal HistoryAdapter interface.
7
- */
8
-
9
- import { reduxActionStore } from "./reduxActionStore";
10
- import { ReduxIcon } from "../components/ReduxIcon";
11
-
12
- /**
13
- * Redux adapter capabilities
14
- * Note: Full time-travel requires the instrument() pattern from redux-devtools.
15
- * This adapter provides history viewing with basic navigation.
16
- */
17
- const REDUX_CAPABILITIES = {
18
- timeTravel: false,
19
- // Would need instrument() for true time travel
20
- toggle: false,
21
- // Would need instrument() for skip/toggle
22
- edit: false,
23
- diff: true,
24
- // We have prevState/nextState
25
- export: true,
26
- import: false,
27
- reset: true,
28
- recording: true
29
- };
30
-
31
- /**
32
- * Convert ReduxAction to HistoryEntry
33
- */
34
- function toHistoryEntry(action, index, _total) {
35
- return {
36
- id: action.id,
37
- label: action.type,
38
- timestamp: action.timestamp,
39
- data: action.payload ?? action.action,
40
- prevState: action.prevState,
41
- nextState: action.nextState,
42
- hasStateChange: action.hasStateChange,
43
- duration: action.duration,
44
- source: "redux",
45
- isCurrent: index === 0,
46
- // Most recent is current
47
- isInFuture: false,
48
- isSkipped: false,
49
- meta: {
50
- fullAction: action.action
51
- }
52
- };
53
- }
54
-
55
- /**
56
- * Options for creating a Redux history adapter
57
- */
58
-
59
- /**
60
- * Create a Redux history adapter
61
- *
62
- * @param options - Optional configuration
63
- * @returns HistoryAdapter instance
64
- */
65
- export function createReduxHistoryAdapter(options) {
66
- const {
67
- icon = ReduxIcon,
68
- color = "#764ABC",
69
- // Redux purple
70
- name = "Redux"
71
- } = options ?? {};
72
- let currentIndex = 0;
73
- const adapter = {
74
- // === Identity ===
75
- id: "redux",
76
- name,
77
- icon,
78
- color,
79
- capabilities: REDUX_CAPABILITIES,
80
- // === Subscription ===
81
- subscribe: callback => {
82
- return reduxActionStore.subscribe(() => {
83
- callback();
84
- });
85
- },
86
- // === Data Access ===
87
- getEntries: () => {
88
- const actions = reduxActionStore.getActions();
89
- return actions.map((action, index) => toHistoryEntry(action, index, actions.length));
90
- },
91
- getEntryCount: () => {
92
- return reduxActionStore.getActions().length;
93
- },
94
- getCurrentState: () => {
95
- const actions = reduxActionStore.getActions();
96
- if (actions.length === 0) return undefined;
97
- // Most recent action's nextState is current state
98
- return actions[0].nextState;
99
- },
100
- getStateAtEntry: entryId => {
101
- const action = reduxActionStore.getActionById(String(entryId));
102
- return action?.nextState;
103
- },
104
- // === Navigation ===
105
- get currentIndex() {
106
- return currentIndex;
107
- },
108
- jumpTo: entryId => {
109
- const actions = reduxActionStore.getActions();
110
- const index = actions.findIndex(a => a.id === String(entryId));
111
- if (index !== -1) {
112
- currentIndex = index;
113
- // Note: This doesn't actually change app state without instrument()
114
- // It just updates which entry is "selected" for viewing
115
- }
116
- },
117
- // === Recording ===
118
- get isRecording() {
119
- return reduxActionStore.getEnabled();
120
- },
121
- setRecording: enabled => {
122
- reduxActionStore.setEnabled(enabled);
123
- },
124
- // === Reset ===
125
- reset: () => {
126
- reduxActionStore.clearActions();
127
- currentIndex = 0;
128
- },
129
- // === Export ===
130
- exportHistory: () => {
131
- const actions = reduxActionStore.getActions();
132
- return JSON.stringify(actions, null, 2);
133
- }
134
- };
135
- return adapter;
136
- }
137
-
138
- /**
139
- * Default Redux adapter instance
140
- * Use this if you only have one Redux store
141
- */
142
- export const reduxHistoryAdapter = createReduxHistoryAdapter();
1
+ "use strict";import{reduxActionStore as r}from"./reduxActionStore";import{ReduxIcon as g}from"../components/ReduxIcon";const l={timeTravel:false,toggle:false,edit:false,diff:true,export:true,import:false,reset:true,recording:true};function p(e,s,a){return{id:e.id,label:e.type,timestamp:e.timestamp,data:e.payload??e.action,prevState:e.prevState,nextState:e.nextState,hasStateChange:e.hasStateChange,duration:e.duration,source:"redux",isCurrent:s===0,isInFuture:false,isSkipped:false,meta:{fullAction:e.action}}}export function createReduxHistoryAdapter(e){const{icon:s=g,color:a="#764ABC",name:u="Redux"}=e??{};let o=0;const c={id:"redux",name:u,icon:s,color:a,capabilities:l,subscribe:t=>{return r.subscribe(()=>{t()})},getEntries:()=>{const t=r.getActions();return t.map((n,i)=>p(n,i,t.length))},getEntryCount:()=>{return r.getActions().length},getCurrentState:()=>{const t=r.getActions();if(t.length===0)return void 0;return t[0].nextState},getStateAtEntry:t=>{const n=r.getActionById(String(t));return n?.nextState},get currentIndex(){return o},jumpTo:t=>{const n=r.getActions();const i=n.findIndex(d=>d.id===String(t));if(i!==-1){o=i}},get isRecording(){return r.getEnabled()},setRecording:t=>{r.setEnabled(t)},reset:()=>{r.clearActions();o=0},exportHistory:()=>{const t=r.getActions();return JSON.stringify(t,null,2)}};return c}export const reduxHistoryAdapter=createReduxHistoryAdapter();
@@ -1,145 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Store-creation-time capture via the OFFICIAL Redux DevTools hook.
5
- *
6
- * `configureStore` defaults `devTools: true` and, at RTK module-evaluation
7
- * time, snapshots `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` if defined.
8
- * That global doesn't exist in React Native — so Buoy claims it (dev builds
9
- * only, and only when nothing else has). This makes Buoy a real store
10
- * ENHANCER, present from the store's birth:
11
- *
12
- * - FULL capture: actions dispatched from inside middleware/thunks (RTK
13
- * `pending`/`fulfilled`/`rejected`, RTK Query) hit our dispatch because the
14
- * enhancer sits BELOW applyMiddleware in the compose chain.
15
- * - Real time travel: we wrap the reducer, so BUOY_JUMP_TO_STATE actually
16
- * jumps — and replaceReducer is re-wrapped, so hosts that inject reducers
17
- * dynamically keep working.
18
- * - Nothing is monkey-patched: this is the integration point Redux itself
19
- * ships for devtools (same one react-native-debugger used).
20
- *
21
- * Race note: RTK snapshots the global when ITS module first evaluates, so this
22
- * module must load before `@reduxjs/toolkit` does. `@buoy-gg/core` requires us
23
- * at module scope, which usually wins; when it doesn't, the dispatch-patch
24
- * fallback in autoInstrument.ts still binds at first mount. Apps that want a
25
- * guarantee put `import '@buoy-gg/redux';` as the FIRST import of their entry.
26
- */
27
-
28
- import { reduxActionStore } from "./reduxActionStore";
29
- import { BUOY_JUMP_TO_STATE, isMiddlewareActive } from "./buoyReduxMiddleware";
30
- import { registerActiveStore, markTimeTravelReducer, ENHANCER_SYMBOL } from "./activeStoreRegistry";
31
- /** Standard redux compose. */
32
- function composeFns(...funcs) {
33
- if (funcs.length === 0) return arg => arg;
34
- if (funcs.length === 1) return funcs[0];
35
- return funcs.reduce((a, b) => (...args) => a(b(...args)));
36
- }
37
-
38
- /** Wrap a reducer so BUOY_JUMP_TO_STATE performs a real state jump. */
39
- function wrapReducer(reducer) {
40
- // Announce the capability, not just perform it: with nothing wrapped, a jump
41
- // is dispatched, ignored, and silent — so the UI needs to know in advance
42
- // whether the button it is drawing can keep its promise.
43
- markTimeTravelReducer();
44
- return (state, action) => {
45
- if (action && action.type === BUOY_JUMP_TO_STATE) {
46
- return action.payload;
47
- }
48
- return reducer(state, action);
49
- };
50
- }
51
-
52
- /** Should this action be recorded? (Mirrors autoInstrument's filters.) */
53
- function isRecordable(action) {
54
- if (typeof action !== "object" || action === null) return false;
55
- const type = action.type;
56
- if (typeof type !== "string") return false;
57
- return !type.startsWith("@@buoy/") && !type.startsWith("@@redux/");
58
- }
59
-
60
- /**
61
- * The Buoy store enhancer. Innermost in the compose chain (below
62
- * applyMiddleware), so every action that reaches the reducer — including ones
63
- * dispatched from inside middleware and thunks — passes through here.
64
- */
65
- export const buoyDevtoolsEnhancer = createStore => (reducer, preloadedState) => {
66
- const store = createStore(wrapReducer(reducer), preloadedState);
67
- const dispatch = action => {
68
- // If the explicit middleware is installed it already records — the
69
- // enhancer stays passive to avoid double capture.
70
- if (!isRecordable(action) || isMiddlewareActive()) {
71
- return store.dispatch(action);
72
- }
73
- const startTime = performance.now();
74
- const prevState = store.getState();
75
- const result = store.dispatch(action);
76
- reduxActionStore.addAction(action, prevState, store.getState(), performance.now() - startTime);
77
- return result;
78
- };
79
- const enhanced = {
80
- ...store,
81
- dispatch,
82
- // Keep time travel alive across dynamic reducer injection.
83
- replaceReducer: next => store.replaceReducer(wrapReducer(next)),
84
- [ENHANCER_SYMBOL]: true
85
- };
86
- registerActiveStore(enhanced, "enhancer");
87
- return enhanced;
88
- };
89
-
90
- /**
91
- * Implements the `__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` calling conventions:
92
- * - `compose(options)(...enhancers)` — what RTK's configureStore does
93
- * - `compose(...enhancers)` — classic createStore usage from the Redux docs
94
- */
95
- function buoyComposeWithDevTools(...args) {
96
- if (args.length === 0) {
97
- return composeFns(buoyDevtoolsEnhancer);
98
- }
99
- if (args.length === 1 && typeof args[0] === "object" && args[0] !== null) {
100
- // Options form → return a compose function.
101
- return (...enhancers) => composeFns(...enhancers, buoyDevtoolsEnhancer);
102
- }
103
- return composeFns(...args, buoyDevtoolsEnhancer);
104
- }
105
- let claimed = false;
106
-
107
- /**
108
- * Claim the DevTools COMPOSE global — dev builds only, and never over an
109
- * existing claimant (e.g. the real browser extension on web).
110
- *
111
- * Idempotent; runs at module scope so simply importing `@buoy-gg/redux` (which
112
- * `@buoy-gg/core` does at module scope) performs the claim as early as
113
- * possible.
114
- *
115
- * DO NOT claim `__REDUX_DEVTOOLS_EXTENSION__` here — ever. That global is
116
- * feature-detected BY PRESENCE by non-redux libraries (zustand's devtools
117
- * middleware, valtio, jotai-devtools) which then call the extension OBJECT
118
- * API (`.connect()` etc.) with no shape check. A bare function satisfies only
119
- * redux's legacy calling convention, so claiming it crashes any host app
120
- * whose zustand store uses `devtools(...)` — at store-module evaluation, so
121
- * the app doesn't boot, and the surfaced error is a misleading React
122
- * "Element type is invalid" from whatever lazy component imports next.
123
- * (Shipped in 7.0.6/7.0.7; broke a large enterprise app; removed in the next
124
- * patch.) Absent, all those libraries no-op gracefully. The COMPOSE global is
125
- * safe because every known consumer CALLS it as a function — that is its
126
- * whole contract — and it covers the dominant path (RTK's configureStore
127
- * default). The legacy `createStore(reducer, __REDUX_DEVTOOLS_EXTENSION__())`
128
- * pattern simply falls back to Buoy's dispatch-patch instrumentation.
129
- */
130
- export function claimDevtoolsHook() {
131
- const isDev = typeof __DEV__ !== "undefined" && __DEV__;
132
- if (!isDev) return false;
133
- const g = globalThis;
134
- if (!g.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
135
- g.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = buoyComposeWithDevTools;
136
- claimed = true;
137
- }
138
- return claimed;
139
- }
140
-
141
- /** Whether Buoy owns the DevTools compose global (i.e. won the load race). */
142
- export function isDevtoolsHookClaimed() {
143
- return claimed;
144
- }
145
- claimDevtoolsHook();
1
+ "use strict";import{reduxActionStore as d}from"./reduxActionStore";import{BUOY_JUMP_TO_STATE as a,isMiddlewareActive as m}from"./buoyReduxMiddleware";import{registerActiveStore as E,markTimeTravelReducer as h,ENHANCER_SYMBOL as O}from"./activeStoreRegistry";function u(...e){if(e.length===0)return t=>t;if(e.length===1)return e[0];return e.reduce((t,r)=>(...o)=>t(r(...o)))}function c(e){h();return(t,r)=>{if(r&&r.type===a){return r.payload}return e(t,r)}}function S(e){if(typeof e!=="object"||e===null)return false;const t=e.type;if(typeof t!=="string")return false;return!t.startsWith("@@buoy/")&&!t.startsWith("@@redux/")}export const buoyDevtoolsEnhancer=e=>(t,r)=>{const o=e(c(t),r);const l=n=>{if(!S(n)||m()){return o.dispatch(n)}const f=performance.now();const _=o.getState();const p=o.dispatch(n);d.addAction(n,_,o.getState(),performance.now()-f);return p};const s={...o,dispatch:l,replaceReducer:n=>o.replaceReducer(c(n)),[O]:true};E(s,"enhancer");return s};function T(...e){if(e.length===0){return u(buoyDevtoolsEnhancer)}if(e.length===1&&typeof e[0]==="object"&&e[0]!==null){return(...t)=>u(...t,buoyDevtoolsEnhancer)}return u(...e,buoyDevtoolsEnhancer)}let i=false;export function claimDevtoolsHook(){const e=typeof __DEV__!=="undefined"&&__DEV__;if(!e)return false;const t=globalThis;if(!t.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__){t.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__=T;i=true}return i}export function isDevtoolsHookClaimed(){return i}claimDevtoolsHook();