@buoy-gg/redux 2.1.16 → 3.0.0

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 +186 -1
  2. package/lib/commonjs/preset.js +98 -1
  3. package/lib/commonjs/redux/components/ReduxActionButton.js +129 -1
  4. package/lib/commonjs/redux/components/ReduxActionDetailContent.js +380 -1
  5. package/lib/commonjs/redux/components/ReduxActionDetailView.js +401 -1
  6. package/lib/commonjs/redux/components/ReduxActionInfoView.js +838 -1
  7. package/lib/commonjs/redux/components/ReduxActionItem.js +366 -1
  8. package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +134 -1
  9. package/lib/commonjs/redux/components/ReduxIcon.js +18 -1
  10. package/lib/commonjs/redux/components/ReduxModal.js +531 -1
  11. package/lib/commonjs/redux/components/index.js +52 -1
  12. package/lib/commonjs/redux/hooks/index.js +25 -1
  13. package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +197 -1
  14. package/lib/commonjs/redux/hooks/useReduxActions.js +75 -1
  15. package/lib/commonjs/redux/index.js +49 -1
  16. package/lib/commonjs/redux/sync/reduxSyncAdapter.js +26 -0
  17. package/lib/commonjs/redux/utils/autoInstrument.js +270 -1
  18. package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +166 -1
  19. package/lib/commonjs/redux/utils/createReduxHistoryAdapter.js +146 -1
  20. package/lib/commonjs/redux/utils/index.js +111 -1
  21. package/lib/commonjs/redux/utils/reduxActionStore.js +413 -1
  22. package/lib/module/index.js +92 -1
  23. package/lib/module/preset.js +94 -1
  24. package/lib/module/redux/components/ReduxActionButton.js +126 -1
  25. package/lib/module/redux/components/ReduxActionDetailContent.js +376 -1
  26. package/lib/module/redux/components/ReduxActionDetailView.js +397 -1
  27. package/lib/module/redux/components/ReduxActionInfoView.js +833 -1
  28. package/lib/module/redux/components/ReduxActionItem.js +362 -1
  29. package/lib/module/redux/components/ReduxDetailViewToggle.js +129 -1
  30. package/lib/module/redux/components/ReduxIcon.js +8 -1
  31. package/lib/module/redux/components/ReduxModal.js +526 -1
  32. package/lib/module/redux/components/index.js +7 -1
  33. package/lib/module/redux/hooks/index.js +4 -1
  34. package/lib/module/redux/hooks/useAutoInstrumentRedux.js +193 -1
  35. package/lib/module/redux/hooks/useReduxActions.js +71 -1
  36. package/lib/module/redux/index.js +13 -1
  37. package/lib/module/redux/sync/reduxSyncAdapter.js +23 -0
  38. package/lib/module/redux/utils/autoInstrument.js +260 -1
  39. package/lib/module/redux/utils/buoyReduxMiddleware.js +157 -1
  40. package/lib/module/redux/utils/createReduxHistoryAdapter.js +142 -1
  41. package/lib/module/redux/utils/index.js +8 -1
  42. package/lib/module/redux/utils/reduxActionStore.js +409 -1
  43. package/lib/typescript/index.d.ts +1 -0
  44. package/lib/typescript/index.d.ts.map +1 -0
  45. package/lib/typescript/preset.d.ts.map +1 -0
  46. package/lib/typescript/redux/components/ReduxActionButton.d.ts.map +1 -0
  47. package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +1 -0
  48. package/lib/typescript/redux/components/ReduxActionDetailView.d.ts.map +1 -0
  49. package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +1 -0
  50. package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +1 -0
  51. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +1 -0
  52. package/lib/typescript/redux/components/ReduxIcon.d.ts.map +1 -0
  53. package/lib/typescript/redux/components/ReduxModal.d.ts.map +1 -0
  54. package/lib/typescript/redux/components/index.d.ts.map +1 -0
  55. package/lib/typescript/redux/hooks/index.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/index.d.ts.map +1 -0
  59. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts +18 -0
  60. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +1 -0
  61. package/lib/typescript/redux/types/index.d.ts.map +1 -0
  62. package/lib/typescript/redux/utils/autoInstrument.d.ts.map +1 -0
  63. package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +1 -0
  64. package/lib/typescript/redux/utils/createReduxHistoryAdapter.d.ts.map +1 -0
  65. package/lib/typescript/redux/utils/index.d.ts.map +1 -0
  66. package/lib/typescript/redux/utils/reduxActionStore.d.ts +25 -0
  67. package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +1 -0
  68. package/package.json +4 -4
@@ -1 +1,413 @@
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 o=0;for(const s of n)i.includes(s)&&t[s]!==e[s]&&o++;const a=[];s>0&&a.push(`+${s}`),r>0&&a.push(`-${r}`),o>0&&a.push(`~${o}`);const c=s+r+o;return 0===a.length?{summary:"nested change",changedCount:1}:{summary:`${a.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}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.reduxActionStore=void 0;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,o=getActionCategory(s),{sliceName:a,actionName:c}=parseActionType(s),u=extractMeta(t),l=formatPayloadPreview(t.payload),{summary:d,changedCount:h}=getStateDiffSummary(e,n),f=(i??0)>16,g={id:`${Date.now()}-${++this.idCounter}`,type:s,payload:t.payload,action:t,timestamp:Date.now(),prevState:e,nextState:n,duration:i,hasStateChange:r,category:o,sliceName:a,actionName:c,meta:u,error:t.error,payloadPreview:l,diffSummary:d,changedKeysCount:h,isSlowAction:f};this.actions=[g,...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}}const reduxActionStore=exports.reduxActionStore=new ReduxActionStore;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.reduxActionStore = void 0;
7
+ /**
8
+ * Redux action store - captures and stores Redux Toolkit actions
9
+ */
10
+
11
+ // ============================================
12
+ // Helper Functions for Enhanced Data
13
+ // ============================================
14
+
15
+ /**
16
+ * Get action category from type string
17
+ */
18
+ function getActionCategory(type) {
19
+ if (type.startsWith("@@")) return "internal";
20
+ if (type.includes("/pending")) return "pending";
21
+ if (type.includes("/fulfilled")) return "fulfilled";
22
+ if (type.includes("/rejected")) return "rejected";
23
+ if (type.includes("/executeQuery")) return "query";
24
+ if (type.includes("/executeMutation")) return "mutation";
25
+ return "action";
26
+ }
27
+
28
+ /**
29
+ * Parse action type to extract slice and action names
30
+ */
31
+ function parseActionType(type) {
32
+ // Handle internal actions
33
+ if (type.startsWith("@@")) {
34
+ const parts = type.split("/");
35
+ return {
36
+ sliceName: null,
37
+ actionName: parts[parts.length - 1] || type
38
+ };
39
+ }
40
+
41
+ // Handle RTK pattern: "sliceName/actionName" or "sliceName/actionName/status"
42
+ const parts = type.split("/");
43
+ if (parts.length >= 2) {
44
+ const sliceName = parts[0];
45
+ // For async thunks, action name might be "fetchUser/pending" -> we want "fetchUser"
46
+ let actionName = parts[1];
47
+
48
+ // If it's an async status, use the base action name
49
+ if (parts.length >= 3 && ["pending", "fulfilled", "rejected"].includes(parts[parts.length - 1])) {
50
+ actionName = parts.slice(1, -1).join("/");
51
+ }
52
+ return {
53
+ sliceName,
54
+ actionName: actionName.toUpperCase()
55
+ };
56
+ }
57
+ return {
58
+ sliceName: null,
59
+ actionName: type.toUpperCase()
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Format payload preview for display
65
+ */
66
+ function formatPayloadPreview(payload, maxLength = 40) {
67
+ if (payload === undefined) return "";
68
+ if (payload === null) return "null";
69
+ try {
70
+ if (typeof payload === "string") {
71
+ return payload.length > maxLength ? `"${payload.slice(0, maxLength - 3)}..."` : `"${payload}"`;
72
+ }
73
+ if (typeof payload === "number" || typeof payload === "boolean") {
74
+ return String(payload);
75
+ }
76
+ if (Array.isArray(payload)) {
77
+ if (payload.length === 0) return "[]";
78
+ const preview = JSON.stringify(payload);
79
+ return preview.length > maxLength ? `[${payload.length} items]` : preview;
80
+ }
81
+ if (typeof payload === "object") {
82
+ const keys = Object.keys(payload);
83
+ if (keys.length === 0) return "{}";
84
+
85
+ // Try to create a meaningful preview
86
+ const preview = JSON.stringify(payload);
87
+ if (preview.length <= maxLength) return preview;
88
+
89
+ // Show key count
90
+ return `{ ${keys.length} keys }`;
91
+ }
92
+ return String(payload).slice(0, maxLength);
93
+ } catch {
94
+ return "[complex]";
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Calculate state diff summary
100
+ */
101
+ function getStateDiffSummary(prevState, nextState) {
102
+ if (prevState === nextState) {
103
+ return {
104
+ summary: "no change",
105
+ changedCount: 0
106
+ };
107
+ }
108
+
109
+ // Handle non-objects
110
+ if (typeof prevState !== "object" || typeof nextState !== "object" || prevState === null || nextState === null) {
111
+ return {
112
+ summary: "changed",
113
+ changedCount: 1
114
+ };
115
+ }
116
+ const prevKeys = Object.keys(prevState);
117
+ const nextKeys = Object.keys(nextState);
118
+ const added = nextKeys.filter(k => !prevKeys.includes(k)).length;
119
+ const removed = prevKeys.filter(k => !nextKeys.includes(k)).length;
120
+
121
+ // Check which existing keys changed
122
+ let changed = 0;
123
+ for (const key of prevKeys) {
124
+ if (nextKeys.includes(key) && prevState[key] !== nextState[key]) {
125
+ changed++;
126
+ }
127
+ }
128
+ const parts = [];
129
+ if (added > 0) parts.push(`+${added}`);
130
+ if (removed > 0) parts.push(`-${removed}`);
131
+ if (changed > 0) parts.push(`~${changed}`);
132
+ const totalChanged = added + removed + changed;
133
+ if (parts.length === 0) {
134
+ return {
135
+ summary: "nested change",
136
+ changedCount: 1
137
+ };
138
+ }
139
+ return {
140
+ summary: `${parts.join(" ")} ${totalChanged === 1 ? "key" : "keys"}`,
141
+ changedCount: totalChanged
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Extract RTK async thunk metadata
147
+ */
148
+ function extractMeta(action) {
149
+ const meta = action.meta;
150
+ if (!meta) return undefined;
151
+ return {
152
+ requestId: meta.requestId,
153
+ requestStatus: meta.requestStatus,
154
+ arg: meta.arg,
155
+ rejectedWithValue: meta.rejectedWithValue,
156
+ aborted: meta.aborted,
157
+ condition: meta.condition
158
+ };
159
+ }
160
+
161
+ // ============================================
162
+ // Redux Action Store
163
+ // ============================================
164
+
165
+ class ReduxActionStore {
166
+ actions = [];
167
+ listeners = new Set();
168
+ clearListeners = new Set();
169
+ maxActions = 200;
170
+ idCounter = 0;
171
+ isEnabled = true;
172
+ captureSuppressed = false;
173
+ addAction(action, prevState, nextState, duration) {
174
+ if (!this.isEnabled || this.captureSuppressed) {
175
+ return;
176
+ }
177
+ const actionType = action.type || "";
178
+
179
+ // Skip actions without a proper type (e.g., thunk functions)
180
+ if (!actionType) {
181
+ return;
182
+ }
183
+ const hasStateChange = prevState !== nextState;
184
+
185
+ // Enhanced data extraction
186
+ const category = getActionCategory(actionType);
187
+ const {
188
+ sliceName,
189
+ actionName
190
+ } = parseActionType(actionType);
191
+ const meta = extractMeta(action);
192
+ const payloadPreview = formatPayloadPreview(action.payload);
193
+ const {
194
+ summary: diffSummary,
195
+ changedCount: changedKeysCount
196
+ } = getStateDiffSummary(prevState, nextState);
197
+ const isSlowAction = (duration ?? 0) > 16; // Frame budget
198
+
199
+ const reduxAction = {
200
+ id: `${Date.now()}-${++this.idCounter}`,
201
+ type: actionType,
202
+ payload: action.payload,
203
+ action,
204
+ timestamp: Date.now(),
205
+ prevState,
206
+ nextState,
207
+ duration,
208
+ hasStateChange,
209
+ // Enhanced fields
210
+ category,
211
+ sliceName,
212
+ actionName,
213
+ meta,
214
+ error: action.error,
215
+ payloadPreview,
216
+ diffSummary,
217
+ changedKeysCount,
218
+ isSlowAction
219
+ };
220
+ this.actions = [reduxAction, ...this.actions].slice(0, this.maxActions);
221
+ this.notifyListeners();
222
+ }
223
+ getActions() {
224
+ return [...this.actions];
225
+ }
226
+ getActionById(id) {
227
+ return this.actions.find(a => a.id === id);
228
+ }
229
+ clearActions() {
230
+ this.actions = [];
231
+ this.notifyListeners();
232
+ this.clearListeners.forEach(listener => {
233
+ try {
234
+ listener();
235
+ } catch {
236
+ // Ignore listener errors
237
+ }
238
+ });
239
+ }
240
+
241
+ /**
242
+ * Listen for clearActions() calls. Used in remote mirror mode to forward a
243
+ * clear performed in the dashboard UI to the synced device.
244
+ */
245
+ onClear(listener) {
246
+ this.clearListeners.add(listener);
247
+ return () => {
248
+ this.clearListeners.delete(listener);
249
+ };
250
+ }
251
+
252
+ // ============================================
253
+ // Remote Mirror Mode
254
+ // ============================================
255
+
256
+ /**
257
+ * Permanently suppress local capture. Use when this store acts as a mirror
258
+ * of a remote device's actions (e.g. the desktop dashboard): actions arrive
259
+ * via replaceActions() and local instrumentation must never record.
260
+ */
261
+ disableCapture() {
262
+ this.captureSuppressed = true;
263
+ }
264
+
265
+ /**
266
+ * Whether the store is in remote mirror mode (capture suppressed). The UI
267
+ * uses this to skip local-instrumentation affordances like the
268
+ * "react-redux not installed" banner.
269
+ */
270
+ isCaptureSuppressed() {
271
+ return this.captureSuppressed;
272
+ }
273
+
274
+ /**
275
+ * Replace the entire action list and notify listeners. Used in remote
276
+ * mirror mode where full snapshots arrive from a synced device. Respects
277
+ * the enabled flag so the UI's pause button freezes the mirror.
278
+ */
279
+ replaceActions(actions) {
280
+ if (!this.isEnabled) {
281
+ return;
282
+ }
283
+ this.actions = actions.slice(0, this.maxActions);
284
+ this.notifyListeners();
285
+ }
286
+ setEnabled(enabled) {
287
+ this.isEnabled = enabled;
288
+ }
289
+ getEnabled() {
290
+ return this.isEnabled;
291
+ }
292
+ subscribe(listener) {
293
+ this.listeners.add(listener);
294
+ return () => {
295
+ this.listeners.delete(listener);
296
+ };
297
+ }
298
+ notifyListeners() {
299
+ const actions = this.getActions();
300
+ this.listeners.forEach(listener => listener(actions));
301
+ }
302
+ setMaxActions(max) {
303
+ this.maxActions = max;
304
+ if (this.actions.length > max) {
305
+ this.actions = this.actions.slice(0, max);
306
+ this.notifyListeners();
307
+ }
308
+ }
309
+ getStats() {
310
+ const total = this.actions.length;
311
+ const withChanges = this.actions.filter(a => a.hasStateChange).length;
312
+ const actionTypes = new Set(this.actions.map(a => a.type));
313
+
314
+ // Calculate average duration
315
+ const durations = this.actions.filter(a => a.duration !== undefined).map(a => a.duration);
316
+ const avgDuration = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0;
317
+ return {
318
+ totalActions: total,
319
+ actionsWithChanges: withChanges,
320
+ actionsWithoutChanges: total - withChanges,
321
+ uniqueActionTypes: actionTypes.size,
322
+ averageDuration: Math.round(avgDuration * 100) / 100
323
+ };
324
+ }
325
+ filterActions(filter) {
326
+ let filtered = [...this.actions];
327
+ if (filter.searchText) {
328
+ const search = filter.searchText.toLowerCase();
329
+ filtered = filtered.filter(a => a.type.toLowerCase().includes(search));
330
+ }
331
+ if (filter.actionTypes && filter.actionTypes.length > 0) {
332
+ filtered = filtered.filter(a => filter.actionTypes.includes(a.type));
333
+ }
334
+ if (filter.onlyWithChanges) {
335
+ filtered = filtered.filter(a => a.hasStateChange);
336
+ }
337
+ return filtered;
338
+ }
339
+ getUniqueActionTypes() {
340
+ return Array.from(new Set(this.actions.map(a => a.type))).sort();
341
+ }
342
+
343
+ /**
344
+ * Find related async actions by requestId
345
+ */
346
+ getRelatedActions(requestId) {
347
+ return this.actions.filter(a => a.meta?.requestId === requestId).sort((a, b) => a.timestamp - b.timestamp); // Sort chronologically
348
+ }
349
+
350
+ /**
351
+ * Get all actions that share a requestId with the given action
352
+ */
353
+ getLinkedActions(action) {
354
+ if (!action.meta?.requestId) return [];
355
+ return this.getRelatedActions(action.meta.requestId);
356
+ }
357
+
358
+ /**
359
+ * Get count of linked actions for a given action
360
+ */
361
+ getLinkedActionsCount(action) {
362
+ if (!action.meta?.requestId) return 0;
363
+ return this.actions.filter(a => a.meta?.requestId === action.meta?.requestId).length;
364
+ }
365
+
366
+ /**
367
+ * Calculate total duration for an async operation (pending to resolution)
368
+ */
369
+ getAsyncOperationDuration(requestId) {
370
+ const related = this.getRelatedActions(requestId);
371
+ if (related.length < 2) return null;
372
+ const pending = related.find(a => a.category === "pending");
373
+ const resolution = related.find(a => a.category === "fulfilled" || a.category === "rejected");
374
+ if (pending && resolution) {
375
+ return resolution.timestamp - pending.timestamp;
376
+ }
377
+ return null;
378
+ }
379
+
380
+ /**
381
+ * Get the base action type (without /pending, /fulfilled, /rejected suffix)
382
+ */
383
+ getBaseActionType(type) {
384
+ const parts = type.split("/");
385
+ if (parts.length >= 2) {
386
+ const lastPart = parts[parts.length - 1];
387
+ if (["pending", "fulfilled", "rejected"].includes(lastPart)) {
388
+ return parts.slice(0, -1).join("/");
389
+ }
390
+ }
391
+ return type;
392
+ }
393
+
394
+ /**
395
+ * Get sequential index for an async operation (1, 2, 3, etc.)
396
+ * Based on order of when pending actions first appeared
397
+ */
398
+ getAsyncOperationIndex(requestId) {
399
+ // Get all unique requestIds with their first timestamp
400
+ const requestIdTimestamps = new Map();
401
+ for (const action of this.actions) {
402
+ if (action.meta?.requestId && !requestIdTimestamps.has(action.meta.requestId)) {
403
+ requestIdTimestamps.set(action.meta.requestId, action.timestamp);
404
+ }
405
+ }
406
+
407
+ // Sort by timestamp (oldest first) and find index
408
+ const sortedIds = Array.from(requestIdTimestamps.entries()).sort((a, b) => a[1] - b[1]).map(([id]) => id);
409
+ const index = sortedIds.indexOf(requestId);
410
+ return index >= 0 ? index + 1 : 0;
411
+ }
412
+ }
413
+ const reduxActionStore = exports.reduxActionStore = new ReduxActionStore();
@@ -1 +1,92 @@
1
- "use strict";export{reduxToolPreset,createReduxTool}from"./preset";export{buoyReduxMiddleware,createBuoyReduxMiddleware,withBuoyDevTools,jumpToState,replayAction,BUOY_JUMP_TO_STATE,BUOY_REPLAY_ACTION}from"./redux/utils/buoyReduxMiddleware";export{instrumentStore,uninstrumentStore,isStoreInstrumented,isAutoInstrumentActive,getActiveStore}from"./redux/utils/autoInstrument";export{createReduxHistoryAdapter,reduxHistoryAdapter}from"./redux/utils/createReduxHistoryAdapter";export{useReduxActions}from"./redux/hooks/useReduxActions";export{useAutoInstrumentRedux,useReduxAvailability}from"./redux/hooks/useAutoInstrumentRedux";export{ReduxModal,FREE_TIER_ACTION_LIMIT}from"./redux/components/ReduxModal";export{ReduxActionItem}from"./redux/components/ReduxActionItem";export{ReduxActionDetailView}from"./redux/components/ReduxActionDetailView";export{ReduxActionDetailContent,ReduxActionDetailFooter}from"./redux/components/ReduxActionDetailContent";export{ReduxIcon}from"./redux/components/ReduxIcon";export{reduxActionStore}from"./redux/utils/reduxActionStore";
1
+ "use strict";
2
+
3
+ /**
4
+ * @buoy-gg/redux
5
+ *
6
+ * Redux Toolkit DevTools for React Native
7
+ *
8
+ * PUBLIC API - Only these exports are supported for external use.
9
+ * Internal stores are not exported to prevent bypassing the tool's
10
+ * intended usage patterns.
11
+ *
12
+ * ZERO-CONFIG: Just install this package - that's it!
13
+ * The Redux DevTools automatically instruments your store when
14
+ * FloatingDevTools mounts. No middleware, no configuration needed.
15
+ *
16
+ * @example Zero-config setup (just install and use!)
17
+ * ```tsx
18
+ * // 1. Install: npm install @buoy-gg/redux
19
+ *
20
+ * // 2. Your existing Redux setup (NO CHANGES NEEDED!)
21
+ * const store = configureStore({ reducer: rootReducer });
22
+ *
23
+ * // 3. Your app with FloatingDevTools (Redux tool auto-detected!)
24
+ * <Provider store={store}>
25
+ * <App />
26
+ * <FloatingDevTools /> // Redux tool appears automatically!
27
+ * </Provider>
28
+ * ```
29
+ *
30
+ * That's it! The Redux tool will auto-detect your store and capture actions.
31
+ *
32
+ * @example Advanced: Manual middleware (only if you need more control)
33
+ * ```tsx
34
+ * // For time-travel or custom options, you can optionally use middleware:
35
+ * import { buoyReduxMiddleware, withBuoyDevTools } from '@buoy-gg/redux';
36
+ *
37
+ * const store = configureStore({
38
+ * reducer: withBuoyDevTools(rootReducer), // Enables time-travel
39
+ * middleware: (getDefaultMiddleware) =>
40
+ * getDefaultMiddleware().concat(buoyReduxMiddleware),
41
+ * });
42
+ * ```
43
+ */
44
+
45
+ // =============================================================================
46
+ // PRESET (Primary entry point for users)
47
+ // =============================================================================
48
+ export { reduxToolPreset, createReduxTool } from "./preset";
49
+
50
+ // =============================================================================
51
+ // MIDDLEWARE (Optional - for manual setup if preferred over auto-instrumentation)
52
+ // =============================================================================
53
+ export { buoyReduxMiddleware, createBuoyReduxMiddleware, withBuoyDevTools, jumpToState, replayAction, BUOY_JUMP_TO_STATE, BUOY_REPLAY_ACTION } from "./redux/utils/buoyReduxMiddleware";
54
+
55
+ // =============================================================================
56
+ // AUTO-INSTRUMENTATION (For programmatic store instrumentation)
57
+ // =============================================================================
58
+ export { instrumentStore, uninstrumentStore, isStoreInstrumented, isAutoInstrumentActive, getActiveStore } from "./redux/utils/autoInstrument";
59
+ // =============================================================================
60
+ // HISTORY ADAPTER (For universal history devtools)
61
+ // =============================================================================
62
+ export { createReduxHistoryAdapter, reduxHistoryAdapter } from "./redux/utils/createReduxHistoryAdapter";
63
+
64
+ // =============================================================================
65
+ // EXTERNAL SYNC (Adapter for @buoy-gg/external-sync's useExternalSync)
66
+ // =============================================================================
67
+ export { reduxSyncAdapter } from "./redux/sync/reduxSyncAdapter";
68
+
69
+ // =============================================================================
70
+ // HOOKS (For consuming Redux action data)
71
+ // =============================================================================
72
+ export { useReduxActions } from "./redux/hooks/useReduxActions";
73
+ // Auto-instrumentation hooks (for advanced use cases)
74
+ export { useAutoInstrumentRedux, useReduxAvailability } from "./redux/hooks/useAutoInstrumentRedux";
75
+ // =============================================================================
76
+ // COMPONENTS (For custom UI implementations)
77
+ // =============================================================================
78
+ export { ReduxModal, FREE_TIER_ACTION_LIMIT } from "./redux/components/ReduxModal";
79
+ export { ReduxActionItem } from "./redux/components/ReduxActionItem";
80
+ export { ReduxActionDetailView } from "./redux/components/ReduxActionDetailView";
81
+ export { ReduxActionDetailContent, ReduxActionDetailFooter } from "./redux/components/ReduxActionDetailContent";
82
+ export { ReduxIcon } from "./redux/components/ReduxIcon";
83
+
84
+ // =============================================================================
85
+ // TYPES
86
+ // =============================================================================
87
+
88
+ // =============================================================================
89
+ // INTERNAL EXPORTS (For @buoy-gg/* packages only - not part of public API)
90
+ // =============================================================================
91
+ /** @internal */
92
+ export { reduxActionStore } from "./redux/utils/reduxActionStore";
@@ -1 +1,94 @@
1
- "use strict";import{ReduxModal}from"./redux/components/ReduxModal";import{ReduxIcon}from"./redux/components/ReduxIcon";import{jsx as _jsx}from"react/jsx-runtime";export const reduxToolPreset={id:"redux",name:"REDUX",description:"Redux action & state inspector",slot:"both",icon:({size:e})=>_jsx(ReduxIcon,{size:e}),component:ReduxModal,props:{enableSharedModalDimensions:!1}};export function createReduxTool(e){return{id:e?.id||"redux",name:e?.name||"REDUX",description:e?.description||"Redux action & state inspector",slot:"both",icon:({size:o})=>_jsx(ReduxIcon,{size:o,color:e?.iconColor}),component:ReduxModal,props:{enableSharedModalDimensions:void 0!==e?.enableSharedModalDimensions&&e.enableSharedModalDimensions}}}
1
+ "use strict";
2
+
3
+ /**
4
+ * Pre-configured Redux DevTools preset for FloatingDevTools
5
+ *
6
+ * ZERO-CONFIG: This preset is auto-discovered by FloatingDevTools!
7
+ * You do NOT need to import or configure anything - just install @buoy-gg/redux
8
+ * and the Redux tool will appear automatically in your FloatingDevTools.
9
+ *
10
+ * @example Automatic (recommended - no code needed!)
11
+ * ```tsx
12
+ * // Just install: npm install @buoy-gg/redux
13
+ * // The Redux tool appears automatically in FloatingDevTools!
14
+ *
15
+ * <Provider store={store}>
16
+ * <App />
17
+ * <FloatingDevTools /> // Redux tool is auto-detected
18
+ * </Provider>
19
+ * ```
20
+ *
21
+ * @example Manual (only for custom configuration)
22
+ * ```tsx
23
+ * import { createReduxTool } from '@buoy-gg/redux';
24
+ *
25
+ * // Only use this if you need to customize the tool
26
+ * const customReduxTool = createReduxTool({
27
+ * name: "STATE",
28
+ * iconColor: "#9945FF",
29
+ * });
30
+ *
31
+ * <FloatingDevTools apps={[customReduxTool]} />
32
+ * ```
33
+ */
34
+
35
+ import { ReduxModal } from "./redux/components/ReduxModal";
36
+ import { ReduxIcon } from "./redux/components/ReduxIcon";
37
+
38
+ /**
39
+ * Pre-configured Redux DevTools preset for FloatingDevTools.
40
+ * Includes:
41
+ * - Live action monitoring
42
+ * - Action/state inspection
43
+ * - State diff visualization
44
+ * - Filter by action type
45
+ */
46
+ import { jsx as _jsx } from "react/jsx-runtime";
47
+ export const reduxToolPreset = {
48
+ id: "redux",
49
+ name: "REDUX",
50
+ description: "Redux action & state inspector",
51
+ slot: "both",
52
+ icon: ({
53
+ size
54
+ }) => /*#__PURE__*/_jsx(ReduxIcon, {
55
+ size: size
56
+ }),
57
+ component: ReduxModal,
58
+ props: {
59
+ enableSharedModalDimensions: false
60
+ }
61
+ };
62
+
63
+ /**
64
+ * Create a custom Redux DevTools configuration.
65
+ * Use this if you want to override default settings.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * import { createReduxTool } from '@buoy-gg/redux';
70
+ *
71
+ * const myReduxTool = createReduxTool({
72
+ * name: "STATE",
73
+ * iconColor: "#9945FF", // Purple color
74
+ * });
75
+ * ```
76
+ */
77
+ export function createReduxTool(options) {
78
+ return {
79
+ id: options?.id || "redux",
80
+ name: options?.name || "REDUX",
81
+ description: options?.description || "Redux action & state inspector",
82
+ slot: "both",
83
+ icon: ({
84
+ size
85
+ }) => /*#__PURE__*/_jsx(ReduxIcon, {
86
+ size: size,
87
+ color: options?.iconColor
88
+ }),
89
+ component: ReduxModal,
90
+ props: {
91
+ enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
92
+ }
93
+ };
94
+ }