@buoy-gg/zustand 2.1.8
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/LICENSE +58 -0
- package/README.md +138 -0
- package/lib/commonjs/index.js +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +1 -0
- package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -0
- package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +1 -0
- package/lib/commonjs/zustand/components/ZustandEventFilterView.js +1 -0
- package/lib/commonjs/zustand/components/ZustandIcon.js +1 -0
- package/lib/commonjs/zustand/components/ZustandModal.js +1 -0
- package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +1 -0
- package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +1 -0
- package/lib/commonjs/zustand/components/ZustandStateInfoView.js +1 -0
- package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +1 -0
- package/lib/commonjs/zustand/components/index.js +1 -0
- package/lib/commonjs/zustand/hooks/index.js +1 -0
- package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +1 -0
- package/lib/commonjs/zustand/index.js +1 -0
- package/lib/commonjs/zustand/types/index.js +1 -0
- package/lib/commonjs/zustand/utils/buoyZustandMiddleware.js +1 -0
- package/lib/commonjs/zustand/utils/index.js +1 -0
- package/lib/commonjs/zustand/utils/zustandStateStore.js +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/preset.js +1 -0
- package/lib/module/zustand/components/ZustandActionButton.js +1 -0
- package/lib/module/zustand/components/ZustandDetailViewToggle.js +1 -0
- package/lib/module/zustand/components/ZustandEventFilterView.js +1 -0
- package/lib/module/zustand/components/ZustandIcon.js +1 -0
- package/lib/module/zustand/components/ZustandModal.js +1 -0
- package/lib/module/zustand/components/ZustandStateChangeItem.js +1 -0
- package/lib/module/zustand/components/ZustandStateDetailContent.js +1 -0
- package/lib/module/zustand/components/ZustandStateInfoView.js +1 -0
- package/lib/module/zustand/components/ZustandStoreBrowser.js +1 -0
- package/lib/module/zustand/components/index.js +1 -0
- package/lib/module/zustand/hooks/index.js +1 -0
- package/lib/module/zustand/hooks/useZustandStateChanges.js +1 -0
- package/lib/module/zustand/index.js +1 -0
- package/lib/module/zustand/types/index.js +1 -0
- package/lib/module/zustand/utils/buoyZustandMiddleware.js +1 -0
- package/lib/module/zustand/utils/index.js +1 -0
- package/lib/module/zustand/utils/zustandStateStore.js +1 -0
- package/lib/typescript/index.d.ts +50 -0
- package/lib/typescript/preset.d.ts +96 -0
- package/lib/typescript/zustand/components/ZustandActionButton.d.ts +42 -0
- package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts +16 -0
- package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts +10 -0
- package/lib/typescript/zustand/components/ZustandIcon.d.ts +10 -0
- package/lib/typescript/zustand/components/ZustandModal.d.ts +10 -0
- package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts +13 -0
- package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts +30 -0
- package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts +18 -0
- package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts +18 -0
- package/lib/typescript/zustand/components/index.d.ts +11 -0
- package/lib/typescript/zustand/hooks/index.d.ts +3 -0
- package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts +38 -0
- package/lib/typescript/zustand/index.d.ts +12 -0
- package/lib/typescript/zustand/types/index.d.ts +72 -0
- package/lib/typescript/zustand/utils/buoyZustandMiddleware.d.ts +84 -0
- package/lib/typescript/zustand/utils/index.d.ts +3 -0
- package/lib/typescript/zustand/utils/zustandStateStore.d.ts +55 -0
- package/package.json +81 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const STORE_COLORS={counter:"#10B981",auth:"#8B5CF6",user:"#3B82F6",cart:"#EC4899",app:"#6366F1",ui:"#F59E0B",settings:"#14B8A6",theme:"#06B6D4",navigation:"#F97316",form:"#EF4444"};function getStoreColor(e){const t=e.toLowerCase();if(STORE_COLORS[t])return STORE_COLORS[t];for(const[e,s]of Object.entries(STORE_COLORS))if(t.includes(e))return s;const s=137*e.split("").reduce((e,t)=>e+t.charCodeAt(0),0)%360,n=.7*(1-Math.abs(1.2-1)),r=n*(1-Math.abs(s/60%2-1)),a=.6-n/2;let i=0,o=0,h=0;s<60?(i=n,o=r):s<120?(i=r,o=n):s<180?(o=n,h=r):s<240?(o=r,h=n):s<300?(i=r,h=n):(i=n,h=r);const g=e=>Math.round(255*(e+a)).toString(16).padStart(2,"0");return`#${g(i)}${g(o)}${g(h)}`}function formatPartialPreview(e,t=40){if(void 0===e)return"";if(null===e)return"null";try{if("function"==typeof e)return"(updater fn)";if("string"==typeof e)return e.length>t?`"${e.slice(0,t-3)}..."`:`"${e}"`;if("number"==typeof e||"boolean"==typeof e)return String(e);if(Array.isArray(e)){if(0===e.length)return"[]";const s=JSON.stringify(e);return s.length>t?`[${e.length} items]`:s}if("object"==typeof e){const s=Object.keys(e);if(0===s.length)return"{}";const n=JSON.stringify(e);return n.length<=t?n:`{ ${s.length} keys }`}return String(e).slice(0,t)}catch{return"[complex]"}}function getStateDiffSummary(e,t){if(e===t)return{summary:"no change",changedKeys:[],changedCount:0};if("object"!=typeof e||"object"!=typeof t||null===e||null===t)return{summary:"changed",changedKeys:[],changedCount:1};const s=Object.keys(e),n=Object.keys(t),r=n.filter(e=>!s.includes(e)),a=s.filter(e=>!n.includes(e)),i=[];for(const r of s)n.includes(r)&&e[r]!==t[r]&&i.push(r);const o=[...r,...a,...i],h=[];r.length>0&&h.push(`+${r.length}`),a.length>0&&h.push(`-${a.length}`),i.length>0&&h.push(`~${i.length}`);const g=o.length;return 0===h.length?{summary:"nested change",changedKeys:[],changedCount:1}:{summary:`${h.join(" ")} ${1===g?"key":"keys"}`,changedKeys:o,changedCount:g}}class ZustandStateStore{stateChanges=[];stores=new Map;listeners=new Set;storeListeners=new Set;maxChanges=200;idCounter=0;isEnabled=!0;addStateChange(e){if(!this.isEnabled)return;const{storeName:t,partial:s,replace:n,prevState:r,nextState:a,duration:i,category:o,isPersisted:h=!1}=e,g=r!==a,{summary:u,changedKeys:l,changedCount:c}=getStateDiffSummary(r,a),d=formatPartialPreview(s),f=(i??0)>16,C=o??(n?"replace":"setState"),S={id:`${Date.now()}-${++this.idCounter}`,storeName:t,timestamp:Date.now(),prevState:r,nextState:a,partial:s,duration:i,hasStateChange:g,replace:n,category:C,changedKeys:l,changedKeysCount:c,diffSummary:u,partialPreview:d,isSlowUpdate:f,isPersisted:h};this.stateChanges=[S,...this.stateChanges].slice(0,this.maxChanges);const y=this.stores.get(t);y&&y.stateChangeCount++,this.notifyListeners()}getStateChanges(){return[...this.stateChanges]}getStateChangeById(e){return this.stateChanges.find(t=>t.id===e)}clearStateChanges(){this.stateChanges=[];for(const e of this.stores.values())e.stateChangeCount=0;this.notifyListeners()}registerStore(e,t,s){if(this.stores.has(e))return;const n={name:e,api:t,stateChangeCount:0,isPersisted:s?.isPersisted??!1,persistName:s?.persistName,color:getStoreColor(e)};this.stores.set(e,n),this.notifyStoreListeners()}unregisterStore(e){this.stores.delete(e),this.notifyStoreListeners()}getStores(){return Array.from(this.stores.values())}getStore(e){return this.stores.get(e)}getStoreColor(e){return this.stores.get(e)?.color??getStoreColor(e)}filterStateChanges(e){let t=[...this.stateChanges];if(e.searchText){const s=e.searchText.toLowerCase();t=t.filter(e=>e.storeName.toLowerCase().includes(s)||e.partialPreview.toLowerCase().includes(s)||e.changedKeys.some(e=>e.toLowerCase().includes(s)))}return e.storeNames&&e.storeNames.length>0&&(t=t.filter(t=>e.storeNames.includes(t.storeName))),e.onlyWithChanges&&(t=t.filter(e=>e.hasStateChange)),t}getStats(){const e=this.stateChanges.length,t=this.stateChanges.filter(e=>e.hasStateChange).length,s=this.stores.size,n=this.stateChanges.filter(e=>void 0!==e.duration).map(e=>e.duration),r=n.length>0?n.reduce((e,t)=>e+t,0)/n.length:0;return{totalChanges:e,changesWithStateChange:t,changesWithoutStateChange:e-t,storeCount:s,averageDuration:Math.round(100*r)/100}}getUniqueStoreNames(){return Array.from(this.stores.keys()).sort()}setEnabled(e){this.isEnabled=e}getEnabled(){return this.isEnabled}setMaxChanges(e){this.maxChanges=e,this.stateChanges.length>e&&(this.stateChanges=this.stateChanges.slice(0,e),this.notifyListeners())}subscribe(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}subscribeToStores(e){return this.storeListeners.add(e),()=>{this.storeListeners.delete(e)}}notifyListeners(){const e=this.getStateChanges();this.listeners.forEach(t=>t(e))}notifyStoreListeners(){const e=this.getStores();this.storeListeners.forEach(t=>t(e))}}export const zustandStateStore=new ZustandStateStore;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @buoy-gg/zustand
|
|
3
|
+
*
|
|
4
|
+
* Zustand Store DevTools for React Native
|
|
5
|
+
*
|
|
6
|
+
* PUBLIC API - Only these exports are supported for external use.
|
|
7
|
+
*
|
|
8
|
+
* @example Recommended setup (one line, zero store modifications)
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { watchStores } from '@buoy-gg/zustand';
|
|
11
|
+
* import { useCounterStore } from './stores/counter';
|
|
12
|
+
* import { useAuthStore } from './stores/auth';
|
|
13
|
+
*
|
|
14
|
+
* // One call, anywhere at module scope or in your root layout:
|
|
15
|
+
* watchStores({
|
|
16
|
+
* counterStore: useCounterStore,
|
|
17
|
+
* authStore: useAuthStore,
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Advanced: middleware for extra detail (partial + timing)
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import { create } from 'zustand';
|
|
24
|
+
* import { buoyDevTools } from '@buoy-gg/zustand';
|
|
25
|
+
*
|
|
26
|
+
* const useCounterStore = create(
|
|
27
|
+
* buoyDevTools(
|
|
28
|
+
* (set) => ({
|
|
29
|
+
* count: 0,
|
|
30
|
+
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
31
|
+
* }),
|
|
32
|
+
* { name: 'counterStore' }
|
|
33
|
+
* )
|
|
34
|
+
* );
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export { zustandToolPreset, createZustandTool } from "./preset";
|
|
38
|
+
export { watchStores } from "./zustand/utils/buoyZustandMiddleware";
|
|
39
|
+
export { buoyDevTools } from "./zustand/utils/buoyZustandMiddleware";
|
|
40
|
+
export { isStoreInstrumented } from "./zustand/utils/buoyZustandMiddleware";
|
|
41
|
+
export { useZustandStateChanges } from "./zustand/hooks/useZustandStateChanges";
|
|
42
|
+
export type { UseZustandStateChangesResult } from "./zustand/hooks/useZustandStateChanges";
|
|
43
|
+
export { ZustandModal } from "./zustand/components/ZustandModal";
|
|
44
|
+
export { ZustandStateChangeItem } from "./zustand/components/ZustandStateChangeItem";
|
|
45
|
+
export { ZustandStateDetailContent, ZustandStateDetailFooter, } from "./zustand/components/ZustandStateDetailContent";
|
|
46
|
+
export { ZustandIcon, ZUSTAND_ICON_COLOR } from "./zustand/components/ZustandIcon";
|
|
47
|
+
export type { ZustandModalProps, ZustandStateChange, ZustandStoreInfo, ZustandFilter, StateChangeCategory, BuoyZustandMiddlewareOptions, } from "./zustand/types";
|
|
48
|
+
/** @internal */
|
|
49
|
+
export { zustandStateStore } from "./zustand/utils/zustandStateStore";
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-configured Zustand DevTools preset for FloatingDevTools
|
|
3
|
+
*
|
|
4
|
+
* ZERO-CONFIG: This preset is auto-discovered by FloatingDevTools!
|
|
5
|
+
* Just install @buoy-gg/zustand and wrap your stores with buoyDevTools().
|
|
6
|
+
*
|
|
7
|
+
* @example Automatic (recommended)
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { create } from 'zustand';
|
|
10
|
+
* import { buoyDevTools } from '@buoy-gg/zustand';
|
|
11
|
+
*
|
|
12
|
+
* const useCounterStore = create(
|
|
13
|
+
* buoyDevTools(
|
|
14
|
+
* (set) => ({ count: 0, increment: () => set((s) => ({ count: s.count + 1 })) }),
|
|
15
|
+
* { name: 'counterStore' }
|
|
16
|
+
* )
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* // The Zustand tool appears automatically in FloatingDevTools!
|
|
20
|
+
* <FloatingDevTools />
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Manual (only for custom configuration)
|
|
24
|
+
* ```tsx
|
|
25
|
+
* import { createZustandTool } from '@buoy-gg/zustand';
|
|
26
|
+
*
|
|
27
|
+
* const customZustandTool = createZustandTool({
|
|
28
|
+
* name: "STATE",
|
|
29
|
+
* iconColor: "#463B3F",
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* <FloatingDevTools apps={[customZustandTool]} />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
import { ZustandModal } from "./zustand/components/ZustandModal";
|
|
36
|
+
/**
|
|
37
|
+
* Pre-configured Zustand DevTools preset for FloatingDevTools.
|
|
38
|
+
* Includes:
|
|
39
|
+
* - Live state change monitoring
|
|
40
|
+
* - State inspection (JSON viewer)
|
|
41
|
+
* - State diff visualization (tree + split)
|
|
42
|
+
* - Filter by store name
|
|
43
|
+
* - Changed keys tracking
|
|
44
|
+
*/
|
|
45
|
+
export declare const zustandToolPreset: {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
slot: "both";
|
|
50
|
+
icon: ({ size }: {
|
|
51
|
+
size: number;
|
|
52
|
+
}) => import("react").JSX.Element;
|
|
53
|
+
component: typeof ZustandModal;
|
|
54
|
+
props: {
|
|
55
|
+
enableSharedModalDimensions: boolean;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Create a custom Zustand DevTools configuration.
|
|
60
|
+
* Use this if you want to override default settings.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* import { createZustandTool } from '@buoy-gg/zustand';
|
|
65
|
+
*
|
|
66
|
+
* const myZustandTool = createZustandTool({
|
|
67
|
+
* name: "STATE",
|
|
68
|
+
* iconColor: "#463B3F",
|
|
69
|
+
* });
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function createZustandTool(options?: {
|
|
73
|
+
/** Tool name (default: "ZUSTAND") */
|
|
74
|
+
name?: string;
|
|
75
|
+
/** Tool description */
|
|
76
|
+
description?: string;
|
|
77
|
+
/** Custom tool ID (default: "zustand") */
|
|
78
|
+
id?: string;
|
|
79
|
+
/** Icon color */
|
|
80
|
+
iconColor?: string;
|
|
81
|
+
/** Enable shared modal dimensions */
|
|
82
|
+
enableSharedModalDimensions?: boolean;
|
|
83
|
+
}): {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
description: string;
|
|
87
|
+
slot: "both";
|
|
88
|
+
icon: ({ size }: {
|
|
89
|
+
size: number;
|
|
90
|
+
}) => import("react").JSX.Element;
|
|
91
|
+
component: typeof ZustandModal;
|
|
92
|
+
props: {
|
|
93
|
+
enableSharedModalDimensions: boolean;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=preset.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZustandActionButton
|
|
3
|
+
*
|
|
4
|
+
* Generic action button for Zustand DevTools footer.
|
|
5
|
+
* Mirrors ReduxActionButton.tsx
|
|
6
|
+
*/
|
|
7
|
+
declare const buttonConfigs: {
|
|
8
|
+
copy: {
|
|
9
|
+
color: "#20C997";
|
|
10
|
+
backgroundColor: string;
|
|
11
|
+
borderColor: string;
|
|
12
|
+
textColor: "#20C997";
|
|
13
|
+
};
|
|
14
|
+
reset: {
|
|
15
|
+
color: "#FFA94D";
|
|
16
|
+
backgroundColor: string;
|
|
17
|
+
borderColor: string;
|
|
18
|
+
textColor: "#FFA94D";
|
|
19
|
+
};
|
|
20
|
+
clear: {
|
|
21
|
+
color: "#EF4444";
|
|
22
|
+
backgroundColor: string;
|
|
23
|
+
borderColor: string;
|
|
24
|
+
textColor: "#EF4444";
|
|
25
|
+
};
|
|
26
|
+
jump: {
|
|
27
|
+
color: "#FFA94D";
|
|
28
|
+
backgroundColor: string;
|
|
29
|
+
borderColor: string;
|
|
30
|
+
textColor: "#FFA94D";
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
export type ButtonType = keyof typeof buttonConfigs;
|
|
34
|
+
interface ZustandActionButtonProps {
|
|
35
|
+
onPress: () => void;
|
|
36
|
+
text: string;
|
|
37
|
+
type: ButtonType;
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function ZustandActionButton({ onPress, text, type, disabled, }: ZustandActionButtonProps): import("react").JSX.Element;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=ZustandActionButton.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZustandDetailViewToggle
|
|
3
|
+
*
|
|
4
|
+
* Three-card toggle for switching between Change, State, and Diff views.
|
|
5
|
+
* Mirrors ReduxDetailViewToggle.tsx
|
|
6
|
+
*/
|
|
7
|
+
import React from "react";
|
|
8
|
+
export type ZustandDetailViewMode = "change" | "state" | "diff";
|
|
9
|
+
interface ZustandDetailViewToggleProps {
|
|
10
|
+
activeView: ZustandDetailViewMode;
|
|
11
|
+
onViewChange: (view: ZustandDetailViewMode) => void;
|
|
12
|
+
diffDisabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare const ZustandDetailViewToggle: React.NamedExoticComponent<ZustandDetailViewToggleProps>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=ZustandDetailViewToggle.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ZustandStoreInfo } from "../types";
|
|
2
|
+
interface ZustandEventFilterViewProps {
|
|
3
|
+
ignoredPatterns: Set<string>;
|
|
4
|
+
onTogglePattern: (pattern: string) => void;
|
|
5
|
+
onAddPattern: (pattern: string) => void;
|
|
6
|
+
stores: ZustandStoreInfo[];
|
|
7
|
+
}
|
|
8
|
+
export declare function ZustandEventFilterView({ ignoredPatterns, onTogglePattern, onAddPattern, stores, }: ZustandEventFilterViewProps): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=ZustandEventFilterView.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand DevTools icon
|
|
3
|
+
*/
|
|
4
|
+
declare const ZUSTAND_ICON_COLOR = "#463B3F";
|
|
5
|
+
export declare function ZustandIcon({ size, color, }: {
|
|
6
|
+
size: number;
|
|
7
|
+
color?: string;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export { ZUSTAND_ICON_COLOR };
|
|
10
|
+
//# sourceMappingURL=ZustandIcon.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Zustand DevTools modal
|
|
3
|
+
*
|
|
4
|
+
* Two tabs mirroring the Storage DevTools pattern:
|
|
5
|
+
* - Stores tab: Browse all registered stores and their current state
|
|
6
|
+
* - Events tab: Live state change monitoring with diffs
|
|
7
|
+
*/
|
|
8
|
+
import type { ZustandModalProps } from "../types";
|
|
9
|
+
export declare function ZustandModal({ visible, onClose, onBack, onMinimize, enableSharedModalDimensions, }: ZustandModalProps): import("react").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=ZustandModal.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact list item for displaying a Zustand state change
|
|
3
|
+
*
|
|
4
|
+
* Mirrors ReduxActionItem.tsx - uses shared CompactRow for consistent styling
|
|
5
|
+
*/
|
|
6
|
+
import type { ZustandStateChange } from "../types";
|
|
7
|
+
interface ZustandStateChangeItemProps {
|
|
8
|
+
change: ZustandStateChange;
|
|
9
|
+
onPress: (change: ZustandStateChange) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function ZustandStateChangeItem({ change, onPress, }: ZustandStateChangeItemProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=ZustandStateChangeItem.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZustandStateDetailContent
|
|
3
|
+
*
|
|
4
|
+
* Detail view for a Zustand state change with 3 view modes:
|
|
5
|
+
* - CHANGE: View change details, partial, metadata
|
|
6
|
+
* - STATE: View current state after change
|
|
7
|
+
* - DIFF: Compare prev vs next state
|
|
8
|
+
*
|
|
9
|
+
* Mirrors ReduxActionDetailContent.tsx
|
|
10
|
+
*/
|
|
11
|
+
import type { ZustandStateChange } from "../types";
|
|
12
|
+
interface ZustandStateDetailContentProps {
|
|
13
|
+
change: ZustandStateChange;
|
|
14
|
+
changes: ZustandStateChange[];
|
|
15
|
+
selectedIndex: number;
|
|
16
|
+
onIndexChange: (index: number) => void;
|
|
17
|
+
disableInternalFooter?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function ZustandStateDetailContent({ change, changes, selectedIndex, onIndexChange, disableInternalFooter, }: ZustandStateDetailContentProps): import("react").JSX.Element;
|
|
20
|
+
/**
|
|
21
|
+
* External footer component for modal use
|
|
22
|
+
*/
|
|
23
|
+
export declare function ZustandStateDetailFooter({ change, changes, selectedIndex, onIndexChange, }: {
|
|
24
|
+
change: ZustandStateChange;
|
|
25
|
+
changes: ZustandStateChange[];
|
|
26
|
+
selectedIndex: number;
|
|
27
|
+
onIndexChange: (index: number) => void;
|
|
28
|
+
}): import("react").JSX.Element | null;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=ZustandStateDetailContent.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZustandStateInfoView
|
|
3
|
+
*
|
|
4
|
+
* Displays comprehensive state change information including:
|
|
5
|
+
* - Change metadata (store, category, duration)
|
|
6
|
+
* - Partial data passed to setState
|
|
7
|
+
* - Changed keys list
|
|
8
|
+
*
|
|
9
|
+
* Mirrors ReduxActionInfoView.tsx
|
|
10
|
+
*/
|
|
11
|
+
import React from "react";
|
|
12
|
+
import type { ZustandStateChange } from "../types";
|
|
13
|
+
interface ZustandStateInfoViewProps {
|
|
14
|
+
change: ZustandStateChange;
|
|
15
|
+
}
|
|
16
|
+
export declare const ZustandStateInfoView: React.NamedExoticComponent<ZustandStateInfoViewProps>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ZustandStateInfoView.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZustandStoreBrowser
|
|
3
|
+
*
|
|
4
|
+
* Browse tab — shows all registered Zustand stores and their current state.
|
|
5
|
+
* Mirrors the Storage Browser pattern from @buoy-gg/storage.
|
|
6
|
+
*
|
|
7
|
+
* Each store is shown as an expandable row with its current state viewable
|
|
8
|
+
* via the shared DataViewer component.
|
|
9
|
+
*/
|
|
10
|
+
import type { ZustandStoreInfo } from "../types";
|
|
11
|
+
interface ZustandStoreBrowserProps {
|
|
12
|
+
stores: ZustandStoreInfo[];
|
|
13
|
+
searchQuery: string;
|
|
14
|
+
onViewHistory: (storeName: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function ZustandStoreBrowser({ stores, searchQuery, onViewHistory, }: ZustandStoreBrowserProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=ZustandStoreBrowser.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { ZustandModal } from "./ZustandModal";
|
|
2
|
+
export { ZustandIcon, ZUSTAND_ICON_COLOR } from "./ZustandIcon";
|
|
3
|
+
export { ZustandStateChangeItem } from "./ZustandStateChangeItem";
|
|
4
|
+
export { ZustandStateDetailContent, ZustandStateDetailFooter, } from "./ZustandStateDetailContent";
|
|
5
|
+
export { ZustandStateInfoView } from "./ZustandStateInfoView";
|
|
6
|
+
export { ZustandDetailViewToggle } from "./ZustandDetailViewToggle";
|
|
7
|
+
export type { ZustandDetailViewMode } from "./ZustandDetailViewToggle";
|
|
8
|
+
export { ZustandActionButton } from "./ZustandActionButton";
|
|
9
|
+
export type { ButtonType } from "./ZustandActionButton";
|
|
10
|
+
export { ZustandStoreBrowser } from "./ZustandStoreBrowser";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for consuming Zustand state changes from the store
|
|
3
|
+
*
|
|
4
|
+
* Mirrors useReduxActions.ts from @buoy-gg/redux
|
|
5
|
+
*/
|
|
6
|
+
import type { ZustandStateChange, ZustandStoreInfo, ZustandFilter } from "../types";
|
|
7
|
+
export interface UseZustandStateChangesResult {
|
|
8
|
+
/** All captured state changes */
|
|
9
|
+
stateChanges: ZustandStateChange[];
|
|
10
|
+
/** Filtered state changes based on current filter */
|
|
11
|
+
filteredChanges: ZustandStateChange[];
|
|
12
|
+
/** Current filter settings */
|
|
13
|
+
filter: ZustandFilter;
|
|
14
|
+
/** Update filter settings */
|
|
15
|
+
setFilter: React.Dispatch<React.SetStateAction<ZustandFilter>>;
|
|
16
|
+
/** Statistics about captured changes */
|
|
17
|
+
stats: {
|
|
18
|
+
totalChanges: number;
|
|
19
|
+
changesWithStateChange: number;
|
|
20
|
+
changesWithoutStateChange: number;
|
|
21
|
+
storeCount: number;
|
|
22
|
+
averageDuration: number;
|
|
23
|
+
};
|
|
24
|
+
/** All registered stores */
|
|
25
|
+
stores: ZustandStoreInfo[];
|
|
26
|
+
/** Clear all captured state changes */
|
|
27
|
+
clearChanges: () => void;
|
|
28
|
+
/** Whether capture is enabled */
|
|
29
|
+
isEnabled: boolean;
|
|
30
|
+
/** Toggle capture on/off */
|
|
31
|
+
toggleCapture: () => void;
|
|
32
|
+
/** Get unique store names */
|
|
33
|
+
storeNames: string[];
|
|
34
|
+
/** Get state change by ID */
|
|
35
|
+
getChangeById: (id: string) => ZustandStateChange | undefined;
|
|
36
|
+
}
|
|
37
|
+
export declare function useZustandStateChanges(): UseZustandStateChangesResult;
|
|
38
|
+
//# sourceMappingURL=useZustandStateChanges.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { ZustandModal } from "./components/ZustandModal";
|
|
2
|
+
export { ZustandIcon, ZUSTAND_ICON_COLOR } from "./components/ZustandIcon";
|
|
3
|
+
export { ZustandStateChangeItem } from "./components/ZustandStateChangeItem";
|
|
4
|
+
export { ZustandStateDetailContent, ZustandStateDetailFooter, } from "./components/ZustandStateDetailContent";
|
|
5
|
+
export { ZustandStateInfoView } from "./components/ZustandStateInfoView";
|
|
6
|
+
export { ZustandDetailViewToggle } from "./components/ZustandDetailViewToggle";
|
|
7
|
+
export { ZustandActionButton } from "./components/ZustandActionButton";
|
|
8
|
+
export { zustandStateStore } from "./utils/zustandStateStore";
|
|
9
|
+
export { watchStores, buoyDevTools, isStoreInstrumented, } from "./utils/buoyZustandMiddleware";
|
|
10
|
+
export { useZustandStateChanges } from "./hooks/useZustandStateChanges";
|
|
11
|
+
export type { ZustandModalProps, ZustandStateChange, ZustandStoreInfo, ZustandFilter, StateChangeCategory, BuoyZustandMiddlewareOptions, } from "./types";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand DevTools types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* State change category
|
|
6
|
+
*/
|
|
7
|
+
export type StateChangeCategory = "setState" | "replace" | "persist" | "initial";
|
|
8
|
+
/**
|
|
9
|
+
* Captured Zustand state change
|
|
10
|
+
*/
|
|
11
|
+
export interface ZustandStateChange {
|
|
12
|
+
id: string;
|
|
13
|
+
storeName: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
prevState: unknown;
|
|
16
|
+
nextState: unknown;
|
|
17
|
+
partial: unknown;
|
|
18
|
+
duration?: number;
|
|
19
|
+
hasStateChange: boolean;
|
|
20
|
+
replace: boolean;
|
|
21
|
+
category: StateChangeCategory;
|
|
22
|
+
changedKeys: string[];
|
|
23
|
+
changedKeysCount: number;
|
|
24
|
+
diffSummary: string;
|
|
25
|
+
partialPreview: string;
|
|
26
|
+
isSlowUpdate: boolean;
|
|
27
|
+
isPersisted: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Tracked Zustand store metadata
|
|
31
|
+
*/
|
|
32
|
+
export interface ZustandStoreInfo {
|
|
33
|
+
name: string;
|
|
34
|
+
api: {
|
|
35
|
+
getState: () => unknown;
|
|
36
|
+
getInitialState?: () => unknown;
|
|
37
|
+
setState: (partial: unknown, replace?: boolean) => void;
|
|
38
|
+
subscribe: (listener: (state: unknown, prevState: unknown) => void) => () => void;
|
|
39
|
+
};
|
|
40
|
+
stateChangeCount: number;
|
|
41
|
+
isPersisted: boolean;
|
|
42
|
+
persistName?: string;
|
|
43
|
+
color: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Filter options
|
|
47
|
+
*/
|
|
48
|
+
export interface ZustandFilter {
|
|
49
|
+
searchText?: string;
|
|
50
|
+
storeNames?: string[];
|
|
51
|
+
onlyWithChanges?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Modal props for ZustandModal
|
|
55
|
+
*/
|
|
56
|
+
export interface ZustandModalProps {
|
|
57
|
+
visible: boolean;
|
|
58
|
+
onClose: () => void;
|
|
59
|
+
onBack?: () => void;
|
|
60
|
+
onMinimize?: (modalState: unknown) => void;
|
|
61
|
+
enableSharedModalDimensions?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Middleware options
|
|
65
|
+
*/
|
|
66
|
+
export interface BuoyZustandMiddlewareOptions {
|
|
67
|
+
/** Store name for display in DevTools */
|
|
68
|
+
name?: string;
|
|
69
|
+
/** Enable capture on creation (default: true) */
|
|
70
|
+
enabled?: boolean;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buoy Zustand DevTools — Store instrumentation
|
|
3
|
+
*
|
|
4
|
+
* Two approaches, from easiest to most detailed:
|
|
5
|
+
*
|
|
6
|
+
* 1. watchStores() — RECOMMENDED. One line, zero store modifications.
|
|
7
|
+
* Uses store.subscribe() externally. Never touches setState.
|
|
8
|
+
* Safe to add/remove — cannot break your stores even if our code has bugs.
|
|
9
|
+
*
|
|
10
|
+
* 2. buoyDevTools() — Opt-in middleware for advanced use.
|
|
11
|
+
* Wraps setState to capture the partial argument and timing.
|
|
12
|
+
* Requires modifying each store's create() call.
|
|
13
|
+
*/
|
|
14
|
+
import type { BuoyZustandMiddlewareOptions } from "../types";
|
|
15
|
+
/** Minimal store shape — what Zustand's create() returns */
|
|
16
|
+
interface ZustandStoreHook {
|
|
17
|
+
getState: () => unknown;
|
|
18
|
+
getInitialState?: () => unknown;
|
|
19
|
+
setState: (partial: unknown, replace?: boolean) => void;
|
|
20
|
+
subscribe: (listener: (state: unknown, prevState: unknown) => void) => () => void;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Watch multiple Zustand stores for state changes.
|
|
25
|
+
*
|
|
26
|
+
* This is the recommended, non-intrusive approach. It uses each store's
|
|
27
|
+
* `.subscribe()` method to observe changes from the outside — it never
|
|
28
|
+
* modifies `setState` or any store internals.
|
|
29
|
+
*
|
|
30
|
+
* **Safe by design:** Even if our listener code has a bug, it cannot break
|
|
31
|
+
* your stores. All listener code is wrapped in try/catch.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* // In your _layout.tsx or App.tsx — ONE line:
|
|
36
|
+
* import { watchStores } from '@buoy-gg/zustand';
|
|
37
|
+
* import { useCounterStore } from './stores/counter';
|
|
38
|
+
* import { useAuthStore } from './stores/auth';
|
|
39
|
+
* import { useCartStore } from './stores/cart';
|
|
40
|
+
*
|
|
41
|
+
* watchStores({
|
|
42
|
+
* counterStore: useCounterStore,
|
|
43
|
+
* authStore: useAuthStore,
|
|
44
|
+
* cartStore: useCartStore,
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @param stores - Object mapping store names to Zustand store hooks
|
|
49
|
+
* @returns Cleanup function that removes all subscriptions
|
|
50
|
+
*/
|
|
51
|
+
export declare function watchStores(stores: Record<string, ZustandStoreHook>): () => void;
|
|
52
|
+
/**
|
|
53
|
+
* Zustand middleware that instruments a store for Buoy DevTools.
|
|
54
|
+
*
|
|
55
|
+
* This is the advanced approach — it wraps setState to capture:
|
|
56
|
+
* - The partial argument passed to setState
|
|
57
|
+
* - Precise timing of each setState call
|
|
58
|
+
*
|
|
59
|
+
* Use this when you want maximum detail. For most cases, `watchStores()`
|
|
60
|
+
* is simpler and safer.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* import { create } from 'zustand';
|
|
65
|
+
* import { buoyDevTools } from '@buoy-gg/zustand';
|
|
66
|
+
*
|
|
67
|
+
* const useCounterStore = create(
|
|
68
|
+
* buoyDevTools(
|
|
69
|
+
* (set) => ({
|
|
70
|
+
* count: 0,
|
|
71
|
+
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
72
|
+
* }),
|
|
73
|
+
* { name: 'counterStore' }
|
|
74
|
+
* )
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function buoyDevTools<T>(config: (set: (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: boolean) => void, get: () => T, api: Record<string, unknown>) => T, options?: BuoyZustandMiddlewareOptions): (set: (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: boolean) => void, get: () => T, api: Record<string, unknown>) => T;
|
|
79
|
+
/**
|
|
80
|
+
* Check if a store is being watched/instrumented
|
|
81
|
+
*/
|
|
82
|
+
export declare function isStoreInstrumented(store: Record<string | symbol, unknown>): boolean;
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=buoyZustandMiddleware.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand state store - captures and stores Zustand state changes
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the architecture of reduxActionStore.ts from @buoy-gg/redux
|
|
5
|
+
*/
|
|
6
|
+
import type { ZustandStateChange, ZustandStoreInfo, ZustandFilter, StateChangeCategory } from "../types";
|
|
7
|
+
declare class ZustandStateStore {
|
|
8
|
+
private stateChanges;
|
|
9
|
+
private stores;
|
|
10
|
+
private listeners;
|
|
11
|
+
private storeListeners;
|
|
12
|
+
private maxChanges;
|
|
13
|
+
private idCounter;
|
|
14
|
+
private isEnabled;
|
|
15
|
+
addStateChange(params: {
|
|
16
|
+
storeName: string;
|
|
17
|
+
partial: unknown;
|
|
18
|
+
replace: boolean;
|
|
19
|
+
prevState: unknown;
|
|
20
|
+
nextState: unknown;
|
|
21
|
+
duration?: number;
|
|
22
|
+
category?: StateChangeCategory;
|
|
23
|
+
isPersisted?: boolean;
|
|
24
|
+
}): void;
|
|
25
|
+
getStateChanges(): ZustandStateChange[];
|
|
26
|
+
getStateChangeById(id: string): ZustandStateChange | undefined;
|
|
27
|
+
clearStateChanges(): void;
|
|
28
|
+
registerStore(name: string, api: ZustandStoreInfo["api"], options?: {
|
|
29
|
+
isPersisted?: boolean;
|
|
30
|
+
persistName?: string;
|
|
31
|
+
}): void;
|
|
32
|
+
unregisterStore(name: string): void;
|
|
33
|
+
getStores(): ZustandStoreInfo[];
|
|
34
|
+
getStore(name: string): ZustandStoreInfo | undefined;
|
|
35
|
+
getStoreColor(name: string): string;
|
|
36
|
+
filterStateChanges(filter: ZustandFilter): ZustandStateChange[];
|
|
37
|
+
getStats(): {
|
|
38
|
+
totalChanges: number;
|
|
39
|
+
changesWithStateChange: number;
|
|
40
|
+
changesWithoutStateChange: number;
|
|
41
|
+
storeCount: number;
|
|
42
|
+
averageDuration: number;
|
|
43
|
+
};
|
|
44
|
+
getUniqueStoreNames(): string[];
|
|
45
|
+
setEnabled(enabled: boolean): void;
|
|
46
|
+
getEnabled(): boolean;
|
|
47
|
+
setMaxChanges(max: number): void;
|
|
48
|
+
subscribe(listener: (changes: ZustandStateChange[]) => void): () => void;
|
|
49
|
+
subscribeToStores(listener: (stores: ZustandStoreInfo[]) => void): () => void;
|
|
50
|
+
private notifyListeners;
|
|
51
|
+
private notifyStoreListeners;
|
|
52
|
+
}
|
|
53
|
+
export declare const zustandStateStore: ZustandStateStore;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=zustandStateStore.d.ts.map
|