@buoy-gg/zustand 2.1.11 → 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.
Files changed (57) hide show
  1. package/LICENSE +58 -0
  2. package/lib/commonjs/index.js +1 -91
  3. package/lib/commonjs/preset.js +1 -102
  4. package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -116
  5. package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +1 -134
  6. package/lib/commonjs/zustand/components/ZustandEventFilterView.js +1 -291
  7. package/lib/commonjs/zustand/components/ZustandIcon.js +1 -35
  8. package/lib/commonjs/zustand/components/ZustandModal.js +1 -603
  9. package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +1 -165
  10. package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +1 -352
  11. package/lib/commonjs/zustand/components/ZustandStateInfoView.js +1 -508
  12. package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +1 -307
  13. package/lib/commonjs/zustand/components/index.js +1 -73
  14. package/lib/commonjs/zustand/hooks/index.js +1 -12
  15. package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +1 -92
  16. package/lib/commonjs/zustand/index.js +1 -99
  17. package/lib/commonjs/zustand/utils/buoyZustandMiddleware.js +1 -220
  18. package/lib/commonjs/zustand/utils/index.js +1 -31
  19. package/lib/commonjs/zustand/utils/zustandStateStore.js +1 -361
  20. package/lib/module/index.js +1 -80
  21. package/lib/module/preset.js +1 -98
  22. package/lib/module/zustand/components/ZustandActionButton.js +1 -112
  23. package/lib/module/zustand/components/ZustandDetailViewToggle.js +1 -129
  24. package/lib/module/zustand/components/ZustandEventFilterView.js +1 -287
  25. package/lib/module/zustand/components/ZustandIcon.js +1 -32
  26. package/lib/module/zustand/components/ZustandModal.js +1 -599
  27. package/lib/module/zustand/components/ZustandStateChangeItem.js +1 -161
  28. package/lib/module/zustand/components/ZustandStateDetailContent.js +1 -348
  29. package/lib/module/zustand/components/ZustandStateInfoView.js +1 -503
  30. package/lib/module/zustand/components/ZustandStoreBrowser.js +1 -303
  31. package/lib/module/zustand/components/index.js +1 -10
  32. package/lib/module/zustand/hooks/index.js +1 -3
  33. package/lib/module/zustand/hooks/useZustandStateChanges.js +1 -88
  34. package/lib/module/zustand/index.js +1 -12
  35. package/lib/module/zustand/utils/buoyZustandMiddleware.js +1 -214
  36. package/lib/module/zustand/utils/index.js +1 -4
  37. package/lib/module/zustand/utils/zustandStateStore.js +1 -357
  38. package/package.json +10 -10
  39. package/lib/typescript/index.d.ts.map +0 -1
  40. package/lib/typescript/preset.d.ts.map +0 -1
  41. package/lib/typescript/zustand/components/ZustandActionButton.d.ts.map +0 -1
  42. package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +0 -1
  43. package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +0 -1
  44. package/lib/typescript/zustand/components/ZustandIcon.d.ts.map +0 -1
  45. package/lib/typescript/zustand/components/ZustandModal.d.ts.map +0 -1
  46. package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +0 -1
  47. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +0 -1
  48. package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +0 -1
  49. package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +0 -1
  50. package/lib/typescript/zustand/components/index.d.ts.map +0 -1
  51. package/lib/typescript/zustand/hooks/index.d.ts.map +0 -1
  52. package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +0 -1
  53. package/lib/typescript/zustand/index.d.ts.map +0 -1
  54. package/lib/typescript/zustand/types/index.d.ts.map +0 -1
  55. package/lib/typescript/zustand/utils/buoyZustandMiddleware.d.ts.map +0 -1
  56. package/lib/typescript/zustand/utils/index.d.ts.map +0 -1
  57. package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +0 -1
@@ -1,220 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.buoyDevTools = buoyDevTools;
7
- exports.isStoreInstrumented = isStoreInstrumented;
8
- exports.watchStores = watchStores;
9
- var _zustandStateStore = require("./zustandStateStore");
10
- /**
11
- * Buoy Zustand DevTools — Store instrumentation
12
- *
13
- * Two approaches, from easiest to most detailed:
14
- *
15
- * 1. watchStores() — RECOMMENDED. One line, zero store modifications.
16
- * Uses store.subscribe() externally. Never touches setState.
17
- * Safe to add/remove — cannot break your stores even if our code has bugs.
18
- *
19
- * 2. buoyDevTools() — Opt-in middleware for advanced use.
20
- * Wraps setState to capture the partial argument and timing.
21
- * Requires modifying each store's create() call.
22
- */
23
-
24
- /** Symbol to mark stores that have been watched/instrumented */
25
- const WATCHED_SYMBOL = Symbol.for("@@buoy-zustand/watched");
26
-
27
- /**
28
- * Detect if a store is using the persist middleware by checking for persist API
29
- */
30
- function detectPersist(store) {
31
- const persist = store.persist;
32
- if (persist && typeof persist.getOptions === "function") {
33
- const options = persist.getOptions();
34
- return {
35
- isPersisted: true,
36
- persistName: options?.name
37
- };
38
- }
39
- return {
40
- isPersisted: false
41
- };
42
- }
43
-
44
- // =============================================================================
45
- // watchStores — Primary, non-intrusive approach
46
- // =============================================================================
47
-
48
- /** Minimal store shape — what Zustand's create() returns */
49
-
50
- /**
51
- * Watch multiple Zustand stores for state changes.
52
- *
53
- * This is the recommended, non-intrusive approach. It uses each store's
54
- * `.subscribe()` method to observe changes from the outside — it never
55
- * modifies `setState` or any store internals.
56
- *
57
- * **Safe by design:** Even if our listener code has a bug, it cannot break
58
- * your stores. All listener code is wrapped in try/catch.
59
- *
60
- * @example
61
- * ```tsx
62
- * // In your _layout.tsx or App.tsx — ONE line:
63
- * import { watchStores } from '@buoy-gg/zustand';
64
- * import { useCounterStore } from './stores/counter';
65
- * import { useAuthStore } from './stores/auth';
66
- * import { useCartStore } from './stores/cart';
67
- *
68
- * watchStores({
69
- * counterStore: useCounterStore,
70
- * authStore: useAuthStore,
71
- * cartStore: useCartStore,
72
- * });
73
- * ```
74
- *
75
- * @param stores - Object mapping store names to Zustand store hooks
76
- * @returns Cleanup function that removes all subscriptions
77
- */
78
- function watchStores(stores) {
79
- const cleanups = [];
80
- for (const [name, store] of Object.entries(stores)) {
81
- // Skip if already watched
82
- const storeAny = store;
83
- if (storeAny[WATCHED_SYMBOL]) continue;
84
- storeAny[WATCHED_SYMBOL] = true;
85
- const {
86
- isPersisted,
87
- persistName
88
- } = detectPersist(store);
89
-
90
- // Register the store for overview
91
- _zustandStateStore.zustandStateStore.registerStore(name, {
92
- getState: store.getState,
93
- getInitialState: store.getInitialState,
94
- setState: store.setState,
95
- subscribe: store.subscribe
96
- }, {
97
- isPersisted,
98
- persistName
99
- });
100
-
101
- // Subscribe to state changes — completely external, never touches setState
102
- const unsubscribe = store.subscribe((state, prevState) => {
103
- try {
104
- _zustandStateStore.zustandStateStore.addStateChange({
105
- storeName: name,
106
- partial: undefined,
107
- // Not available in subscribe-only mode
108
- replace: false,
109
- prevState,
110
- nextState: state,
111
- duration: undefined,
112
- // Not available in subscribe-only mode
113
- isPersisted
114
- });
115
- } catch {
116
- // Silently catch — our bug must never propagate into the store
117
- }
118
- });
119
- cleanups.push(() => {
120
- unsubscribe();
121
- delete store[WATCHED_SYMBOL];
122
- _zustandStateStore.zustandStateStore.unregisterStore(name);
123
- });
124
- }
125
- return () => {
126
- cleanups.forEach(fn => fn());
127
- };
128
- }
129
-
130
- // =============================================================================
131
- // buoyDevTools — Opt-in middleware (advanced, more detail)
132
- // =============================================================================
133
-
134
- /** Auto-incrementing store name counter for unnamed stores */
135
- let unnamedCounter = 0;
136
-
137
- /**
138
- * Zustand middleware that instruments a store for Buoy DevTools.
139
- *
140
- * This is the advanced approach — it wraps setState to capture:
141
- * - The partial argument passed to setState
142
- * - Precise timing of each setState call
143
- *
144
- * Use this when you want maximum detail. For most cases, `watchStores()`
145
- * is simpler and safer.
146
- *
147
- * @example
148
- * ```tsx
149
- * import { create } from 'zustand';
150
- * import { buoyDevTools } from '@buoy-gg/zustand';
151
- *
152
- * const useCounterStore = create(
153
- * buoyDevTools(
154
- * (set) => ({
155
- * count: 0,
156
- * increment: () => set((s) => ({ count: s.count + 1 })),
157
- * }),
158
- * { name: 'counterStore' }
159
- * )
160
- * );
161
- * ```
162
- */
163
- function buoyDevTools(config, options) {
164
- return (set, get, api) => {
165
- const storeName = options?.name ?? `store-${++unnamedCounter}`;
166
- const enabled = options?.enabled !== false;
167
- const instrumentedSet = (partial, replace) => {
168
- if (!enabled) {
169
- return set(partial, replace);
170
- }
171
- const startTime = performance.now();
172
- const prevState = get();
173
- set(partial, replace);
174
- const nextState = get();
175
- const duration = performance.now() - startTime;
176
- const resolvedPartial = typeof partial === "function" ? "(updater fn)" : partial;
177
- _zustandStateStore.zustandStateStore.addStateChange({
178
- storeName,
179
- partial: resolvedPartial,
180
- replace: replace ?? false,
181
- prevState,
182
- nextState,
183
- duration,
184
- isPersisted: false
185
- });
186
- };
187
- const result = config(instrumentedSet, get, api);
188
-
189
- // Register the store after creation
190
- setTimeout(() => {
191
- if (api[WATCHED_SYMBOL]) return;
192
- api[WATCHED_SYMBOL] = true;
193
- const {
194
- isPersisted,
195
- persistName
196
- } = detectPersist(api);
197
- _zustandStateStore.zustandStateStore.registerStore(storeName, {
198
- getState: get,
199
- getInitialState: api.getInitialState,
200
- setState: set,
201
- subscribe: api.subscribe
202
- }, {
203
- isPersisted,
204
- persistName
205
- });
206
- }, 0);
207
- return result;
208
- };
209
- }
210
-
211
- // =============================================================================
212
- // Utility
213
- // =============================================================================
214
-
215
- /**
216
- * Check if a store is being watched/instrumented
217
- */
218
- function isStoreInstrumented(store) {
219
- return store[WATCHED_SYMBOL] === true;
220
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.buoyDevTools=buoyDevTools,exports.isStoreInstrumented=isStoreInstrumented,exports.watchStores=watchStores;var _zustandStateStore=require("./zustandStateStore");const WATCHED_SYMBOL=Symbol.for("@@buoy-zustand/watched");function detectPersist(t){const e=t.persist;if(e&&"function"==typeof e.getOptions){const t=e.getOptions();return{isPersisted:!0,persistName:t?.name}}return{isPersisted:!1}}function watchStores(t){const e=[];for(const[s,r]of Object.entries(t)){const t=r;if(t[WATCHED_SYMBOL])continue;t[WATCHED_SYMBOL]=!0;const{isPersisted:n,persistName:a}=detectPersist(r);_zustandStateStore.zustandStateStore.registerStore(s,{getState:r.getState,getInitialState:r.getInitialState,setState:r.setState,subscribe:r.subscribe},{isPersisted:n,persistName:a});const o=r.subscribe((t,e)=>{try{_zustandStateStore.zustandStateStore.addStateChange({storeName:s,partial:void 0,replace:!1,prevState:e,nextState:t,duration:void 0,isPersisted:n})}catch{}});e.push(()=>{o(),delete r[WATCHED_SYMBOL],_zustandStateStore.zustandStateStore.unregisterStore(s)})}return()=>{e.forEach(t=>t())}}let unnamedCounter=0;function buoyDevTools(t,e){return(s,r,n)=>{const a=e?.name??"store-"+ ++unnamedCounter,o=!1!==e?.enabled,i=t((t,e)=>{if(!o)return s(t,e);const n=performance.now(),i=r();s(t,e);const S=r(),u=performance.now()-n,d="function"==typeof t?"(updater fn)":t;_zustandStateStore.zustandStateStore.addStateChange({storeName:a,partial:d,replace:e??!1,prevState:i,nextState:S,duration:u,isPersisted:!1})},r,n);return setTimeout(()=>{if(n[WATCHED_SYMBOL])return;n[WATCHED_SYMBOL]=!0;const{isPersisted:t,persistName:e}=detectPersist(n);_zustandStateStore.zustandStateStore.registerStore(a,{getState:r,getInitialState:n.getInitialState,setState:s,subscribe:n.subscribe},{isPersisted:t,persistName:e})},0),i}}function isStoreInstrumented(t){return!0===t[WATCHED_SYMBOL]}
@@ -1,31 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "buoyDevTools", {
7
- enumerable: true,
8
- get: function () {
9
- return _buoyZustandMiddleware.buoyDevTools;
10
- }
11
- });
12
- Object.defineProperty(exports, "isStoreInstrumented", {
13
- enumerable: true,
14
- get: function () {
15
- return _buoyZustandMiddleware.isStoreInstrumented;
16
- }
17
- });
18
- Object.defineProperty(exports, "watchStores", {
19
- enumerable: true,
20
- get: function () {
21
- return _buoyZustandMiddleware.watchStores;
22
- }
23
- });
24
- Object.defineProperty(exports, "zustandStateStore", {
25
- enumerable: true,
26
- get: function () {
27
- return _zustandStateStore.zustandStateStore;
28
- }
29
- });
30
- var _zustandStateStore = require("./zustandStateStore");
31
- var _buoyZustandMiddleware = require("./buoyZustandMiddleware");
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"buoyDevTools",{enumerable:!0,get:function(){return _buoyZustandMiddleware.buoyDevTools}}),Object.defineProperty(exports,"isStoreInstrumented",{enumerable:!0,get:function(){return _buoyZustandMiddleware.isStoreInstrumented}}),Object.defineProperty(exports,"watchStores",{enumerable:!0,get:function(){return _buoyZustandMiddleware.watchStores}}),Object.defineProperty(exports,"zustandStateStore",{enumerable:!0,get:function(){return _zustandStateStore.zustandStateStore}});var _zustandStateStore=require("./zustandStateStore"),_buoyZustandMiddleware=require("./buoyZustandMiddleware");
@@ -1,361 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.zustandStateStore = void 0;
7
- /**
8
- * Zustand state store - captures and stores Zustand state changes
9
- *
10
- * Mirrors the architecture of reduxActionStore.ts from @buoy-gg/redux
11
- */
12
-
13
- // ============================================
14
- // Store Color Palette
15
- // ============================================
16
-
17
- const STORE_COLORS = {
18
- counter: "#10B981",
19
- // emerald
20
- auth: "#8B5CF6",
21
- // purple
22
- user: "#3B82F6",
23
- // blue
24
- cart: "#EC4899",
25
- // pink
26
- app: "#6366F1",
27
- // indigo
28
- ui: "#F59E0B",
29
- // amber
30
- settings: "#14B8A6",
31
- // teal
32
- theme: "#06B6D4",
33
- // cyan
34
- navigation: "#F97316",
35
- // orange
36
- form: "#EF4444" // red
37
- };
38
-
39
- /**
40
- * Get consistent color for a store based on its name
41
- */
42
- function getStoreColor(storeName) {
43
- const lowerName = storeName.toLowerCase();
44
-
45
- // Check for exact match
46
- if (STORE_COLORS[lowerName]) {
47
- return STORE_COLORS[lowerName];
48
- }
49
-
50
- // Check for partial matches
51
- for (const [key, color] of Object.entries(STORE_COLORS)) {
52
- if (lowerName.includes(key)) {
53
- return color;
54
- }
55
- }
56
-
57
- // Generate consistent hex color from name hash
58
- const hash = storeName.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
59
- const hue = hash * 137 % 360;
60
- // Convert HSL to hex so "color + alpha_hex" pattern works everywhere
61
- const s = 0.7;
62
- const l = 0.6;
63
- const c = (1 - Math.abs(2 * l - 1)) * s;
64
- const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
65
- const m = l - c / 2;
66
- let r = 0,
67
- g = 0,
68
- b = 0;
69
- if (hue < 60) {
70
- r = c;
71
- g = x;
72
- } else if (hue < 120) {
73
- r = x;
74
- g = c;
75
- } else if (hue < 180) {
76
- g = c;
77
- b = x;
78
- } else if (hue < 240) {
79
- g = x;
80
- b = c;
81
- } else if (hue < 300) {
82
- r = x;
83
- b = c;
84
- } else {
85
- r = c;
86
- b = x;
87
- }
88
- const toHex = v => Math.round((v + m) * 255).toString(16).padStart(2, "0");
89
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
90
- }
91
-
92
- // ============================================
93
- // Helper Functions
94
- // ============================================
95
-
96
- /**
97
- * Format payload preview for display
98
- */
99
- function formatPartialPreview(partial, maxLength = 40) {
100
- if (partial === undefined) return "";
101
- if (partial === null) return "null";
102
- try {
103
- if (typeof partial === "function") {
104
- return "(updater fn)";
105
- }
106
- if (typeof partial === "string") {
107
- return partial.length > maxLength ? `"${partial.slice(0, maxLength - 3)}..."` : `"${partial}"`;
108
- }
109
- if (typeof partial === "number" || typeof partial === "boolean") {
110
- return String(partial);
111
- }
112
- if (Array.isArray(partial)) {
113
- if (partial.length === 0) return "[]";
114
- const preview = JSON.stringify(partial);
115
- return preview.length > maxLength ? `[${partial.length} items]` : preview;
116
- }
117
- if (typeof partial === "object") {
118
- const keys = Object.keys(partial);
119
- if (keys.length === 0) return "{}";
120
- const preview = JSON.stringify(partial);
121
- if (preview.length <= maxLength) return preview;
122
- return `{ ${keys.length} keys }`;
123
- }
124
- return String(partial).slice(0, maxLength);
125
- } catch {
126
- return "[complex]";
127
- }
128
- }
129
-
130
- /**
131
- * Calculate state diff summary
132
- */
133
- function getStateDiffSummary(prevState, nextState) {
134
- if (prevState === nextState) {
135
- return {
136
- summary: "no change",
137
- changedKeys: [],
138
- changedCount: 0
139
- };
140
- }
141
- if (typeof prevState !== "object" || typeof nextState !== "object" || prevState === null || nextState === null) {
142
- return {
143
- summary: "changed",
144
- changedKeys: [],
145
- changedCount: 1
146
- };
147
- }
148
- const prevKeys = Object.keys(prevState);
149
- const nextKeys = Object.keys(nextState);
150
- const added = nextKeys.filter(k => !prevKeys.includes(k));
151
- const removed = prevKeys.filter(k => !nextKeys.includes(k));
152
- const changed = [];
153
- for (const key of prevKeys) {
154
- if (nextKeys.includes(key) && prevState[key] !== nextState[key]) {
155
- changed.push(key);
156
- }
157
- }
158
- const allChangedKeys = [...added, ...removed, ...changed];
159
- const parts = [];
160
- if (added.length > 0) parts.push(`+${added.length}`);
161
- if (removed.length > 0) parts.push(`-${removed.length}`);
162
- if (changed.length > 0) parts.push(`~${changed.length}`);
163
- const totalChanged = allChangedKeys.length;
164
- if (parts.length === 0) {
165
- return {
166
- summary: "nested change",
167
- changedKeys: [],
168
- changedCount: 1
169
- };
170
- }
171
- return {
172
- summary: `${parts.join(" ")} ${totalChanged === 1 ? "key" : "keys"}`,
173
- changedKeys: allChangedKeys,
174
- changedCount: totalChanged
175
- };
176
- }
177
-
178
- // ============================================
179
- // Zustand State Store
180
- // ============================================
181
-
182
- class ZustandStateStore {
183
- stateChanges = [];
184
- stores = new Map();
185
- listeners = new Set();
186
- storeListeners = new Set();
187
- maxChanges = 200;
188
- idCounter = 0;
189
- isEnabled = true;
190
-
191
- // ---- State Change Tracking ----
192
-
193
- addStateChange(params) {
194
- if (!this.isEnabled) return;
195
- const {
196
- storeName,
197
- partial,
198
- replace,
199
- prevState,
200
- nextState,
201
- duration,
202
- category,
203
- isPersisted = false
204
- } = params;
205
- const hasStateChange = prevState !== nextState;
206
- const {
207
- summary,
208
- changedKeys,
209
- changedCount
210
- } = getStateDiffSummary(prevState, nextState);
211
- const partialPreview = formatPartialPreview(partial);
212
- const isSlowUpdate = (duration ?? 0) > 16;
213
- const resolvedCategory = category ?? (replace ? "replace" : "setState");
214
- const stateChange = {
215
- id: `${Date.now()}-${++this.idCounter}`,
216
- storeName,
217
- timestamp: Date.now(),
218
- prevState,
219
- nextState,
220
- partial,
221
- duration,
222
- hasStateChange,
223
- replace,
224
- category: resolvedCategory,
225
- changedKeys,
226
- changedKeysCount: changedCount,
227
- diffSummary: summary,
228
- partialPreview,
229
- isSlowUpdate,
230
- isPersisted
231
- };
232
- this.stateChanges = [stateChange, ...this.stateChanges].slice(0, this.maxChanges);
233
-
234
- // Update store change count
235
- const storeInfo = this.stores.get(storeName);
236
- if (storeInfo) {
237
- storeInfo.stateChangeCount++;
238
- }
239
- this.notifyListeners();
240
- }
241
- getStateChanges() {
242
- return [...this.stateChanges];
243
- }
244
- getStateChangeById(id) {
245
- return this.stateChanges.find(c => c.id === id);
246
- }
247
- clearStateChanges() {
248
- this.stateChanges = [];
249
- // Reset counts
250
- for (const store of this.stores.values()) {
251
- store.stateChangeCount = 0;
252
- }
253
- this.notifyListeners();
254
- }
255
-
256
- // ---- Store Registry ----
257
-
258
- registerStore(name, api, options) {
259
- if (this.stores.has(name)) return;
260
- const storeInfo = {
261
- name,
262
- api,
263
- stateChangeCount: 0,
264
- isPersisted: options?.isPersisted ?? false,
265
- persistName: options?.persistName,
266
- color: getStoreColor(name)
267
- };
268
- this.stores.set(name, storeInfo);
269
- this.notifyStoreListeners();
270
- }
271
- unregisterStore(name) {
272
- this.stores.delete(name);
273
- this.notifyStoreListeners();
274
- }
275
- getStores() {
276
- return Array.from(this.stores.values());
277
- }
278
- getStore(name) {
279
- return this.stores.get(name);
280
- }
281
- getStoreColor(name) {
282
- return this.stores.get(name)?.color ?? getStoreColor(name);
283
- }
284
-
285
- // ---- Filtering ----
286
-
287
- filterStateChanges(filter) {
288
- let filtered = [...this.stateChanges];
289
- if (filter.searchText) {
290
- const search = filter.searchText.toLowerCase();
291
- filtered = filtered.filter(c => c.storeName.toLowerCase().includes(search) || c.partialPreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
292
- }
293
- if (filter.storeNames && filter.storeNames.length > 0) {
294
- filtered = filtered.filter(c => filter.storeNames.includes(c.storeName));
295
- }
296
- if (filter.onlyWithChanges) {
297
- filtered = filtered.filter(c => c.hasStateChange);
298
- }
299
- return filtered;
300
- }
301
-
302
- // ---- Stats ----
303
-
304
- getStats() {
305
- const total = this.stateChanges.length;
306
- const withChanges = this.stateChanges.filter(c => c.hasStateChange).length;
307
- const storeCount = this.stores.size;
308
- const durations = this.stateChanges.filter(c => c.duration !== undefined).map(c => c.duration);
309
- const avgDuration = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0;
310
- return {
311
- totalChanges: total,
312
- changesWithStateChange: withChanges,
313
- changesWithoutStateChange: total - withChanges,
314
- storeCount,
315
- averageDuration: Math.round(avgDuration * 100) / 100
316
- };
317
- }
318
- getUniqueStoreNames() {
319
- return Array.from(this.stores.keys()).sort();
320
- }
321
-
322
- // ---- Enable / Disable ----
323
-
324
- setEnabled(enabled) {
325
- this.isEnabled = enabled;
326
- }
327
- getEnabled() {
328
- return this.isEnabled;
329
- }
330
- setMaxChanges(max) {
331
- this.maxChanges = max;
332
- if (this.stateChanges.length > max) {
333
- this.stateChanges = this.stateChanges.slice(0, max);
334
- this.notifyListeners();
335
- }
336
- }
337
-
338
- // ---- Subscriptions ----
339
-
340
- subscribe(listener) {
341
- this.listeners.add(listener);
342
- return () => {
343
- this.listeners.delete(listener);
344
- };
345
- }
346
- subscribeToStores(listener) {
347
- this.storeListeners.add(listener);
348
- return () => {
349
- this.storeListeners.delete(listener);
350
- };
351
- }
352
- notifyListeners() {
353
- const changes = this.getStateChanges();
354
- this.listeners.forEach(listener => listener(changes));
355
- }
356
- notifyStoreListeners() {
357
- const stores = this.getStores();
358
- this.storeListeners.forEach(listener => listener(stores));
359
- }
360
- }
361
- const zustandStateStore = exports.zustandStateStore = new ZustandStateStore();
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.zustandStateStore=void 0;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,r=.7*(1-Math.abs(1.2-1)),n=r*(1-Math.abs(s/60%2-1)),a=.6-r/2;let i=0,o=0,h=0;s<60?(i=r,o=n):s<120?(i=n,o=r):s<180?(o=r,h=n):s<240?(o=n,h=r):s<300?(i=n,h=r):(i=r,h=n);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 r=JSON.stringify(e);return r.length<=t?r:`{ ${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),r=Object.keys(t),n=r.filter(e=>!s.includes(e)),a=s.filter(e=>!r.includes(e)),i=[];for(const n of s)r.includes(n)&&e[n]!==t[n]&&i.push(n);const o=[...n,...a,...i],h=[];n.length>0&&h.push(`+${n.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:r,prevState:n,nextState:a,duration:i,category:o,isPersisted:h=!1}=e,g=n!==a,{summary:u,changedKeys:l,changedCount:c}=getStateDiffSummary(n,a),d=formatPartialPreview(s),f=(i??0)>16,S=o??(r?"replace":"setState"),C={id:`${Date.now()}-${++this.idCounter}`,storeName:t,timestamp:Date.now(),prevState:n,nextState:a,partial:s,duration:i,hasStateChange:g,replace:r,category:S,changedKeys:l,changedKeysCount:c,diffSummary:u,partialPreview:d,isSlowUpdate:f,isPersisted:h};this.stateChanges=[C,...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 r={name:e,api:t,stateChangeCount:0,isPersisted:s?.isPersisted??!1,persistName:s?.persistName,color:getStoreColor(e)};this.stores.set(e,r),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,r=this.stateChanges.filter(e=>void 0!==e.duration).map(e=>e.duration),n=r.length>0?r.reduce((e,t)=>e+t,0)/r.length:0;return{totalChanges:e,changesWithStateChange:t,changesWithoutStateChange:e-t,storeCount:s,averageDuration:Math.round(100*n)/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))}}const zustandStateStore=exports.zustandStateStore=new ZustandStateStore;