@buoy-gg/redux 2.1.12 → 2.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/index.js +1 -179
- package/lib/commonjs/preset.js +1 -98
- package/lib/commonjs/redux/components/ReduxActionButton.js +1 -129
- package/lib/commonjs/redux/components/ReduxActionDetailContent.js +1 -380
- package/lib/commonjs/redux/components/ReduxActionDetailView.js +1 -401
- package/lib/commonjs/redux/components/ReduxActionInfoView.js +1 -838
- package/lib/commonjs/redux/components/ReduxActionItem.js +1 -366
- package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +1 -134
- package/lib/commonjs/redux/components/ReduxIcon.js +1 -18
- package/lib/commonjs/redux/components/ReduxModal.js +1 -530
- package/lib/commonjs/redux/components/index.js +1 -52
- package/lib/commonjs/redux/hooks/index.js +1 -25
- package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +1 -197
- package/lib/commonjs/redux/hooks/useReduxActions.js +1 -75
- package/lib/commonjs/redux/index.js +1 -49
- package/lib/commonjs/redux/utils/autoInstrument.js +1 -270
- package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +1 -166
- package/lib/commonjs/redux/utils/createReduxHistoryAdapter.js +1 -146
- package/lib/commonjs/redux/utils/index.js +1 -111
- package/lib/commonjs/redux/utils/reduxActionStore.js +1 -358
- package/lib/module/index.js +1 -87
- package/lib/module/preset.js +1 -94
- package/lib/module/redux/components/ReduxActionButton.js +1 -126
- package/lib/module/redux/components/ReduxActionDetailContent.js +1 -376
- package/lib/module/redux/components/ReduxActionDetailView.js +1 -397
- package/lib/module/redux/components/ReduxActionInfoView.js +1 -833
- package/lib/module/redux/components/ReduxActionItem.js +1 -362
- package/lib/module/redux/components/ReduxDetailViewToggle.js +1 -129
- package/lib/module/redux/components/ReduxIcon.js +1 -8
- package/lib/module/redux/components/ReduxModal.js +1 -525
- package/lib/module/redux/components/index.js +1 -7
- package/lib/module/redux/hooks/index.js +1 -4
- package/lib/module/redux/hooks/useAutoInstrumentRedux.js +1 -193
- package/lib/module/redux/hooks/useReduxActions.js +1 -71
- package/lib/module/redux/index.js +1 -13
- package/lib/module/redux/utils/autoInstrument.js +1 -260
- package/lib/module/redux/utils/buoyReduxMiddleware.js +1 -157
- package/lib/module/redux/utils/createReduxHistoryAdapter.js +1 -142
- package/lib/module/redux/utils/index.js +1 -8
- package/lib/module/redux/utils/reduxActionStore.js +1 -354
- package/package.json +4 -4
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxActionButton.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxActionDetailView.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxIcon.d.ts.map +0 -1
- package/lib/typescript/redux/components/ReduxModal.d.ts.map +0 -1
- package/lib/typescript/redux/components/index.d.ts.map +0 -1
- package/lib/typescript/redux/hooks/index.d.ts.map +0 -1
- package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts.map +0 -1
- package/lib/typescript/redux/hooks/useReduxActions.d.ts.map +0 -1
- package/lib/typescript/redux/index.d.ts.map +0 -1
- package/lib/typescript/redux/types/index.d.ts.map +0 -1
- package/lib/typescript/redux/utils/autoInstrument.d.ts.map +0 -1
- package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +0 -1
- package/lib/typescript/redux/utils/createReduxHistoryAdapter.d.ts.map +0 -1
- package/lib/typescript/redux/utils/index.d.ts.map +0 -1
- package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +0 -1
|
@@ -1,157 +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 } from "./reduxActionStore";
|
|
20
|
-
|
|
21
|
-
// ============================================
|
|
22
|
-
// Middleware Active Tracking
|
|
23
|
-
// ============================================
|
|
24
|
-
|
|
25
|
-
/** Flag to indicate middleware is handling action capture */
|
|
26
|
-
let middlewareActive = false;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Check if the explicit middleware is active
|
|
30
|
-
* Used by auto-instrument to avoid duplicate capture
|
|
31
|
-
*/
|
|
32
|
-
export function isMiddlewareActive() {
|
|
33
|
-
return middlewareActive;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ============================================
|
|
37
|
-
// Time-Travel Action Types
|
|
38
|
-
// ============================================
|
|
39
|
-
|
|
40
|
-
export const BUOY_JUMP_TO_STATE = "@@buoy/JUMP_TO_STATE";
|
|
41
|
-
export const BUOY_REPLAY_ACTION = "@@buoy/REPLAY_ACTION";
|
|
42
|
-
// Store reference to dispatch for time-travel
|
|
43
|
-
let storeDispatch = null;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Jump to a specific state (time-travel)
|
|
47
|
-
*/
|
|
48
|
-
export function jumpToState(state) {
|
|
49
|
-
if (storeDispatch) {
|
|
50
|
-
storeDispatch({
|
|
51
|
-
type: BUOY_JUMP_TO_STATE,
|
|
52
|
-
payload: state
|
|
53
|
-
});
|
|
54
|
-
} else {
|
|
55
|
-
console.warn("[BuoyRedux] Cannot jump to state - middleware not initialized");
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Replay an action (dispatch it again)
|
|
61
|
-
*/
|
|
62
|
-
export function replayAction(action) {
|
|
63
|
-
if (storeDispatch) {
|
|
64
|
-
// Dispatch the original action directly
|
|
65
|
-
storeDispatch(action);
|
|
66
|
-
} else {
|
|
67
|
-
console.warn("[BuoyRedux] Cannot replay action - middleware not initialized");
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Wrap your root reducer to enable time-travel support
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* ```tsx
|
|
76
|
-
* const store = configureStore({
|
|
77
|
-
* reducer: withBuoyDevTools(rootReducer),
|
|
78
|
-
* middleware: (getDefault) => getDefault().concat(buoyReduxMiddleware),
|
|
79
|
-
* });
|
|
80
|
-
* ```
|
|
81
|
-
*/
|
|
82
|
-
export function withBuoyDevTools(reducer) {
|
|
83
|
-
return (state, action) => {
|
|
84
|
-
// Handle jump to state action
|
|
85
|
-
if (action.type === BUOY_JUMP_TO_STATE) {
|
|
86
|
-
const jumpAction = action;
|
|
87
|
-
return jumpAction.payload;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Pass through to original reducer
|
|
91
|
-
return reducer(state, action);
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Default middleware - just add to your store!
|
|
97
|
-
*/
|
|
98
|
-
export const buoyReduxMiddleware = storeApi => {
|
|
99
|
-
// Mark middleware as active so auto-instrument knows to skip
|
|
100
|
-
middlewareActive = true;
|
|
101
|
-
|
|
102
|
-
// Store dispatch reference for time-travel
|
|
103
|
-
storeDispatch = storeApi.dispatch;
|
|
104
|
-
return next => action => {
|
|
105
|
-
const actionObj = action;
|
|
106
|
-
const actionType = actionObj.type;
|
|
107
|
-
|
|
108
|
-
// Don't record devtools internal actions
|
|
109
|
-
if (actionType?.startsWith("@@buoy/")) {
|
|
110
|
-
return next(action);
|
|
111
|
-
}
|
|
112
|
-
const startTime = performance.now();
|
|
113
|
-
const prevState = storeApi.getState();
|
|
114
|
-
const result = next(action);
|
|
115
|
-
const nextState = storeApi.getState();
|
|
116
|
-
const duration = performance.now() - startTime;
|
|
117
|
-
reduxActionStore.addAction(actionObj, prevState, nextState, duration);
|
|
118
|
-
return result;
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Create middleware with custom options
|
|
124
|
-
*/
|
|
125
|
-
export function createBuoyReduxMiddleware(options = {}) {
|
|
126
|
-
const {
|
|
127
|
-
maxActions = 200,
|
|
128
|
-
ignoreActions = []
|
|
129
|
-
} = options;
|
|
130
|
-
reduxActionStore.setMaxActions(maxActions);
|
|
131
|
-
return storeApi => {
|
|
132
|
-
// Mark middleware as active so auto-instrument knows to skip
|
|
133
|
-
middlewareActive = true;
|
|
134
|
-
|
|
135
|
-
// Store dispatch reference for time-travel
|
|
136
|
-
storeDispatch = storeApi.dispatch;
|
|
137
|
-
return next => action => {
|
|
138
|
-
const actionObj = action;
|
|
139
|
-
const actionType = actionObj.type || "";
|
|
140
|
-
|
|
141
|
-
// Don't record devtools internal actions
|
|
142
|
-
if (actionType?.startsWith("@@buoy/")) {
|
|
143
|
-
return next(action);
|
|
144
|
-
}
|
|
145
|
-
if (ignoreActions.includes(actionType)) {
|
|
146
|
-
return next(action);
|
|
147
|
-
}
|
|
148
|
-
const startTime = performance.now();
|
|
149
|
-
const prevState = storeApi.getState();
|
|
150
|
-
const result = next(action);
|
|
151
|
-
const nextState = storeApi.getState();
|
|
152
|
-
const duration = performance.now() - startTime;
|
|
153
|
-
reduxActionStore.addAction(actionObj, prevState, nextState, duration);
|
|
154
|
-
return result;
|
|
155
|
-
};
|
|
156
|
-
};
|
|
157
|
-
}
|
|
1
|
+
"use strict";import{reduxActionStore}from"./reduxActionStore";let middlewareActive=!1;export function isMiddlewareActive(){return middlewareActive}export const BUOY_JUMP_TO_STATE="@@buoy/JUMP_TO_STATE";export const BUOY_REPLAY_ACTION="@@buoy/REPLAY_ACTION";let storeDispatch=null;export function jumpToState(t){storeDispatch?storeDispatch({type:BUOY_JUMP_TO_STATE,payload:t}):console.warn("[BuoyRedux] Cannot jump to state - middleware not initialized")}export function replayAction(t){storeDispatch?storeDispatch(t):console.warn("[BuoyRedux] Cannot replay action - middleware not initialized")}export function withBuoyDevTools(t){return(e,o)=>o.type===BUOY_JUMP_TO_STATE?o.payload:t(e,o)}export const buoyReduxMiddleware=t=>(middlewareActive=!0,storeDispatch=t.dispatch,e=>o=>{const r=o,n=r.type;if(n?.startsWith("@@buoy/"))return e(o);const i=performance.now(),a=t.getState(),c=e(o),s=t.getState(),d=performance.now()-i;return reduxActionStore.addAction(r,a,s,d),c});export function createBuoyReduxMiddleware(t={}){const{maxActions:e=200,ignoreActions:o=[]}=t;return reduxActionStore.setMaxActions(e),t=>(middlewareActive=!0,storeDispatch=t.dispatch,e=>r=>{const n=r,i=n.type||"";if(i?.startsWith("@@buoy/"))return e(r);if(o.includes(i))return e(r);const a=performance.now(),c=t.getState(),s=e(r),d=t.getState(),u=performance.now()-a;return reduxActionStore.addAction(n,c,d,u),s})}
|
|
@@ -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}from"./reduxActionStore";import{ReduxIcon}from"../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}}}export function createReduxHistoryAdapter(t){const{icon:e=ReduxIcon,color:r="#764ABC",name:n="Redux"}=t??{};let o=0;return{id:"redux",name:n,icon:e,color:r,capabilities:REDUX_CAPABILITIES,subscribe:t=>reduxActionStore.subscribe(()=>{t()}),getEntries:()=>{const t=reduxActionStore.getActions();return t.map((e,r)=>toHistoryEntry(e,r,t.length))},getEntryCount:()=>reduxActionStore.getActions().length,getCurrentState:()=>{const t=reduxActionStore.getActions();if(0!==t.length)return t[0].nextState},getStateAtEntry:t=>{const e=reduxActionStore.getActionById(String(t));return e?.nextState},get currentIndex(){return o},jumpTo:t=>{const e=reduxActionStore.getActions().findIndex(e=>e.id===String(t));-1!==e&&(o=e)},get isRecording(){return reduxActionStore.getEnabled()},setRecording:t=>{reduxActionStore.setEnabled(t)},reset:()=>{reduxActionStore.clearActions(),o=0},exportHistory:()=>{const t=reduxActionStore.getActions();return JSON.stringify(t,null,2)}}}export const reduxHistoryAdapter=createReduxHistoryAdapter();
|
|
@@ -1,8 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
export { reduxActionStore } from "./reduxActionStore";
|
|
4
|
-
export { buoyReduxMiddleware, createBuoyReduxMiddleware, withBuoyDevTools, jumpToState, replayAction, BUOY_JUMP_TO_STATE, BUOY_REPLAY_ACTION } from "./buoyReduxMiddleware";
|
|
5
|
-
export { createReduxHistoryAdapter, reduxHistoryAdapter } from "./createReduxHistoryAdapter";
|
|
6
|
-
|
|
7
|
-
// Auto-instrumentation (zero-config)
|
|
8
|
-
export { instrumentStore, uninstrumentStore, isStoreInstrumented, isAutoInstrumentActive, getActiveStore, jumpToState as autoJumpToState, replayAction as autoReplayAction } from "./autoInstrument";
|
|
1
|
+
"use strict";export{reduxActionStore}from"./reduxActionStore";export{buoyReduxMiddleware,createBuoyReduxMiddleware,withBuoyDevTools,jumpToState,replayAction,BUOY_JUMP_TO_STATE,BUOY_REPLAY_ACTION}from"./buoyReduxMiddleware";export{createReduxHistoryAdapter,reduxHistoryAdapter}from"./createReduxHistoryAdapter";export{instrumentStore,uninstrumentStore,isStoreInstrumented,isAutoInstrumentActive,getActiveStore,jumpToState as autoJumpToState,replayAction as autoReplayAction}from"./autoInstrument";
|
|
@@ -1,354 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Redux action store - captures and stores Redux Toolkit actions
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ============================================
|
|
8
|
-
// Helper Functions for Enhanced Data
|
|
9
|
-
// ============================================
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Get action category from type string
|
|
13
|
-
*/
|
|
14
|
-
function getActionCategory(type) {
|
|
15
|
-
if (type.startsWith("@@")) return "internal";
|
|
16
|
-
if (type.includes("/pending")) return "pending";
|
|
17
|
-
if (type.includes("/fulfilled")) return "fulfilled";
|
|
18
|
-
if (type.includes("/rejected")) return "rejected";
|
|
19
|
-
if (type.includes("/executeQuery")) return "query";
|
|
20
|
-
if (type.includes("/executeMutation")) return "mutation";
|
|
21
|
-
return "action";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Parse action type to extract slice and action names
|
|
26
|
-
*/
|
|
27
|
-
function parseActionType(type) {
|
|
28
|
-
// Handle internal actions
|
|
29
|
-
if (type.startsWith("@@")) {
|
|
30
|
-
const parts = type.split("/");
|
|
31
|
-
return {
|
|
32
|
-
sliceName: null,
|
|
33
|
-
actionName: parts[parts.length - 1] || type
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Handle RTK pattern: "sliceName/actionName" or "sliceName/actionName/status"
|
|
38
|
-
const parts = type.split("/");
|
|
39
|
-
if (parts.length >= 2) {
|
|
40
|
-
const sliceName = parts[0];
|
|
41
|
-
// For async thunks, action name might be "fetchUser/pending" -> we want "fetchUser"
|
|
42
|
-
let actionName = parts[1];
|
|
43
|
-
|
|
44
|
-
// If it's an async status, use the base action name
|
|
45
|
-
if (parts.length >= 3 && ["pending", "fulfilled", "rejected"].includes(parts[parts.length - 1])) {
|
|
46
|
-
actionName = parts.slice(1, -1).join("/");
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
sliceName,
|
|
50
|
-
actionName: actionName.toUpperCase()
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
sliceName: null,
|
|
55
|
-
actionName: type.toUpperCase()
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Format payload preview for display
|
|
61
|
-
*/
|
|
62
|
-
function formatPayloadPreview(payload, maxLength = 40) {
|
|
63
|
-
if (payload === undefined) return "";
|
|
64
|
-
if (payload === null) return "null";
|
|
65
|
-
try {
|
|
66
|
-
if (typeof payload === "string") {
|
|
67
|
-
return payload.length > maxLength ? `"${payload.slice(0, maxLength - 3)}..."` : `"${payload}"`;
|
|
68
|
-
}
|
|
69
|
-
if (typeof payload === "number" || typeof payload === "boolean") {
|
|
70
|
-
return String(payload);
|
|
71
|
-
}
|
|
72
|
-
if (Array.isArray(payload)) {
|
|
73
|
-
if (payload.length === 0) return "[]";
|
|
74
|
-
const preview = JSON.stringify(payload);
|
|
75
|
-
return preview.length > maxLength ? `[${payload.length} items]` : preview;
|
|
76
|
-
}
|
|
77
|
-
if (typeof payload === "object") {
|
|
78
|
-
const keys = Object.keys(payload);
|
|
79
|
-
if (keys.length === 0) return "{}";
|
|
80
|
-
|
|
81
|
-
// Try to create a meaningful preview
|
|
82
|
-
const preview = JSON.stringify(payload);
|
|
83
|
-
if (preview.length <= maxLength) return preview;
|
|
84
|
-
|
|
85
|
-
// Show key count
|
|
86
|
-
return `{ ${keys.length} keys }`;
|
|
87
|
-
}
|
|
88
|
-
return String(payload).slice(0, maxLength);
|
|
89
|
-
} catch {
|
|
90
|
-
return "[complex]";
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Calculate state diff summary
|
|
96
|
-
*/
|
|
97
|
-
function getStateDiffSummary(prevState, nextState) {
|
|
98
|
-
if (prevState === nextState) {
|
|
99
|
-
return {
|
|
100
|
-
summary: "no change",
|
|
101
|
-
changedCount: 0
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Handle non-objects
|
|
106
|
-
if (typeof prevState !== "object" || typeof nextState !== "object" || prevState === null || nextState === null) {
|
|
107
|
-
return {
|
|
108
|
-
summary: "changed",
|
|
109
|
-
changedCount: 1
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
const prevKeys = Object.keys(prevState);
|
|
113
|
-
const nextKeys = Object.keys(nextState);
|
|
114
|
-
const added = nextKeys.filter(k => !prevKeys.includes(k)).length;
|
|
115
|
-
const removed = prevKeys.filter(k => !nextKeys.includes(k)).length;
|
|
116
|
-
|
|
117
|
-
// Check which existing keys changed
|
|
118
|
-
let changed = 0;
|
|
119
|
-
for (const key of prevKeys) {
|
|
120
|
-
if (nextKeys.includes(key) && prevState[key] !== nextState[key]) {
|
|
121
|
-
changed++;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const parts = [];
|
|
125
|
-
if (added > 0) parts.push(`+${added}`);
|
|
126
|
-
if (removed > 0) parts.push(`-${removed}`);
|
|
127
|
-
if (changed > 0) parts.push(`~${changed}`);
|
|
128
|
-
const totalChanged = added + removed + changed;
|
|
129
|
-
if (parts.length === 0) {
|
|
130
|
-
return {
|
|
131
|
-
summary: "nested change",
|
|
132
|
-
changedCount: 1
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
summary: `${parts.join(" ")} ${totalChanged === 1 ? "key" : "keys"}`,
|
|
137
|
-
changedCount: totalChanged
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Extract RTK async thunk metadata
|
|
143
|
-
*/
|
|
144
|
-
function extractMeta(action) {
|
|
145
|
-
const meta = action.meta;
|
|
146
|
-
if (!meta) return undefined;
|
|
147
|
-
return {
|
|
148
|
-
requestId: meta.requestId,
|
|
149
|
-
requestStatus: meta.requestStatus,
|
|
150
|
-
arg: meta.arg,
|
|
151
|
-
rejectedWithValue: meta.rejectedWithValue,
|
|
152
|
-
aborted: meta.aborted,
|
|
153
|
-
condition: meta.condition
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ============================================
|
|
158
|
-
// Redux Action Store
|
|
159
|
-
// ============================================
|
|
160
|
-
|
|
161
|
-
class ReduxActionStore {
|
|
162
|
-
actions = [];
|
|
163
|
-
listeners = new Set();
|
|
164
|
-
maxActions = 200;
|
|
165
|
-
idCounter = 0;
|
|
166
|
-
isEnabled = true;
|
|
167
|
-
addAction(action, prevState, nextState, duration) {
|
|
168
|
-
if (!this.isEnabled) {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const actionType = action.type || "";
|
|
172
|
-
|
|
173
|
-
// Skip actions without a proper type (e.g., thunk functions)
|
|
174
|
-
if (!actionType) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const hasStateChange = prevState !== nextState;
|
|
178
|
-
|
|
179
|
-
// Enhanced data extraction
|
|
180
|
-
const category = getActionCategory(actionType);
|
|
181
|
-
const {
|
|
182
|
-
sliceName,
|
|
183
|
-
actionName
|
|
184
|
-
} = parseActionType(actionType);
|
|
185
|
-
const meta = extractMeta(action);
|
|
186
|
-
const payloadPreview = formatPayloadPreview(action.payload);
|
|
187
|
-
const {
|
|
188
|
-
summary: diffSummary,
|
|
189
|
-
changedCount: changedKeysCount
|
|
190
|
-
} = getStateDiffSummary(prevState, nextState);
|
|
191
|
-
const isSlowAction = (duration ?? 0) > 16; // Frame budget
|
|
192
|
-
|
|
193
|
-
const reduxAction = {
|
|
194
|
-
id: `${Date.now()}-${++this.idCounter}`,
|
|
195
|
-
type: actionType,
|
|
196
|
-
payload: action.payload,
|
|
197
|
-
action,
|
|
198
|
-
timestamp: Date.now(),
|
|
199
|
-
prevState,
|
|
200
|
-
nextState,
|
|
201
|
-
duration,
|
|
202
|
-
hasStateChange,
|
|
203
|
-
// Enhanced fields
|
|
204
|
-
category,
|
|
205
|
-
sliceName,
|
|
206
|
-
actionName,
|
|
207
|
-
meta,
|
|
208
|
-
error: action.error,
|
|
209
|
-
payloadPreview,
|
|
210
|
-
diffSummary,
|
|
211
|
-
changedKeysCount,
|
|
212
|
-
isSlowAction
|
|
213
|
-
};
|
|
214
|
-
this.actions = [reduxAction, ...this.actions].slice(0, this.maxActions);
|
|
215
|
-
this.notifyListeners();
|
|
216
|
-
}
|
|
217
|
-
getActions() {
|
|
218
|
-
return [...this.actions];
|
|
219
|
-
}
|
|
220
|
-
getActionById(id) {
|
|
221
|
-
return this.actions.find(a => a.id === id);
|
|
222
|
-
}
|
|
223
|
-
clearActions() {
|
|
224
|
-
this.actions = [];
|
|
225
|
-
this.notifyListeners();
|
|
226
|
-
}
|
|
227
|
-
setEnabled(enabled) {
|
|
228
|
-
this.isEnabled = enabled;
|
|
229
|
-
}
|
|
230
|
-
getEnabled() {
|
|
231
|
-
return this.isEnabled;
|
|
232
|
-
}
|
|
233
|
-
subscribe(listener) {
|
|
234
|
-
this.listeners.add(listener);
|
|
235
|
-
return () => {
|
|
236
|
-
this.listeners.delete(listener);
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
notifyListeners() {
|
|
240
|
-
const actions = this.getActions();
|
|
241
|
-
this.listeners.forEach(listener => listener(actions));
|
|
242
|
-
}
|
|
243
|
-
setMaxActions(max) {
|
|
244
|
-
this.maxActions = max;
|
|
245
|
-
if (this.actions.length > max) {
|
|
246
|
-
this.actions = this.actions.slice(0, max);
|
|
247
|
-
this.notifyListeners();
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
getStats() {
|
|
251
|
-
const total = this.actions.length;
|
|
252
|
-
const withChanges = this.actions.filter(a => a.hasStateChange).length;
|
|
253
|
-
const actionTypes = new Set(this.actions.map(a => a.type));
|
|
254
|
-
|
|
255
|
-
// Calculate average duration
|
|
256
|
-
const durations = this.actions.filter(a => a.duration !== undefined).map(a => a.duration);
|
|
257
|
-
const avgDuration = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0;
|
|
258
|
-
return {
|
|
259
|
-
totalActions: total,
|
|
260
|
-
actionsWithChanges: withChanges,
|
|
261
|
-
actionsWithoutChanges: total - withChanges,
|
|
262
|
-
uniqueActionTypes: actionTypes.size,
|
|
263
|
-
averageDuration: Math.round(avgDuration * 100) / 100
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
filterActions(filter) {
|
|
267
|
-
let filtered = [...this.actions];
|
|
268
|
-
if (filter.searchText) {
|
|
269
|
-
const search = filter.searchText.toLowerCase();
|
|
270
|
-
filtered = filtered.filter(a => a.type.toLowerCase().includes(search));
|
|
271
|
-
}
|
|
272
|
-
if (filter.actionTypes && filter.actionTypes.length > 0) {
|
|
273
|
-
filtered = filtered.filter(a => filter.actionTypes.includes(a.type));
|
|
274
|
-
}
|
|
275
|
-
if (filter.onlyWithChanges) {
|
|
276
|
-
filtered = filtered.filter(a => a.hasStateChange);
|
|
277
|
-
}
|
|
278
|
-
return filtered;
|
|
279
|
-
}
|
|
280
|
-
getUniqueActionTypes() {
|
|
281
|
-
return Array.from(new Set(this.actions.map(a => a.type))).sort();
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Find related async actions by requestId
|
|
286
|
-
*/
|
|
287
|
-
getRelatedActions(requestId) {
|
|
288
|
-
return this.actions.filter(a => a.meta?.requestId === requestId).sort((a, b) => a.timestamp - b.timestamp); // Sort chronologically
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Get all actions that share a requestId with the given action
|
|
293
|
-
*/
|
|
294
|
-
getLinkedActions(action) {
|
|
295
|
-
if (!action.meta?.requestId) return [];
|
|
296
|
-
return this.getRelatedActions(action.meta.requestId);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Get count of linked actions for a given action
|
|
301
|
-
*/
|
|
302
|
-
getLinkedActionsCount(action) {
|
|
303
|
-
if (!action.meta?.requestId) return 0;
|
|
304
|
-
return this.actions.filter(a => a.meta?.requestId === action.meta?.requestId).length;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Calculate total duration for an async operation (pending to resolution)
|
|
309
|
-
*/
|
|
310
|
-
getAsyncOperationDuration(requestId) {
|
|
311
|
-
const related = this.getRelatedActions(requestId);
|
|
312
|
-
if (related.length < 2) return null;
|
|
313
|
-
const pending = related.find(a => a.category === "pending");
|
|
314
|
-
const resolution = related.find(a => a.category === "fulfilled" || a.category === "rejected");
|
|
315
|
-
if (pending && resolution) {
|
|
316
|
-
return resolution.timestamp - pending.timestamp;
|
|
317
|
-
}
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Get the base action type (without /pending, /fulfilled, /rejected suffix)
|
|
323
|
-
*/
|
|
324
|
-
getBaseActionType(type) {
|
|
325
|
-
const parts = type.split("/");
|
|
326
|
-
if (parts.length >= 2) {
|
|
327
|
-
const lastPart = parts[parts.length - 1];
|
|
328
|
-
if (["pending", "fulfilled", "rejected"].includes(lastPart)) {
|
|
329
|
-
return parts.slice(0, -1).join("/");
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return type;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Get sequential index for an async operation (1, 2, 3, etc.)
|
|
337
|
-
* Based on order of when pending actions first appeared
|
|
338
|
-
*/
|
|
339
|
-
getAsyncOperationIndex(requestId) {
|
|
340
|
-
// Get all unique requestIds with their first timestamp
|
|
341
|
-
const requestIdTimestamps = new Map();
|
|
342
|
-
for (const action of this.actions) {
|
|
343
|
-
if (action.meta?.requestId && !requestIdTimestamps.has(action.meta.requestId)) {
|
|
344
|
-
requestIdTimestamps.set(action.meta.requestId, action.timestamp);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Sort by timestamp (oldest first) and find index
|
|
349
|
-
const sortedIds = Array.from(requestIdTimestamps.entries()).sort((a, b) => a[1] - b[1]).map(([id]) => id);
|
|
350
|
-
const index = sortedIds.indexOf(requestId);
|
|
351
|
-
return index >= 0 ? index + 1 : 0;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
export const reduxActionStore = new ReduxActionStore();
|
|
1
|
+
"use strict";function getActionCategory(t){return t.startsWith("@@")?"internal":t.includes("/pending")?"pending":t.includes("/fulfilled")?"fulfilled":t.includes("/rejected")?"rejected":t.includes("/executeQuery")?"query":t.includes("/executeMutation")?"mutation":"action"}function parseActionType(t){if(t.startsWith("@@")){const e=t.split("/");return{sliceName:null,actionName:e[e.length-1]||t}}const e=t.split("/");if(e.length>=2){const t=e[0];let n=e[1];return e.length>=3&&["pending","fulfilled","rejected"].includes(e[e.length-1])&&(n=e.slice(1,-1).join("/")),{sliceName:t,actionName:n.toUpperCase()}}return{sliceName:null,actionName:t.toUpperCase()}}function formatPayloadPreview(t,e=40){if(void 0===t)return"";if(null===t)return"null";try{if("string"==typeof t)return t.length>e?`"${t.slice(0,e-3)}..."`:`"${t}"`;if("number"==typeof t||"boolean"==typeof t)return String(t);if(Array.isArray(t)){if(0===t.length)return"[]";const n=JSON.stringify(t);return n.length>e?`[${t.length} items]`:n}if("object"==typeof t){const n=Object.keys(t);if(0===n.length)return"{}";const i=JSON.stringify(t);return i.length<=e?i:`{ ${n.length} keys }`}return String(t).slice(0,e)}catch{return"[complex]"}}function getStateDiffSummary(t,e){if(t===e)return{summary:"no change",changedCount:0};if("object"!=typeof t||"object"!=typeof e||null===t||null===e)return{summary:"changed",changedCount:1};const n=Object.keys(t),i=Object.keys(e),s=i.filter(t=>!n.includes(t)).length,r=n.filter(t=>!i.includes(t)).length;let a=0;for(const s of n)i.includes(s)&&t[s]!==e[s]&&a++;const o=[];s>0&&o.push(`+${s}`),r>0&&o.push(`-${r}`),a>0&&o.push(`~${a}`);const c=s+r+a;return 0===o.length?{summary:"nested change",changedCount:1}:{summary:`${o.join(" ")} ${1===c?"key":"keys"}`,changedCount:c}}function extractMeta(t){const e=t.meta;if(e)return{requestId:e.requestId,requestStatus:e.requestStatus,arg:e.arg,rejectedWithValue:e.rejectedWithValue,aborted:e.aborted,condition:e.condition}}class ReduxActionStore{actions=[];listeners=new Set;maxActions=200;idCounter=0;isEnabled=!0;addAction(t,e,n,i){if(!this.isEnabled)return;const s=t.type||"";if(!s)return;const r=e!==n,a=getActionCategory(s),{sliceName:o,actionName:c}=parseActionType(s),u=extractMeta(t),l=formatPayloadPreview(t.payload),{summary:d,changedCount:h}=getStateDiffSummary(e,n),g=(i??0)>16,f={id:`${Date.now()}-${++this.idCounter}`,type:s,payload:t.payload,action:t,timestamp:Date.now(),prevState:e,nextState:n,duration:i,hasStateChange:r,category:a,sliceName:o,actionName:c,meta:u,error:t.error,payloadPreview:l,diffSummary:d,changedKeysCount:h,isSlowAction:g};this.actions=[f,...this.actions].slice(0,this.maxActions),this.notifyListeners()}getActions(){return[...this.actions]}getActionById(t){return this.actions.find(e=>e.id===t)}clearActions(){this.actions=[],this.notifyListeners()}setEnabled(t){this.isEnabled=t}getEnabled(){return this.isEnabled}subscribe(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}notifyListeners(){const t=this.getActions();this.listeners.forEach(e=>e(t))}setMaxActions(t){this.maxActions=t,this.actions.length>t&&(this.actions=this.actions.slice(0,t),this.notifyListeners())}getStats(){const t=this.actions.length,e=this.actions.filter(t=>t.hasStateChange).length,n=new Set(this.actions.map(t=>t.type)),i=this.actions.filter(t=>void 0!==t.duration).map(t=>t.duration),s=i.length>0?i.reduce((t,e)=>t+e,0)/i.length:0;return{totalActions:t,actionsWithChanges:e,actionsWithoutChanges:t-e,uniqueActionTypes:n.size,averageDuration:Math.round(100*s)/100}}filterActions(t){let e=[...this.actions];if(t.searchText){const n=t.searchText.toLowerCase();e=e.filter(t=>t.type.toLowerCase().includes(n))}return t.actionTypes&&t.actionTypes.length>0&&(e=e.filter(e=>t.actionTypes.includes(e.type))),t.onlyWithChanges&&(e=e.filter(t=>t.hasStateChange)),e}getUniqueActionTypes(){return Array.from(new Set(this.actions.map(t=>t.type))).sort()}getRelatedActions(t){return this.actions.filter(e=>e.meta?.requestId===t).sort((t,e)=>t.timestamp-e.timestamp)}getLinkedActions(t){return t.meta?.requestId?this.getRelatedActions(t.meta.requestId):[]}getLinkedActionsCount(t){return t.meta?.requestId?this.actions.filter(e=>e.meta?.requestId===t.meta?.requestId).length:0}getAsyncOperationDuration(t){const e=this.getRelatedActions(t);if(e.length<2)return null;const n=e.find(t=>"pending"===t.category),i=e.find(t=>"fulfilled"===t.category||"rejected"===t.category);return n&&i?i.timestamp-n.timestamp:null}getBaseActionType(t){const e=t.split("/");if(e.length>=2){const t=e[e.length-1];if(["pending","fulfilled","rejected"].includes(t))return e.slice(0,-1).join("/")}return t}getAsyncOperationIndex(t){const e=new Map;for(const t of this.actions)t.meta?.requestId&&!e.has(t.meta.requestId)&&e.set(t.meta.requestId,t.timestamp);const n=Array.from(e.entries()).sort((t,e)=>t[1]-e[1]).map(([t])=>t).indexOf(t);return n>=0?n+1:0}}export const reduxActionStore=new ReduxActionStore;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/redux",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.13",
|
|
4
4
|
"description": "Redux Toolkit DevTools for React Native",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
],
|
|
27
27
|
"sideEffects": false,
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@buoy-gg/shared-ui": "2.1.
|
|
30
|
-
"@buoy-gg/
|
|
31
|
-
"@buoy-gg/
|
|
29
|
+
"@buoy-gg/shared-ui": "2.1.13",
|
|
30
|
+
"@buoy-gg/floating-tools-core": "2.1.13",
|
|
31
|
+
"@buoy-gg/license": "2.1.13"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@reduxjs/toolkit": ">=2.0.0",
|