@buoy-gg/react-query 1.7.8 → 2.1.1

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 (38) hide show
  1. package/lib/commonjs/index.js +619 -24
  2. package/lib/commonjs/preset.js +1 -1
  3. package/lib/commonjs/react-query/components/ReactQueryDevToolsModal.js +12 -0
  4. package/lib/commonjs/react-query/components/modals/MutationBrowserModal.js +2 -1
  5. package/lib/commonjs/react-query/components/modals/QueryBrowserModal.js +8 -14
  6. package/lib/commonjs/react-query/hooks/useAllMutations.js +65 -15
  7. package/lib/commonjs/react-query/hooks/useWifiState.js +3 -3
  8. package/lib/commonjs/react-query/index.js +11 -0
  9. package/lib/commonjs/react-query/stores/index.js +48 -0
  10. package/lib/commonjs/react-query/stores/reactQueryEventStore.js +311 -0
  11. package/lib/commonjs/react-query/utils/modalStorageOperations.js +2 -2
  12. package/lib/module/index.js +113 -8
  13. package/lib/module/preset.js +2 -2
  14. package/lib/module/react-query/components/ReactQueryDevToolsModal.js +13 -1
  15. package/lib/module/react-query/components/modals/MutationBrowserModal.js +2 -1
  16. package/lib/module/react-query/components/modals/QueryBrowserModal.js +9 -15
  17. package/lib/module/react-query/hooks/useAllMutations.js +66 -16
  18. package/lib/module/react-query/hooks/useWifiState.js +4 -4
  19. package/lib/module/react-query/index.js +2 -1
  20. package/lib/module/react-query/stores/index.js +3 -0
  21. package/lib/module/react-query/stores/reactQueryEventStore.js +302 -0
  22. package/lib/module/react-query/utils/modalStorageOperations.js +3 -3
  23. package/lib/typescript/index.d.ts +61 -5
  24. package/lib/typescript/index.d.ts.map +1 -1
  25. package/lib/typescript/react-query/components/ReactQueryDevToolsModal.d.ts.map +1 -1
  26. package/lib/typescript/react-query/components/modals/MutationBrowserModal.d.ts.map +1 -1
  27. package/lib/typescript/react-query/components/modals/QueryBrowserModal.d.ts.map +1 -1
  28. package/lib/typescript/react-query/hooks/useAllMutations.d.ts +2 -2
  29. package/lib/typescript/react-query/hooks/useAllMutations.d.ts.map +1 -1
  30. package/lib/typescript/react-query/hooks/useWifiState.d.ts +1 -1
  31. package/lib/typescript/react-query/hooks/useWifiState.d.ts.map +1 -1
  32. package/lib/typescript/react-query/index.d.ts +1 -0
  33. package/lib/typescript/react-query/index.d.ts.map +1 -1
  34. package/lib/typescript/react-query/stores/index.d.ts +2 -0
  35. package/lib/typescript/react-query/stores/index.d.ts.map +1 -0
  36. package/lib/typescript/react-query/stores/reactQueryEventStore.d.ts +99 -0
  37. package/lib/typescript/react-query/stores/reactQueryEventStore.d.ts.map +1 -0
  38. package/package.json +17 -3
@@ -1,5 +1,15 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * @buoy-gg/react-query
5
+ *
6
+ * React Query DevTools for React Native.
7
+ *
8
+ * PUBLIC API - Only these exports are supported for external use.
9
+ * Internal stores and event subscription mechanisms are not exported
10
+ * to prevent bypassing the tool's intended usage patterns.
11
+ */
12
+
3
13
  // Check if @tanstack/react-query is installed
4
14
  try {
5
15
  require("@tanstack/react-query");
@@ -7,13 +17,108 @@ try {
7
17
  throw new Error("\n\n[@buoy-gg/react-query] ERROR: Missing required peer dependency\n\n" + "This package requires @tanstack/react-query to be installed.\n\n" + "Install it with:\n" + " npm install @tanstack/react-query\n" + " or\n" + " pnpm add @tanstack/react-query\n" + " or\n" + " yarn add @tanstack/react-query\n\n" + "For more information, visit: https://tanstack.com/query/latest\n");
8
18
  }
9
19
 
10
- // Export preset configuration (easiest way to add to FloatingDevTools!)
20
+ // =============================================================================
21
+ // PRESET (Primary entry point for users)
22
+ // =============================================================================
11
23
  export { reactQueryToolPreset, createReactQueryTool, wifiTogglePreset, createWifiToggleTool } from "./preset";
12
24
 
13
- // React Query dev tools entry point
14
- // Re-export the full dev tools surface so consumers can tree-shake as needed
15
- export * from "./react-query";
16
- export * from "./react-query/components";
17
- export * from "./react-query/hooks";
18
- export * from "./react-query/utils";
19
- export * from "./react-query/types";
25
+ // =============================================================================
26
+ // QUERY CLIENT CONNECTION (Required for users to connect their QueryClient)
27
+ // =============================================================================
28
+ export { setQueryClient, disconnectQueryClient } from "./react-query/stores/reactQueryEventStore";
29
+
30
+ // =============================================================================
31
+ // COMPONENTS (For custom UI implementations)
32
+ // =============================================================================
33
+
34
+ // Main modals
35
+ export { ReactQueryModal } from "./react-query/components/modals/ReactQueryModal";
36
+ export { ReactQueryModalHeader } from "./react-query/components/modals/ReactQueryModalHeader";
37
+ export { QueryBrowserModal } from "./react-query/components/modals/QueryBrowserModal";
38
+ export { MutationBrowserModal } from "./react-query/components/modals/MutationBrowserModal";
39
+ export { MutationEditorModal } from "./react-query/components/modals/MutationEditorModal";
40
+ export { DataEditorModal } from "./react-query/components/modals/DataEditorModal";
41
+ export { QueryBrowserFooter } from "./react-query/components/modals/QueryBrowserFooter";
42
+ export { MutationBrowserFooter } from "./react-query/components/modals/MutationBrowserFooter";
43
+ export { SwipeIndicator } from "./react-query/components/modals/SwipeIndicator";
44
+
45
+ // Query browser components
46
+ export { Explorer, QueryBrowser, QueryDetails, QueryInformation, QueryActions, QueryRow, QueryStatus, QueryStatusCount, QueryDetailsChip, MutationsList, MutationDetails, MutationInformation, MutationButton, MutationStatusCount, MutationDetailsChips, ActionButton, ClearCacheButton, NetworkToggleButton, StorageStatusCount } from "./react-query/components/query-browser";
47
+
48
+ // Mode components
49
+ export { QueryBrowserMode } from "./react-query/components/QueryBrowserMode";
50
+ export { MutationBrowserMode } from "./react-query/components/MutationBrowserMode";
51
+ export { MutationEditorMode } from "./react-query/components/MutationEditorMode";
52
+ export { DataEditorMode } from "./react-query/components/DataEditorMode";
53
+ export { QuerySelector } from "./react-query/components/QuerySelector";
54
+ export { QueryDebugInfo } from "./react-query/components/QueryDebugInfo";
55
+ export { WifiToggle } from "./react-query/components/WifiToggle";
56
+ export { ReactQuerySection } from "./react-query/components/ReactQuerySection";
57
+ export { ReactQueryDevToolsModal } from "./react-query/components/ReactQueryDevToolsModal";
58
+
59
+ // Shared data viewer components
60
+ export { VirtualizedDataExplorer, DataViewer, TypeLegend } from "@buoy-gg/shared-ui/dataViewer";
61
+
62
+ // =============================================================================
63
+ // HOOKS (For consuming React Query data)
64
+ // =============================================================================
65
+ export { default as useAllQueries } from "./react-query/hooks/useAllQueries";
66
+ export { default as useAllMutations } from "./react-query/hooks/useAllMutations";
67
+ export { useGetQueryByQueryKey, useGetQueryByQueryKeyWithVersion } from "./react-query/hooks/useSelectedQuery";
68
+ export { useGetMutationById } from "./react-query/hooks/useSelectedMutation";
69
+ export { default as useQueryStatusCounts } from "./react-query/hooks/useQueryStatusCounts";
70
+ export { useStorageQueryCounts } from "./react-query/hooks/useStorageQueryCounts";
71
+ export { useReactQueryState } from "./react-query/hooks/useReactQueryState";
72
+ export { useActionButtons } from "./react-query/hooks/useActionButtons";
73
+ export { useMutationActionButtons } from "./react-query/hooks/useMutationActionButtons";
74
+ export { useModalManager } from "./react-query/hooks/useModalManager";
75
+ export { useModalPersistence } from "./react-query/hooks/useModalPersistence";
76
+ export { useWifiState } from "./react-query/hooks/useWifiState";
77
+
78
+ // =============================================================================
79
+ // UTILITIES (Public helpers only)
80
+ // =============================================================================
81
+
82
+ // Action utilities
83
+ export { default as invalidate } from "./react-query/utils/actions/invalidate";
84
+ export { default as refetch } from "./react-query/utils/actions/refetch";
85
+ export { default as reset } from "./react-query/utils/actions/reset";
86
+ export { default as remove } from "./react-query/utils/actions/remove";
87
+ export { default as deleteItem } from "./react-query/utils/actions/deleteItem";
88
+ export { default as triggerError } from "./react-query/utils/actions/triggerError";
89
+ export { default as triggerLoading } from "./react-query/utils/actions/triggerLoading";
90
+
91
+ // Query status utilities
92
+ export * from "./react-query/utils/getQueryStatusLabel";
93
+ export * from "./react-query/utils/getQueryStatusColor";
94
+
95
+ // Storage utilities
96
+ export * from "./react-query/utils/getStorageQueryCounts";
97
+ export * from "./react-query/utils/storageQueryUtils";
98
+ export * from "./react-query/utils/modalStorageOperations";
99
+
100
+ // Data manipulation utilities
101
+ export * from "./react-query/utils/updateNestedDataByPath";
102
+ export * from "./react-query/utils/deleteNestedDataByPath";
103
+ export { safeStringify, displayValue, parseDisplayValue } from "@buoy-gg/shared-ui";
104
+
105
+ // =============================================================================
106
+ // TYPES
107
+ // =============================================================================
108
+
109
+ export { isPlainObject } from "./react-query/types/types";
110
+
111
+ // =============================================================================
112
+ // INTERNAL EXPORTS (For @buoy-gg/* packages only - not part of public API)
113
+ // =============================================================================
114
+ /** @internal */
115
+ export { reactQueryEventStore } from "./react-query/stores/reactQueryEventStore";
116
+
117
+ // =============================================================================
118
+ // NOT EXPORTED (Internal controls)
119
+ // =============================================================================
120
+ // The following are intentionally NOT exported to prevent bypassing:
121
+ // - getReactQueryEvents (internal store access)
122
+ // - subscribeToReactQueryEvents (internal subscription)
123
+ // - clearReactQueryEvents (internal store control)
124
+ // - isReactQueryConnected (internal state query)
@@ -21,7 +21,7 @@ import { QueryIcon } from "@buoy-gg/floating-tools-core";
21
21
  import { Wifi } from "@buoy-gg/shared-ui";
22
22
  import { ReactQueryDevToolsModal } from "./react-query/components/ReactQueryDevToolsModal";
23
23
  import { onlineManager } from "@tanstack/react-query";
24
- import { devToolsStorageKeys, safeSetItem } from "@buoy-gg/shared-ui";
24
+ import { devToolsStorageKeys, persistentStorage } from "@buoy-gg/shared-ui";
25
25
 
26
26
  // Empty component for toggle-only mode
27
27
  const EmptyComponent = () => null;
@@ -29,7 +29,7 @@ const EmptyComponent = () => null;
29
29
  // Save WiFi state to storage
30
30
  const saveWifiState = async enabled => {
31
31
  try {
32
- await safeSetItem(devToolsStorageKeys.settings.wifiEnabled(), enabled.toString());
32
+ await persistentStorage.setItem(devToolsStorageKeys.settings.wifiEnabled(), enabled.toString());
33
33
  } catch (error) {
34
34
  // Failed to save WiFi state
35
35
  }
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
 
3
- import { useCallback, useState } from "react";
3
+ import { useCallback, useState, useEffect } from "react";
4
+ import { useQueryClient } from "@tanstack/react-query";
4
5
  import { ReactQueryModal } from "./modals/ReactQueryModal";
6
+ import { reactQueryEventStore } from "../stores/reactQueryEventStore";
5
7
 
6
8
  /** Configuration options for the high-level React Query dev tools modal wrapper. */
7
9
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -15,6 +17,16 @@ export function ReactQueryDevToolsModal({
15
17
  onMinimize,
16
18
  enableSharedModalDimensions = true
17
19
  }) {
20
+ // Get QueryClient and connect to event store for unified Events DevTools
21
+ const queryClient = useQueryClient();
22
+ useEffect(() => {
23
+ // Connect the QueryClient to the event store so events flow to unified Events DevTools
24
+ // Only connect if not already connected (to preserve connection across modal open/close)
25
+ if (!reactQueryEventStore.isConnected()) {
26
+ reactQueryEventStore.setQueryClient(queryClient);
27
+ }
28
+ // Don't disconnect on unmount - keep the connection for unified Events DevTools
29
+ }, [queryClient]);
18
30
  const [selectedQueryKey, setSelectedQueryKey] = useState(undefined);
19
31
  const [selectedMutationId, setSelectedMutationId] = useState(undefined);
20
32
  const [activeFilter, setActiveFilter] = useState(null);
@@ -37,7 +37,8 @@ export function MutationBrowserModal({
37
37
  // Clear mutation cache handler
38
38
  const handleClearCache = useCallback(() => {
39
39
  queryClient.getMutationCache().clear();
40
- }, [queryClient]);
40
+ onMutationSelect(undefined);
41
+ }, [queryClient, onMutationSelect]);
41
42
 
42
43
  // Track modal mode for conditional styling
43
44
  // Initialize with bottomSheet but it will be updated from persisted state if available
@@ -9,7 +9,7 @@ import { QueryBrowserFooter } from "./QueryBrowserFooter";
9
9
  import { QueryFilterViewV3 } from "../QueryFilterViewV3";
10
10
  import { useState, useCallback, useEffect } from "react";
11
11
  import { View, StyleSheet } from "react-native";
12
- import { devToolsStorageKeys, useSafeAsyncStorage, buoyColors } from "@buoy-gg/shared-ui";
12
+ import { devToolsStorageKeys, persistentStorage, buoyColors } from "@buoy-gg/shared-ui";
13
13
  import useAllQueries from "../../hooks/useAllQueries";
14
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  /**
@@ -48,17 +48,11 @@ export function QueryBrowserModal({
48
48
  const [ignoredPatterns, setIgnoredPatterns] = useState(new Set());
49
49
  const [includedPatterns, setIncludedPatterns] = useState(new Set());
50
50
 
51
- // AsyncStorage for persisting ignored patterns
52
- const {
53
- getItem: safeGetItem,
54
- setItem: safeSetItem
55
- } = useSafeAsyncStorage();
56
-
57
51
  // Load ignored patterns from storage on mount
58
52
  useEffect(() => {
59
53
  const loadFilters = async () => {
60
54
  try {
61
- const stored = await safeGetItem(devToolsStorageKeys.reactQuery.ignoredPatterns());
55
+ const stored = await persistentStorage.getItem(devToolsStorageKeys.reactQuery.ignoredPatterns());
62
56
  if (stored) {
63
57
  const patterns = JSON.parse(stored);
64
58
  if (Array.isArray(patterns)) {
@@ -70,13 +64,13 @@ export function QueryBrowserModal({
70
64
  }
71
65
  };
72
66
  loadFilters();
73
- }, [safeGetItem]);
67
+ }, []);
74
68
 
75
69
  // Load included patterns from storage on mount
76
70
  useEffect(() => {
77
71
  const loadFilters = async () => {
78
72
  try {
79
- const stored = await safeGetItem(devToolsStorageKeys.reactQuery.includedPatterns());
73
+ const stored = await persistentStorage.getItem(devToolsStorageKeys.reactQuery.includedPatterns());
80
74
  if (stored) {
81
75
  const patterns = JSON.parse(stored);
82
76
  if (Array.isArray(patterns)) {
@@ -88,33 +82,33 @@ export function QueryBrowserModal({
88
82
  }
89
83
  };
90
84
  loadFilters();
91
- }, [safeGetItem]);
85
+ }, []);
92
86
 
93
87
  // Save ignored patterns to storage when they change
94
88
  useEffect(() => {
95
89
  const saveFilters = async () => {
96
90
  try {
97
91
  const patterns = Array.from(ignoredPatterns);
98
- await safeSetItem(devToolsStorageKeys.reactQuery.ignoredPatterns(), JSON.stringify(patterns));
92
+ await persistentStorage.setItem(devToolsStorageKeys.reactQuery.ignoredPatterns(), JSON.stringify(patterns));
99
93
  } catch (error) {
100
94
  console.error("Failed to save ignored patterns:", error);
101
95
  }
102
96
  };
103
97
  saveFilters();
104
- }, [ignoredPatterns, safeSetItem]);
98
+ }, [ignoredPatterns]);
105
99
 
106
100
  // Save included patterns to storage when they change
107
101
  useEffect(() => {
108
102
  const saveFilters = async () => {
109
103
  try {
110
104
  const patterns = Array.from(includedPatterns);
111
- await safeSetItem(devToolsStorageKeys.reactQuery.includedPatterns(), JSON.stringify(patterns));
105
+ await persistentStorage.setItem(devToolsStorageKeys.reactQuery.includedPatterns(), JSON.stringify(patterns));
112
106
  } catch (error) {
113
107
  console.error("Failed to save included patterns:", error);
114
108
  }
115
109
  };
116
110
  saveFilters();
117
- }, [includedPatterns, safeSetItem]);
111
+ }, [includedPatterns]);
118
112
 
119
113
  // Toggle pattern in ignored set
120
114
  const handlePatternToggle = useCallback(pattern => {
@@ -1,30 +1,80 @@
1
1
  "use strict";
2
2
 
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { useQueryClient } from "@tanstack/react-query";
5
5
 
6
6
  /**
7
- * Tracks all active React Query mutations with lightweight change detection. Debounces cache
8
- * updates so large mutation batches do not thrash the UI thread on mobile.
7
+ * Tracks all active React Query mutations with lightweight change detection.
8
+ * Mirrors the pattern used in useAllQueries for consistency.
9
9
  */
10
10
  function useAllMutations() {
11
11
  const queryClient = useQueryClient();
12
- const [mutations, setMutations] = useState([]);
13
- const mutationsSnapshotRef = useRef(null);
12
+ const [mutations, setMutations] = useState(() => {
13
+ // Initialize with current mutations to avoid flash
14
+ return queryClient.getMutationCache().getAll();
15
+ });
16
+
17
+ // Track mutation states using a Map for O(1) lookups
18
+ const mutationStatesRef = useRef(new Map());
19
+ const updateTimerRef = useRef(undefined);
20
+
21
+ // Check if mutations have changed
22
+ const hasMutationsChanged = useCallback(newMutations => {
23
+ const statesMap = mutationStatesRef.current;
24
+
25
+ // Quick length check first
26
+ if (newMutations.length !== statesMap.size) {
27
+ return true;
28
+ }
29
+
30
+ // Check if any mutation state has changed
31
+ for (const mutation of newMutations) {
32
+ const prevState = statesMap.get(mutation.mutationId);
33
+ if (!prevState) return true;
34
+ if (prevState.status !== mutation.state.status || prevState.submittedAt !== mutation.state.submittedAt || prevState.isPaused !== mutation.state.isPaused) {
35
+ return true;
36
+ }
37
+ }
38
+ return false;
39
+ }, []);
40
+
41
+ // Update function
42
+ const updateMutations = useCallback(() => {
43
+ const allMutations = queryClient.getMutationCache().getAll();
44
+ if (hasMutationsChanged(allMutations)) {
45
+ // Update states map
46
+ const newStatesMap = new Map();
47
+ allMutations.forEach(m => {
48
+ newStatesMap.set(m.mutationId, m.state);
49
+ });
50
+ mutationStatesRef.current = newStatesMap;
51
+ setMutations(allMutations);
52
+ }
53
+ }, [queryClient, hasMutationsChanged]);
14
54
  useEffect(() => {
15
- const updateMutations = () => {
16
- const newMutations = queryClient.getMutationCache().getAll();
17
- const newStates = newMutations.map(m => m.state);
18
- const snapshot = JSON.stringify(newStates);
19
- if (mutationsSnapshotRef.current !== snapshot) {
20
- mutationsSnapshotRef.current = snapshot;
21
- setTimeout(() => setMutations(newMutations), 0);
55
+ // Initial update
56
+ updateMutations();
57
+
58
+ // Subscribe with event filtering (matching useAllQueries pattern)
59
+ const unsubscribe = queryClient.getMutationCache().subscribe(event => {
60
+ // Process events that affect mutation list
61
+ if (event.type === "added" || event.type === "removed" || event.type === "updated") {
62
+ // Debounce updates to batch rapid changes
63
+ if (updateTimerRef.current) {
64
+ clearTimeout(updateTimerRef.current);
65
+ }
66
+ updateTimerRef.current = setTimeout(() => {
67
+ updateMutations();
68
+ }, 10);
69
+ }
70
+ });
71
+ return () => {
72
+ unsubscribe();
73
+ if (updateTimerRef.current) {
74
+ clearTimeout(updateTimerRef.current);
22
75
  }
23
76
  };
24
- setTimeout(updateMutations, 0);
25
- const unsubscribe = queryClient.getMutationCache().subscribe(updateMutations);
26
- return () => unsubscribe();
27
- }, [queryClient]);
77
+ }, [queryClient, updateMutations]);
28
78
  return {
29
79
  mutations
30
80
  };
@@ -2,10 +2,10 @@
2
2
 
3
3
  import { useEffect, useState, useRef } from "react";
4
4
  import { onlineManager } from "@tanstack/react-query";
5
- import { devToolsStorageKeys, safeGetItem, safeSetItem } from "@buoy-gg/shared-ui";
5
+ import { devToolsStorageKeys, persistentStorage } from "@buoy-gg/shared-ui";
6
6
 
7
7
  /**
8
- * Synchronizes a local Wi-Fi toggle with React Querys `onlineManager`, persisting the selection
8
+ * Synchronizes a local Wi-Fi toggle with React Query's `onlineManager`, persisting the selection
9
9
  * so developers can simulate offline mode across reloads.
10
10
  */
11
11
  export function useWifiState() {
@@ -17,7 +17,7 @@ export function useWifiState() {
17
17
  if (hasLoadedPersistedState.current) return;
18
18
  const loadPersistedState = async () => {
19
19
  try {
20
- const savedState = await safeGetItem(devToolsStorageKeys.settings.wifiEnabled());
20
+ const savedState = await persistentStorage.getItem(devToolsStorageKeys.settings.wifiEnabled());
21
21
  if (savedState !== null) {
22
22
  const isEnabled = savedState === "true";
23
23
  setIsOnline(isEnabled);
@@ -34,7 +34,7 @@ export function useWifiState() {
34
34
  // Save WiFi state when it changes
35
35
  const saveWifiState = async enabled => {
36
36
  try {
37
- await safeSetItem(devToolsStorageKeys.settings.wifiEnabled(), enabled.toString());
37
+ await persistentStorage.setItem(devToolsStorageKeys.settings.wifiEnabled(), enabled.toString());
38
38
  } catch (error) {
39
39
  // Failed to save WiFi state
40
40
  }
@@ -1,3 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export * from "./ReactQueryDevTools";
3
+ export * from "./ReactQueryDevTools";
4
+ export * from "./stores";
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export { reactQueryEventStore, setQueryClient, disconnectQueryClient, getReactQueryEvents, subscribeToReactQueryEvents, clearReactQueryEvents, isReactQueryConnected } from "./reactQueryEventStore";