@okyrychenko-dev/react-action-guard 0.2.2 → 0.3.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.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @okyrychenko-dev/react-action-guard
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@okyrychenko-dev/react-action-guard.svg)](https://www.npmjs.com/package/@okyrychenko-dev/react-action-guard)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@okyrychenko-dev/react-action-guard.svg)](https://www.npmjs.com/package/@okyrychenko-dev/react-action-guard)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
3
7
  > Elegant UI blocking management for React applications with priorities, scopes, and automatic cleanup
4
8
 
5
9
  ## Features
@@ -36,6 +40,16 @@ function MyComponent() {
36
40
  }
37
41
  ```
38
42
 
43
+ ## Documentation
44
+
45
+ 📚 **[Interactive Storybook Documentation](https://your-storybook-url.com)** - Explore live examples and detailed guides for all hooks
46
+
47
+ To run Storybook locally:
48
+
49
+ ```bash
50
+ npm run storybook
51
+ ```
52
+
39
53
  ## API Reference
40
54
 
41
55
  ### Hooks
@@ -676,25 +690,7 @@ Contributions are welcome! Please ensure:
676
690
 
677
691
  ## Changelog
678
692
 
679
- ### v0.2.0
680
-
681
- - Added middleware system (analytics, logging, performance)
682
- - Added advanced hooks:
683
- - `useConfirmableBlocker` - blockers with user confirmation
684
- - `useScheduledBlocker` - time-based blocking for maintenance windows
685
- - `useConditionalBlocker` - dynamic blocking based on conditions
686
- - Added `updateBlocker` method to store
687
- - Improved TypeScript types with discriminated unions
688
- - Added tree-shaking support
689
- - Added support for Amplitude analytics provider
690
- - Performance improvements and code refactoring
691
-
692
- ### v0.1.0
693
-
694
- - Initial release
695
- - Basic blocking system with priorities and scopes
696
- - `useBlocker`, `useIsBlocked`, `useAsyncAction` hooks
697
- - Zustand-based state management
693
+ See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes in each version.
698
694
 
699
695
  ## License
700
696
 
package/dist/index.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var react = require('react');
4
- var zustand = require('zustand');
5
4
  var middleware = require('zustand/middleware');
5
+ var zustand = require('zustand');
6
+ var shallow = require('zustand/shallow');
6
7
 
7
- // src/hooks/useBlocker.ts
8
+ // src/hooks/useAsyncAction/useAsyncAction.ts
8
9
 
9
10
  // src/store/uiBlockingStore.constants.ts
10
11
  var DEFAULT_SCOPE = "global";
@@ -12,8 +13,13 @@ var DEFAULT_REASON = "Unknown";
12
13
  var DEFAULT_PRIORITY = 0;
13
14
  var ASYNC_ACTION_PRIORITY = 20;
14
15
  var DEVTOOLS_NAME = "UIBlocking";
15
-
16
- // src/store/uiBlockingStore.utils.ts
16
+ function createShallowStore(storeCreator) {
17
+ const storeApi = zustand.createStore(storeCreator);
18
+ const useScopedStore = (selector) => {
19
+ return zustand.useStore(storeApi, shallow.useShallow(selector));
20
+ };
21
+ return { useStore: useScopedStore, useStoreApi: storeApi };
22
+ }
17
23
  function normalizeScopeToArray(scope) {
18
24
  if (typeof scope === "string") {
19
25
  return [scope];
@@ -178,30 +184,14 @@ var devtoolsConfig = {
178
184
  };
179
185
 
180
186
  // src/store/uiBlockingStore.store.ts
181
- var useUIBlockingStore = zustand.create()(
182
- middleware.devtools(createUIBlockingActions, devtoolsConfig)
183
- );
184
-
185
- // src/hooks/useBlocker.ts
186
- var useBlocker = (blockerId, config, isActive = true) => {
187
- const { addBlocker, removeBlocker } = useUIBlockingStore();
188
- react.useEffect(() => {
189
- if (!isActive || !blockerId) {
190
- return;
191
- }
192
- addBlocker(blockerId, config);
193
- return () => {
194
- removeBlocker(blockerId);
195
- };
196
- }, [blockerId, isActive, addBlocker, removeBlocker]);
197
- };
187
+ var { useStore: useUIBlockingStore} = createShallowStore(middleware.devtools(createUIBlockingActions, devtoolsConfig));
198
188
 
199
- // src/hooks/useIsBlocked.ts
200
- var useIsBlocked = (scope) => {
201
- return useUIBlockingStore((state) => state.isBlocked(scope));
202
- };
189
+ // src/hooks/useAsyncAction/useAsyncAction.ts
203
190
  var useAsyncAction = (actionId, scope) => {
204
- const { addBlocker, removeBlocker } = useUIBlockingStore();
191
+ const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({
192
+ addBlocker: state.addBlocker,
193
+ removeBlocker: state.removeBlocker
194
+ }));
205
195
  const executeWithBlocking = react.useCallback(
206
196
  async (asyncFn) => {
207
197
  const blockerId = `${actionId}-${Date.now().toString()}`;
@@ -220,15 +210,23 @@ var useAsyncAction = (actionId, scope) => {
220
210
  );
221
211
  return executeWithBlocking;
222
212
  };
223
- function useConfigRef(config) {
224
- const configRef = react.useRef(config);
213
+ var useBlocker = (blockerId, config, isActive = true) => {
214
+ const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({
215
+ addBlocker: state.addBlocker,
216
+ removeBlocker: state.removeBlocker
217
+ }));
225
218
  react.useEffect(() => {
226
- configRef.current = config;
227
- }, [config]);
228
- return configRef;
229
- }
219
+ if (!isActive || !blockerId) {
220
+ return;
221
+ }
222
+ addBlocker(blockerId, config);
223
+ return () => {
224
+ removeBlocker(blockerId);
225
+ };
226
+ }, [blockerId, isActive, addBlocker, removeBlocker]);
227
+ };
230
228
 
231
- // src/hooks/useBlocker.utils.ts
229
+ // src/hooks/useBlocker/useBlocker.utils.ts
232
230
  function createBlockerConfig(config) {
233
231
  return {
234
232
  scope: config.scope,
@@ -236,10 +234,20 @@ function createBlockerConfig(config) {
236
234
  priority: config.priority
237
235
  };
238
236
  }
237
+ function useConfigRef(config) {
238
+ const configRef = react.useRef(config);
239
+ react.useEffect(() => {
240
+ configRef.current = config;
241
+ }, [config]);
242
+ return configRef;
243
+ }
239
244
 
240
- // src/hooks/useConditionalBlocker.ts
245
+ // src/hooks/useConditionalBlocker/useConditionalBlocker.ts
241
246
  var useConditionalBlocker = (blockerId, config) => {
242
- const { addBlocker, removeBlocker } = useUIBlockingStore();
247
+ const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({
248
+ addBlocker: state.addBlocker,
249
+ removeBlocker: state.removeBlocker
250
+ }));
243
251
  const intervalRef = react.useRef(null);
244
252
  const isBlockedRef = react.useRef(false);
245
253
  const configRef = useConfigRef(config);
@@ -251,11 +259,10 @@ var useConditionalBlocker = (blockerId, config) => {
251
259
  }
252
260
  if (shouldBlock) {
253
261
  addBlocker(blockerId, createBlockerConfig(currentConfig));
254
- isBlockedRef.current = true;
255
262
  } else {
256
263
  removeBlocker(blockerId);
257
- isBlockedRef.current = false;
258
264
  }
265
+ isBlockedRef.current = shouldBlock;
259
266
  }, [blockerId, addBlocker, removeBlocker, configRef]);
260
267
  react.useEffect(() => {
261
268
  checkCondition();
@@ -317,8 +324,55 @@ var useConfirmableBlocker = (blockerId, config) => {
317
324
  onCancel
318
325
  };
319
326
  };
327
+
328
+ // src/hooks/useIsBlocked/useIsBlocked.ts
329
+ var useIsBlocked = (scope) => {
330
+ return useUIBlockingStore((state) => state.isBlocked(scope));
331
+ };
332
+
333
+ // src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
334
+ var MAX_TIMEOUT_MS = 2147483647;
335
+ function parseDate(date) {
336
+ return new Date(date).getTime();
337
+ }
338
+ function isValidTimestamp(timestamp) {
339
+ return !Number.isNaN(timestamp);
340
+ }
341
+ function calculateEndTime(schedule, startTime) {
342
+ if (schedule.duration) {
343
+ return startTime + schedule.duration;
344
+ }
345
+ if (schedule.end) {
346
+ const endTime = parseDate(schedule.end);
347
+ return isValidTimestamp(endTime) ? endTime : void 0;
348
+ }
349
+ return void 0;
350
+ }
351
+ function isSafeTimeout(delay) {
352
+ return delay > 0 && delay <= MAX_TIMEOUT_MS;
353
+ }
354
+ function isInBlockingPeriod(startTime, endTime, now) {
355
+ if (startTime > now) {
356
+ return false;
357
+ }
358
+ if (!endTime) {
359
+ return true;
360
+ }
361
+ return endTime > now;
362
+ }
363
+ function isScheduleInPast(startTime, endTime, now) {
364
+ if (!endTime) {
365
+ return false;
366
+ }
367
+ return endTime <= now && startTime <= now;
368
+ }
369
+
370
+ // src/hooks/useScheduledBlocker/useScheduledBlocker.ts
320
371
  var useScheduledBlocker = (blockerId, config) => {
321
- const { addBlocker, removeBlocker } = useUIBlockingStore();
372
+ const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({
373
+ addBlocker: state.addBlocker,
374
+ removeBlocker: state.removeBlocker
375
+ }));
322
376
  const timeoutsRef = react.useRef({});
323
377
  const configRef = useConfigRef(config);
324
378
  const cleanup = react.useCallback(() => {
@@ -333,41 +387,47 @@ var useScheduledBlocker = (blockerId, config) => {
333
387
  }, []);
334
388
  react.useEffect(() => {
335
389
  const currentConfig = configRef.current;
336
- const startTime = new Date(currentConfig.schedule.start).getTime();
390
+ const startTime = parseDate(currentConfig.schedule.start);
337
391
  const now = Date.now();
338
- let endTime;
339
- if (currentConfig.schedule.duration) {
340
- endTime = startTime + currentConfig.schedule.duration;
341
- } else if (currentConfig.schedule.end) {
342
- endTime = new Date(currentConfig.schedule.end).getTime();
392
+ if (!isValidTimestamp(startTime)) {
393
+ console.error(`[UIBlocking] Invalid start time for blocker "${blockerId}"`);
394
+ return;
343
395
  }
344
- const scheduleEnd = (end) => {
345
- const remainingTime = end - Date.now();
396
+ const scheduleEnd = () => {
397
+ const endTime = calculateEndTime(currentConfig.schedule, startTime);
398
+ if (!endTime) {
399
+ return;
400
+ }
401
+ const remainingTime = endTime - Date.now();
346
402
  if (remainingTime > 0) {
347
403
  timeoutsRef.current.end = setTimeout(() => {
348
- removeBlocker(blockerId);
349
404
  configRef.current.onScheduleEnd?.();
405
+ removeBlocker(blockerId);
350
406
  }, remainingTime);
351
407
  }
352
408
  };
409
+ const startBlocking = () => {
410
+ const cfg = configRef.current;
411
+ addBlocker(blockerId, createBlockerConfig(cfg));
412
+ cfg.onScheduleStart?.();
413
+ scheduleEnd();
414
+ };
353
415
  if (startTime > now) {
354
- timeoutsRef.current.start = setTimeout(() => {
355
- const cfg = configRef.current;
356
- addBlocker(blockerId, createBlockerConfig(cfg));
357
- cfg.onScheduleStart?.();
358
- if (endTime) {
359
- scheduleEnd(endTime);
360
- }
361
- }, startTime - now);
362
- } else if (endTime && endTime > now) {
363
- addBlocker(blockerId, createBlockerConfig(currentConfig));
364
- currentConfig.onScheduleStart?.();
365
- scheduleEnd(endTime);
366
- } else if (!endTime && startTime <= now) {
367
- addBlocker(blockerId, createBlockerConfig(currentConfig));
368
- currentConfig.onScheduleStart?.();
416
+ const delay = startTime - now;
417
+ if (!isSafeTimeout(delay)) {
418
+ console.warn(
419
+ `[UIBlocking] Schedule delay exceeds maximum timeout (${MAX_TIMEOUT_MS.toString()}ms) for blocker "${blockerId}"`
420
+ );
421
+ return;
422
+ }
423
+ timeoutsRef.current.start = setTimeout(startBlocking, delay);
369
424
  } else {
370
- console.warn(`[UIBlocking] Schedule is in the past for blocker "${blockerId}"`);
425
+ const endTime = calculateEndTime(currentConfig.schedule, startTime);
426
+ if (isInBlockingPeriod(startTime, endTime, now)) {
427
+ startBlocking();
428
+ } else if (isScheduleInPast(startTime, endTime, now)) {
429
+ console.warn(`[UIBlocking] Schedule is in the past for blocker "${blockerId}"`);
430
+ }
371
431
  }
372
432
  return () => {
373
433
  cleanup();
@@ -376,56 +436,6 @@ var useScheduledBlocker = (blockerId, config) => {
376
436
  }, [blockerId, addBlocker, removeBlocker, cleanup]);
377
437
  };
378
438
 
379
- // src/middleware/loggerMiddleware.utils.ts
380
- var getActionEmoji = (action) => {
381
- switch (action) {
382
- case "add":
383
- return "\u2795";
384
- case "remove":
385
- return "\u2796";
386
- case "update":
387
- return "\u{1F504}";
388
- case "cancel":
389
- return "\u274C";
390
- case "timeout":
391
- return "\u23F1\uFE0F";
392
- default:
393
- return "\u2753";
394
- }
395
- };
396
- var extractConfigDetails = (config) => {
397
- if (!config) {
398
- return {};
399
- }
400
- const { scope, reason, priority } = config;
401
- const details = {
402
- scope,
403
- reason
404
- };
405
- if (priority !== void 0) {
406
- details.priority = priority;
407
- }
408
- return details;
409
- };
410
- var formatLogData = (context) => {
411
- const configDetails = extractConfigDetails(context.config);
412
- if (context.prevState) {
413
- return {
414
- config: configDetails,
415
- prevState: extractConfigDetails(context.prevState)
416
- };
417
- }
418
- return configDetails;
419
- };
420
-
421
- // src/middleware/loggerMiddleware.ts
422
- var LOG_PREFIX = "[UIBlocking]";
423
- var loggerMiddleware = (context) => {
424
- const emoji = getActionEmoji(context.action);
425
- const logData = formatLogData(context);
426
- console.log(LOG_PREFIX, emoji, context.blockerId, logData);
427
- };
428
-
429
439
  // src/middleware/analyticsMiddleware.utils.ts
430
440
  var capitalizeFirst = (str) => {
431
441
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -496,6 +506,56 @@ var createAnalyticsMiddleware = (config) => {
496
506
  };
497
507
  };
498
508
 
509
+ // src/middleware/loggerMiddleware.utils.ts
510
+ var getActionEmoji = (action) => {
511
+ switch (action) {
512
+ case "add":
513
+ return "\u2795";
514
+ case "remove":
515
+ return "\u2796";
516
+ case "update":
517
+ return "\u{1F504}";
518
+ case "cancel":
519
+ return "\u274C";
520
+ case "timeout":
521
+ return "\u23F1\uFE0F";
522
+ default:
523
+ return "\u2753";
524
+ }
525
+ };
526
+ var extractConfigDetails = (config) => {
527
+ if (!config) {
528
+ return {};
529
+ }
530
+ const { scope, reason, priority } = config;
531
+ const details = {
532
+ scope,
533
+ reason
534
+ };
535
+ if (priority !== void 0) {
536
+ details.priority = priority;
537
+ }
538
+ return details;
539
+ };
540
+ var formatLogData = (context) => {
541
+ const configDetails = extractConfigDetails(context.config);
542
+ if (context.prevState) {
543
+ return {
544
+ config: configDetails,
545
+ prevState: extractConfigDetails(context.prevState)
546
+ };
547
+ }
548
+ return configDetails;
549
+ };
550
+
551
+ // src/middleware/loggerMiddleware.ts
552
+ var LOG_PREFIX = "[UIBlocking]";
553
+ var loggerMiddleware = (context) => {
554
+ const emoji = getActionEmoji(context.action);
555
+ const logData = formatLogData(context);
556
+ console.log(LOG_PREFIX, emoji, context.blockerId, logData);
557
+ };
558
+
499
559
  // src/middleware/performanceMiddleware.utils.ts
500
560
  var handleAddAction = (blockerId, timestamp, blockStartTimes) => {
501
561
  blockStartTimes.set(blockerId, timestamp);
@@ -513,7 +573,9 @@ var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBl
513
573
  const duration = context.timestamp - startTime;
514
574
  blockStartTimes.delete(context.blockerId);
515
575
  if (duration >= slowBlockThreshold) {
516
- console.warn(`[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`);
576
+ console.warn(
577
+ `[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`
578
+ );
517
579
  onSlowBlock?.(context.blockerId, duration);
518
580
  }
519
581
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store/uiBlockingStore.constants.ts","../src/store/uiBlockingStore.utils.ts","../src/store/uiBlockingStore.actions.ts","../src/store/uiBlockingStore.config.ts","../src/store/uiBlockingStore.store.ts","../src/hooks/useBlocker.ts","../src/hooks/useIsBlocked.ts","../src/hooks/useAsyncAction.ts","../src/hooks/useConfigRef.ts","../src/hooks/useBlocker.utils.ts","../src/hooks/useConditionalBlocker.ts","../src/hooks/useConfirmableBlocker.ts","../src/hooks/useScheduledBlocker.ts","../src/middleware/loggerMiddleware.utils.ts","../src/middleware/loggerMiddleware.ts","../src/middleware/analyticsMiddleware.utils.ts","../src/middleware/analyticsMiddleware.ts","../src/middleware/performanceMiddleware.utils.ts","../src/middleware/performanceMiddleware.ts"],"names":["create","devtools","useEffect","useCallback","useRef","useState"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Default values and constants for UI Blocking Store\n */\n\n/**\n * Default scope for blockers\n */\nexport const DEFAULT_SCOPE = \"global\" as const;\n\n/**\n * Default reason when none is provided\n */\nexport const DEFAULT_REASON = \"Unknown\" as const;\n\n/**\n * Default priority for blockers\n */\nexport const DEFAULT_PRIORITY = 0 as const;\n\n/**\n * Default priority for async actions\n */\nexport const ASYNC_ACTION_PRIORITY = 20 as const;\n\n/**\n * DevTools store name\n */\nexport const DEVTOOLS_NAME = \"UIBlocking\" as const;\n","/**\n * Normalize scope to array format\n *\n * @param scope - Single scope or array of scopes\n * @returns Array of scopes\n *\n * @internal\n */\nexport function normalizeScopeToArray(scope: string | ReadonlyArray<string>): Array<string> {\n if (typeof scope === \"string\") {\n return [scope];\n }\n return Array.from(scope);\n}\n","import type { StateCreator } from \"zustand\";\nimport type { BlockerConfig, BlockerInfo, StoredBlocker, UIBlockingStore } from \"./uiBlockingStore.types\";\nimport { DEFAULT_SCOPE, DEFAULT_REASON, DEFAULT_PRIORITY } from \"./uiBlockingStore.constants\";\nimport { normalizeScopeToArray } from \"./uiBlockingStore.utils\";\nimport { Middleware, MiddlewareContext } from \"../middleware\";\n\n/**\n * UI Blocking Store Slice\n *\n * Implements the state and actions for UI blocking management.\n * This slice follows the Zustand slice pattern for better code organization.\n */\nexport const createUIBlockingActions: StateCreator<UIBlockingStore, [], [], UIBlockingStore> = (set, get) => ({\n // State\n activeBlockers: new Map(),\n middlewares: new Map(),\n\n registerMiddleware: (name: string, middleware: Middleware) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.set(name, middleware);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Registered middleware: ${name}`);\n },\n\n unregisterMiddleware: (name: string) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.delete(name);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Unregistered middleware: ${name}`);\n },\n\n runMiddlewares: async (context: MiddlewareContext) => {\n const { middlewares } = get();\n\n for (const [name, middleware] of middlewares) {\n try {\n await middleware(context);\n } catch (error) {\n console.error(`[UIBlocking] Middleware \"${name}\" error:`, error);\n }\n }\n },\n\n // Actions\n /**\n * Add a blocker to the store\n *\n * @param id - Unique blocker identifier\n * @param config - Optional blocker configuration\n *\n */\n addBlocker: (id: string, config: BlockerConfig = {}): void => {\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n const storedBlocker: StoredBlocker = {\n scope: config.scope ?? DEFAULT_SCOPE,\n reason: config.reason ?? DEFAULT_REASON,\n priority: config.priority ?? DEFAULT_PRIORITY,\n timestamp: config.timestamp ?? Date.now(),\n };\n newBlockers.set(id, storedBlocker);\n\n return { activeBlockers: newBlockers };\n });\n\n void get().runMiddlewares({\n action: \"add\",\n blockerId: id,\n config: {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n },\n timestamp: Date.now(),\n });\n },\n\n /**\n * Remove a blocker from the store\n *\n * @param id - Blocker identifier to remove\n *\n */\n removeBlocker: (id: string): void => {\n const prevBlocker = get().activeBlockers.get(id);\n\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n newBlockers.delete(id);\n\n return { activeBlockers: newBlockers };\n });\n\n if (prevBlocker) {\n void get().runMiddlewares({\n action: \"remove\",\n blockerId: id,\n config: prevBlocker,\n timestamp: Date.now(),\n prevState: prevBlocker,\n });\n }\n },\n\n /**\n * Check if a scope is currently blocked\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the scopes are blocked\n *\n */\n isBlocked: (scope: string | ReadonlyArray<string> = DEFAULT_SCOPE): boolean => {\n const { activeBlockers } = get();\n const scopes = normalizeScopeToArray(scope);\n\n for (const [, blocker] of activeBlockers) {\n // Global blocking blocks everything\n if (blocker.scope === DEFAULT_SCOPE) {\n return true;\n }\n\n // Check scope intersection\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (scopes.some((s) => blockerScopes.includes(s))) {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Get detailed blocking information for a specific scope\n *\n * @param scope - Scope to get blocking information for\n * @returns Array of blockers affecting the scope, sorted by priority (highest first)\n *\n */\n getBlockingInfo: (scope: string): ReadonlyArray<BlockerInfo> => {\n const { activeBlockers } = get();\n const blockers: Array<BlockerInfo> = [];\n\n for (const [id, blocker] of activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {\n blockers.push({ id, ...blocker });\n }\n }\n\n return blockers.sort((a, b) => b.priority - a.priority);\n },\n\n /**\n * Clear all blockers from the store\n */\n clearAllBlockers: (): void => {\n set({ activeBlockers: new Map() });\n },\n\n /**\n * Clear all blockers for a specific scope\n *\n * @param scope - Scope to clear blockers for\n *\n */\n clearBlockersForScope: (scope: string): void => {\n set((state) => {\n const newBlockers = new Map();\n\n for (const [id, blocker] of state.activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (!blockerScopes.includes(scope)) {\n newBlockers.set(id, blocker);\n }\n }\n\n return { activeBlockers: newBlockers };\n });\n },\n});\n","import type { DevtoolsOptions } from \"zustand/middleware\";\nimport { DEVTOOLS_NAME } from \"./uiBlockingStore.constants\";\n\n/**\n * DevTools configuration for UI Blocking Store\n *\n * Enables Redux DevTools integration only in development mode\n * for better debugging experience without production overhead.\n */\nexport const devtoolsConfig: DevtoolsOptions = {\n name: DEVTOOLS_NAME,\n enabled: process.env.NODE_ENV !== \"production\",\n};\n","import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { createUIBlockingActions } from \"./uiBlockingStore.actions\";\nimport { devtoolsConfig } from \"./uiBlockingStore.config\";\nimport type { UIBlockingStore } from \"./uiBlockingStore.types\";\n\n/**\n * UI Blocking Store\n *\n * Global Zustand store for managing UI blocking state across the application.\n *\n * Features:\n * - Priority-based blocking system\n * - Scope-based blocking (global, specific areas, or multiple scopes)\n * - Automatic cleanup on unmount (when using hooks)\n * - DevTools integration for debugging\n *\n */\nexport const useUIBlockingStore = create<UIBlockingStore>()(\n devtools(createUIBlockingActions, devtoolsConfig)\n);\n","import { useEffect } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport type { BlockerConfig } from \"../store\";\n\n/**\n * Hook to automatically add and remove a blocker based on component lifecycle\n *\n * Automatically adds a blocker when the component mounts (or when activated)\n * and removes it when the component unmounts (or when deactivated).\n *\n * @param blockerId - Unique identifier for the blocker\n * @param config - Blocker configuration (scope, reason, priority)\n * @param isActive - Whether the blocker is currently active (default: true)\n *\n */\nexport const useBlocker = (blockerId: string, config: BlockerConfig, isActive = true): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n useEffect(() => {\n if (!isActive || !blockerId) {\n return;\n }\n\n addBlocker(blockerId, config);\n\n return () => {\n removeBlocker(blockerId);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, isActive, addBlocker, removeBlocker]);\n};\n","import { useUIBlockingStore } from \"../store\";\n\n/**\n * Hook to check if a scope is currently blocked\n *\n * Returns a boolean indicating whether the specified scope(s) are blocked.\n * Automatically re-renders when the blocking state changes.\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the specified scopes are blocked\n *\n */\nexport const useIsBlocked = (scope?: string | ReadonlyArray<string>): boolean => {\n return useUIBlockingStore((state) => state.isBlocked(scope));\n};\n","import { useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ASYNC_ACTION_PRIORITY } from \"../store\";\n\n/**\n * Hook to wrap async functions with automatic blocking/unblocking\n *\n * Creates a wrapper function that automatically adds a blocker before executing\n * the async function and removes it after completion (success or failure).\n *\n * @template T - The return type of the async function\n * @param actionId - Unique identifier for the action (used in blocker ID and reason)\n * @param scope - Scope(s) to block during execution (default: \"global\")\n * @returns Function that wraps async functions with blocking behavior\n *\n */\nexport const useAsyncAction = <T = unknown>(\n actionId: string,\n scope?: string | ReadonlyArray<string>\n): ((asyncFn: () => Promise<T>) => Promise<T>) => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n\n const executeWithBlocking = useCallback(\n async (asyncFn: () => Promise<T>): Promise<T> => {\n const blockerId = `${actionId}-${Date.now().toString()}`;\n\n try {\n addBlocker(blockerId, {\n scope,\n reason: `Executing ${actionId}`,\n priority: ASYNC_ACTION_PRIORITY,\n });\n\n return await asyncFn();\n } finally {\n removeBlocker(blockerId);\n }\n },\n [actionId, scope, addBlocker, removeBlocker]\n );\n\n return executeWithBlocking;\n};\n","import { useRef, useEffect, RefObject } from \"react\";\n\n/**\n * Keeps a ref updated with the latest config value.\n * Useful for accessing current config in callbacks without stale closures.\n *\n * @param config - Configuration object to track\n * @returns Mutable ref that always contains the latest config\n *\n */\nexport function useConfigRef<T>(config: T): RefObject<T> {\n const configRef = useRef(config);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n return configRef;\n}\n","import { BlockerConfig } from \"../store\";\n\n/**\n * Creates a blocker configuration object from partial config.\n * Filters out undefined values to create a clean BlockerConfig.\n *\n * @param config - Partial blocker configuration\n * @returns Complete BlockerConfig object\n *\n */\nexport function createBlockerConfig(config: {\n scope?: string | ReadonlyArray<string>;\n reason?: string;\n priority?: number;\n}): BlockerConfig {\n return {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n };\n}\n","import { useRef, useEffect, useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ConditionalBlockerConfig } from \"./useConditionalBlocker.types\";\nimport { useConfigRef } from \"./useConfigRef\";\nimport { createBlockerConfig } from \"./useBlocker.utils\";\n\n/**\n * Hook for conditional blocking based on a condition function.\n *\n * Periodically checks a condition and blocks/unblocks based on the result.\n * The condition is evaluated at the specified interval (default 1000ms).\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including condition function and check interval\n *\n */\nexport const useConditionalBlocker = <TState = unknown>(\n blockerId: string,\n config: ConditionalBlockerConfig<TState>\n): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const isBlockedRef = useRef(false);\n const configRef = useConfigRef(config);\n\n const checkCondition = useCallback(() => {\n const currentConfig = configRef.current;\n const shouldBlock = currentConfig.condition(currentConfig.state);\n\n // No state change needed\n if (shouldBlock === isBlockedRef.current) {\n return;\n }\n\n if (shouldBlock) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n isBlockedRef.current = true;\n } else {\n removeBlocker(blockerId);\n isBlockedRef.current = false;\n }\n }, [blockerId, addBlocker, removeBlocker, configRef]);\n\n useEffect(() => {\n checkCondition();\n\n const interval = configRef.current.checkInterval ?? 1000;\n intervalRef.current = setInterval(checkCondition, interval);\n\n return (): void => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n if (isBlockedRef.current) {\n removeBlocker(blockerId);\n isBlockedRef.current = false;\n }\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [checkCondition, blockerId, removeBlocker]);\n};\n","import { useState, useCallback } from \"react\";\nimport { useBlocker } from \"./useBlocker\";\nimport { ConfirmableBlockerConfig, UseConfirmableBlockerReturn } from \"./useConfirmableBlocker.types\";\n\n/**\n * Hook for creating a confirmable action with blocking UI\n *\n */\nexport const useConfirmableBlocker = (\n blockerId: string,\n config: ConfirmableBlockerConfig\n): UseConfirmableBlockerReturn => {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n\n useBlocker(\n blockerId,\n {\n ...config,\n reason: config.reason ?? config.confirmMessage,\n },\n isDialogOpen || isExecuting\n );\n\n const execute = useCallback(() => {\n setIsDialogOpen(true);\n }, []);\n\n const onConfirm = useCallback(async () => {\n setIsDialogOpen(false);\n setIsExecuting(true);\n\n try {\n await config.onConfirm();\n } catch (error) {\n console.error(\"[UIBlocking] Confirmable action error:\", error);\n throw error;\n } finally {\n setIsExecuting(false);\n }\n }, [config]);\n\n const onCancel = useCallback(() => {\n setIsDialogOpen(false);\n config.onCancel?.();\n }, [config]);\n\n return {\n execute,\n isDialogOpen,\n isExecuting,\n confirmConfig: {\n title: config.confirmTitle ?? \"Confirm Action\",\n message: config.confirmMessage,\n confirmText: config.confirmButtonText ?? \"Confirm\",\n cancelText: config.cancelButtonText ?? \"Cancel\",\n },\n onConfirm,\n onCancel,\n };\n};\n","import { useEffect, useRef, useCallback } from \"react\";\nimport { useUIBlockingStore } from \"../store\";\nimport { ScheduledBlockerConfig } from \"./useScheduledBlocker.types\";\nimport { useConfigRef } from \"./useConfigRef\";\nimport { createBlockerConfig } from \"./useBlocker.utils\";\n\n/**\n * Hook for scheduling UI blocking for a specific time period.\n *\n * Blocks UI during a scheduled maintenance window or time period.\n * Supports start/end times as Date objects, ISO strings, or timestamps.\n * If both duration and end are provided, duration takes precedence.\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including schedule and callbacks\n *\n */\nexport const useScheduledBlocker = (blockerId: string, config: ScheduledBlockerConfig): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore();\n const timeoutsRef = useRef<{\n start?: ReturnType<typeof setTimeout>;\n end?: ReturnType<typeof setTimeout>;\n }>({});\n const configRef = useConfigRef(config);\n\n const cleanup = useCallback(() => {\n if (timeoutsRef.current.start) {\n clearTimeout(timeoutsRef.current.start);\n timeoutsRef.current.start = undefined;\n }\n if (timeoutsRef.current.end) {\n clearTimeout(timeoutsRef.current.end);\n timeoutsRef.current.end = undefined;\n }\n }, []);\n\n useEffect(() => {\n const currentConfig = configRef.current;\n const startTime = new Date(currentConfig.schedule.start).getTime();\n const now = Date.now();\n\n let endTime: number | undefined;\n if (currentConfig.schedule.duration) {\n endTime = startTime + currentConfig.schedule.duration;\n } else if (currentConfig.schedule.end) {\n endTime = new Date(currentConfig.schedule.end).getTime();\n }\n\n const scheduleEnd = (end: number): void => {\n const remainingTime = end - Date.now();\n if (remainingTime > 0) {\n timeoutsRef.current.end = setTimeout(() => {\n removeBlocker(blockerId);\n configRef.current.onScheduleEnd?.();\n }, remainingTime);\n }\n };\n\n // If start time hasn't arrived yet\n if (startTime > now) {\n timeoutsRef.current.start = setTimeout(() => {\n const cfg = configRef.current;\n addBlocker(blockerId, createBlockerConfig(cfg));\n cfg.onScheduleStart?.();\n\n if (endTime) {\n scheduleEnd(endTime);\n }\n }, startTime - now);\n }\n // If already in blocking period\n else if (endTime && endTime > now) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n currentConfig.onScheduleStart?.();\n scheduleEnd(endTime);\n }\n // If schedule without end/duration\n else if (!endTime && startTime <= now) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n currentConfig.onScheduleStart?.();\n } else {\n console.warn(`[UIBlocking] Schedule is in the past for blocker \"${blockerId}\"`);\n }\n\n return (): void => {\n cleanup();\n removeBlocker(blockerId);\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, addBlocker, removeBlocker, cleanup]);\n};\n","import { MiddlewareBlockerConfig, MiddlewareContext } from \"./middleware.types\";\n\nexport const getActionEmoji = (action: string): string => {\n switch (action) {\n case \"add\":\n return \"➕\";\n case \"remove\":\n return \"➖\";\n case \"update\":\n return \"🔄\";\n case \"cancel\":\n return \"❌\";\n case \"timeout\":\n return \"⏱️\";\n default:\n return \"❓\";\n }\n};\n\nconst extractConfigDetails = (config?: MiddlewareBlockerConfig): Record<string, unknown> => {\n if (!config) {\n return {};\n }\n\n const { scope, reason, priority } = config;\n\n const details: Record<string, unknown> = {\n scope,\n reason,\n };\n\n if (priority !== undefined) {\n details.priority = priority;\n }\n\n return details;\n};\n\nexport const formatLogData = (context: MiddlewareContext): Record<string, unknown> => {\n const configDetails = extractConfigDetails(context.config);\n\n if (context.prevState) {\n return {\n config: configDetails,\n prevState: extractConfigDetails(context.prevState),\n };\n }\n\n return configDetails;\n};\n","import { getActionEmoji, formatLogData } from \"./loggerMiddleware.utils\";\nimport { Middleware } from \"./middleware.types\";\n\nconst LOG_PREFIX = \"[UIBlocking]\";\n\nexport const loggerMiddleware: Middleware = (context) => {\n const emoji = getActionEmoji(context.action);\n const logData = formatLogData(context);\n\n console.log(LOG_PREFIX, emoji, context.blockerId, logData);\n};\n","import { AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport { MiddlewareContext } from \"./middleware.types\";\n\nconst capitalizeFirst = (str: string): string => {\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\nexport const buildEventData = (context: MiddlewareContext): AnalyticsEventData => {\n const eventData: AnalyticsEventData = {\n blocker_id: context.blockerId,\n };\n const { scope, reason, priority } = context.config ?? {};\n\n if (scope !== undefined) {\n eventData.scope = Array.isArray(scope) ? scope.join(\",\") : scope;\n }\n\n if (reason !== undefined) {\n eventData.reason = reason;\n }\n\n if (priority !== undefined) {\n eventData.priority = priority;\n }\n\n return eventData;\n};\n\nexport const trackGoogleAnalytics = (action: string, eventData: AnalyticsEventData): void => {\n if (window.gtag) {\n window.gtag(\"event\", `ui_blocking_${action}`, eventData);\n }\n};\n\nexport const trackMixpanel = (action: string, eventData: AnalyticsEventData): void => {\n if (window.mixpanel) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.mixpanel.track(eventName, eventData);\n }\n};\n\nexport const trackAmplitude = (action: string, eventData: AnalyticsEventData): void => {\n if (window.amplitude) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.amplitude.track(eventName, eventData);\n }\n};\n\nexport const trackCustom = (\n trackFn: (event: string, data: AnalyticsEventData) => void,\n action: string,\n eventData: AnalyticsEventData\n): void => {\n try {\n trackFn(`ui_blocking_${action}`, eventData);\n } catch {\n // Silently ignore errors in custom tracker\n }\n};\n","import { AnalyticsConfig, AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport {\n buildEventData,\n trackGoogleAnalytics,\n trackMixpanel,\n trackAmplitude,\n trackCustom,\n} from \"./analyticsMiddleware.utils\";\nimport { Middleware, MiddlewareContext } from \"./middleware.types\";\n\nconst trackEvent = (config: AnalyticsConfig, context: MiddlewareContext, eventData: AnalyticsEventData): void => {\n if (\"provider\" in config) {\n switch (config.provider) {\n case \"ga\":\n trackGoogleAnalytics(context.action, eventData);\n return;\n case \"mixpanel\":\n trackMixpanel(context.action, eventData);\n return;\n case \"amplitude\":\n trackAmplitude(context.action, eventData);\n return;\n }\n }\n\n if (\"track\" in config) {\n trackCustom(config.track, context.action, eventData);\n }\n};\n\nexport const createAnalyticsMiddleware = (config: AnalyticsConfig): Middleware => {\n return (context) => {\n const eventData = buildEventData(context);\n\n trackEvent(config, context, eventData);\n };\n};\n","export const handleAddAction = (blockerId: string, timestamp: number, blockStartTimes: Map<string, number>): void => {\n blockStartTimes.set(blockerId, timestamp);\n};\n","import { Middleware, MiddlewareContext } from \"./middleware.types\";\nimport { PerformanceConfig } from \"./performanceMiddleware.types\";\nimport { handleAddAction } from \"./performanceMiddleware.utils\";\n\nconst DEFAULT_SLOW_BLOCK_THRESHOLD = 3000;\nconst ACTION_ADD = \"add\";\nconst ACTION_REMOVE = \"remove\";\n\nconst handleRemoveAction = (\n context: MiddlewareContext,\n blockStartTimes: Map<string, number>,\n slowBlockThreshold: number,\n onSlowBlock?: (blockerId: string, duration: number) => void\n): void => {\n const startTime = blockStartTimes.get(context.blockerId);\n\n if (startTime === undefined) {\n return;\n }\n\n const duration = context.timestamp - startTime;\n blockStartTimes.delete(context.blockerId);\n\n if (duration >= slowBlockThreshold) {\n console.warn(`[UIBlocking] Slow block detected: \"${context.blockerId}\" took ${duration.toString()}ms`);\n\n onSlowBlock?.(context.blockerId, duration);\n }\n};\n\nexport const createPerformanceMiddleware = (config: PerformanceConfig = {}): Middleware => {\n const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;\n const blockStartTimes = new Map<string, number>();\n\n return (context) => {\n if (context.action === ACTION_ADD) {\n handleAddAction(context.blockerId, context.timestamp, blockStartTimes);\n } else if (context.action === ACTION_REMOVE) {\n handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);\n }\n };\n};\n"]}
1
+ {"version":3,"sources":["../src/store/uiBlockingStore.constants.ts","../src/store/uiBlockingStore.utils.ts","../src/store/uiBlockingStore.actions.ts","../src/store/uiBlockingStore.config.ts","../src/store/uiBlockingStore.store.ts","../src/hooks/useAsyncAction/useAsyncAction.ts","../src/hooks/useBlocker/useBlocker.ts","../src/hooks/useBlocker/useBlocker.utils.ts","../src/hooks/useConfigRef.ts","../src/hooks/useConditionalBlocker/useConditionalBlocker.ts","../src/hooks/useConfirmableBlocker/useConfirmableBlocker.ts","../src/hooks/useIsBlocked/useIsBlocked.ts","../src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts","../src/hooks/useScheduledBlocker/useScheduledBlocker.ts","../src/middleware/analyticsMiddleware.utils.ts","../src/middleware/analyticsMiddleware.ts","../src/middleware/loggerMiddleware.utils.ts","../src/middleware/loggerMiddleware.ts","../src/middleware/performanceMiddleware.utils.ts","../src/middleware/performanceMiddleware.ts"],"names":["createStore","useStore","useShallow","devtools","useCallback","useEffect","useRef","useState"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Default values and constants for UI Blocking Store\n */\n\n/**\n * Default scope for blockers\n */\nexport const DEFAULT_SCOPE = \"global\" as const;\n\n/**\n * Default reason when none is provided\n */\nexport const DEFAULT_REASON = \"Unknown\" as const;\n\n/**\n * Default priority for blockers\n */\nexport const DEFAULT_PRIORITY = 0 as const;\n\n/**\n * Default priority for async actions\n */\nexport const ASYNC_ACTION_PRIORITY = 20 as const;\n\n/**\n * DevTools store name\n */\nexport const DEVTOOLS_NAME = \"UIBlocking\" as const;\n","import { StateCreator, StoreApi, StoreMutators, createStore, useStore } from \"zustand\";\nimport { useShallow } from \"zustand/shallow\";\n\n/**\n * Creates a Zustand store with automatic shallow comparison for all selectors\n *\n * This utility wraps the standard Zustand store creation and automatically applies\n * `useShallow` to all selectors, preventing unnecessary re-renders when selected\n * values haven't changed (shallow equality).\n *\n * @template StoreState - The type of the store state\n * @template TMutators - Array of middleware mutators (e.g., devtools, persist)\n *\n * @param storeCreator - The state creator function for Zustand\n * @returns Object containing the store hook and store API\n *\n * @see https://docs.pmnd.rs/zustand/guides/prevent-rerenders-with-use-shallow\n */\nexport function createShallowStore<\n StoreState,\n TMutators extends Array<[keyof StoreMutators<StoreState, StoreState>, unknown]> = [],\n>(\n storeCreator: StateCreator<StoreState, [], TMutators, StoreState>\n): {\n useStore: <T>(selector: (state: StoreState) => T) => T;\n useStoreApi: StoreApi<StoreState>;\n} {\n const storeApi: StoreApi<StoreState> = createStore<StoreState, TMutators>(storeCreator);\n\n const useScopedStore = <T>(selector: (state: StoreState) => T): T => {\n return useStore(storeApi, useShallow(selector));\n };\n\n return { useStore: useScopedStore, useStoreApi: storeApi };\n}\n\n/**\n * Normalize scope to array format\n *\n * @param scope - Single scope or array of scopes\n * @returns Array of scopes\n *\n * @internal\n */\nexport function normalizeScopeToArray(scope: string | ReadonlyArray<string>): Array<string> {\n if (typeof scope === \"string\") {\n return [scope];\n }\n return Array.from(scope);\n}\n","import { Middleware, MiddlewareContext } from \"../middleware\";\nimport { DEFAULT_PRIORITY, DEFAULT_REASON, DEFAULT_SCOPE } from \"./uiBlockingStore.constants\";\nimport { normalizeScopeToArray } from \"./uiBlockingStore.utils\";\nimport type {\n BlockerConfig,\n BlockerInfo,\n StoredBlocker,\n UIBlockingStore,\n} from \"./uiBlockingStore.types\";\nimport type { StateCreator } from \"zustand\";\n\n/**\n * UI Blocking Store Slice\n *\n * Implements the state and actions for UI blocking management.\n * This slice follows the Zustand slice pattern for better code organization.\n */\nexport const createUIBlockingActions: StateCreator<UIBlockingStore, [], [], UIBlockingStore> = (\n set,\n get\n) => ({\n // State\n activeBlockers: new Map(),\n middlewares: new Map(),\n\n registerMiddleware: (name: string, middleware: Middleware) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.set(name, middleware);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Registered middleware: ${name}`);\n },\n\n unregisterMiddleware: (name: string) => {\n set((state) => {\n const newMiddlewares = new Map(state.middlewares);\n newMiddlewares.delete(name);\n return { middlewares: newMiddlewares };\n });\n console.log(`[UIBlocking] Unregistered middleware: ${name}`);\n },\n\n runMiddlewares: async (context: MiddlewareContext) => {\n const { middlewares } = get();\n\n for (const [name, middleware] of middlewares) {\n try {\n await middleware(context);\n } catch (error) {\n console.error(`[UIBlocking] Middleware \"${name}\" error:`, error);\n }\n }\n },\n\n // Actions\n /**\n * Add a blocker to the store\n *\n * @param id - Unique blocker identifier\n * @param config - Optional blocker configuration\n *\n */\n addBlocker: (id: string, config: BlockerConfig = {}): void => {\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n const storedBlocker: StoredBlocker = {\n scope: config.scope ?? DEFAULT_SCOPE,\n reason: config.reason ?? DEFAULT_REASON,\n priority: config.priority ?? DEFAULT_PRIORITY,\n timestamp: config.timestamp ?? Date.now(),\n };\n newBlockers.set(id, storedBlocker);\n\n return { activeBlockers: newBlockers };\n });\n\n void get().runMiddlewares({\n action: \"add\",\n blockerId: id,\n config: {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n },\n timestamp: Date.now(),\n });\n },\n\n /**\n * Remove a blocker from the store\n *\n * @param id - Blocker identifier to remove\n *\n */\n removeBlocker: (id: string): void => {\n const prevBlocker = get().activeBlockers.get(id);\n\n set((state) => {\n const newBlockers = new Map(state.activeBlockers);\n newBlockers.delete(id);\n\n return { activeBlockers: newBlockers };\n });\n\n if (prevBlocker) {\n void get().runMiddlewares({\n action: \"remove\",\n blockerId: id,\n config: prevBlocker,\n timestamp: Date.now(),\n prevState: prevBlocker,\n });\n }\n },\n\n /**\n * Check if a scope is currently blocked\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the scopes are blocked\n *\n */\n isBlocked: (scope: string | ReadonlyArray<string> = DEFAULT_SCOPE): boolean => {\n const { activeBlockers } = get();\n const scopes = normalizeScopeToArray(scope);\n\n for (const [, blocker] of activeBlockers) {\n // Global blocking blocks everything\n if (blocker.scope === DEFAULT_SCOPE) {\n return true;\n }\n\n // Check scope intersection\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (scopes.some((s) => blockerScopes.includes(s))) {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Get detailed blocking information for a specific scope\n *\n * @param scope - Scope to get blocking information for\n * @returns Array of blockers affecting the scope, sorted by priority (highest first)\n *\n */\n getBlockingInfo: (scope: string): ReadonlyArray<BlockerInfo> => {\n const { activeBlockers } = get();\n const blockers: Array<BlockerInfo> = [];\n\n for (const [id, blocker] of activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {\n blockers.push({ id, ...blocker });\n }\n }\n\n return blockers.sort((a, b) => b.priority - a.priority);\n },\n\n /**\n * Clear all blockers from the store\n */\n clearAllBlockers: (): void => {\n set({ activeBlockers: new Map() });\n },\n\n /**\n * Clear all blockers for a specific scope\n *\n * @param scope - Scope to clear blockers for\n *\n */\n clearBlockersForScope: (scope: string): void => {\n set((state) => {\n const newBlockers = new Map();\n\n for (const [id, blocker] of state.activeBlockers) {\n const blockerScopes = normalizeScopeToArray(blocker.scope);\n\n if (!blockerScopes.includes(scope)) {\n newBlockers.set(id, blocker);\n }\n }\n\n return { activeBlockers: newBlockers };\n });\n },\n});\n","import { DEVTOOLS_NAME } from \"./uiBlockingStore.constants\";\nimport type { DevtoolsOptions } from \"zustand/middleware\";\n\n/**\n * DevTools configuration for UI Blocking Store\n *\n * Enables Redux DevTools integration only in development mode\n * for better debugging experience without production overhead.\n */\nexport const devtoolsConfig: DevtoolsOptions = {\n name: DEVTOOLS_NAME,\n enabled: process.env.NODE_ENV !== \"production\",\n};\n","import { devtools } from \"zustand/middleware\";\nimport { createUIBlockingActions } from \"./uiBlockingStore.actions\";\nimport { devtoolsConfig } from \"./uiBlockingStore.config\";\nimport { createShallowStore } from \"./uiBlockingStore.utils\";\nimport type { UIBlockingStore } from \"./uiBlockingStore.types\";\n\n/**\n * UI Blocking Store\n *\n * Global Zustand store for managing UI blocking state across the application.\n *\n * Features:\n * - Priority-based blocking system\n * - Scope-based blocking (global, specific areas, or multiple scopes)\n * - Automatic cleanup on unmount (when using hooks)\n * - DevTools integration for debugging\n * - Automatic shallow comparison for selectors\n *\n */\nconst { useStore: useUIBlockingStore, useStoreApi: uiBlockingStoreApi } = createShallowStore<\n UIBlockingStore,\n [[\"zustand/devtools\", never]]\n>(devtools(createUIBlockingActions, devtoolsConfig));\n\nexport { useUIBlockingStore, uiBlockingStoreApi };\n","import { useCallback } from \"react\";\nimport { ASYNC_ACTION_PRIORITY, useUIBlockingStore } from \"../../store\";\n\n/**\n * Hook to wrap async functions with automatic blocking/unblocking\n *\n * Creates a wrapper function that automatically adds a blocker before executing\n * the async function and removes it after completion (success or failure).\n *\n * @template T - The return type of the async function\n * @param actionId - Unique identifier for the action (used in blocker ID and reason)\n * @param scope - Scope(s) to block during execution (default: \"global\")\n * @returns Function that wraps async functions with blocking behavior\n *\n */\nexport const useAsyncAction = <T = unknown>(\n actionId: string,\n scope?: string | ReadonlyArray<string>\n): ((asyncFn: () => Promise<T>) => Promise<T>) => {\n const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({\n addBlocker: state.addBlocker,\n removeBlocker: state.removeBlocker,\n }));\n\n const executeWithBlocking = useCallback(\n async (asyncFn: () => Promise<T>): Promise<T> => {\n const blockerId = `${actionId}-${Date.now().toString()}`;\n\n try {\n addBlocker(blockerId, {\n scope,\n reason: `Executing ${actionId}`,\n priority: ASYNC_ACTION_PRIORITY,\n });\n\n return await asyncFn();\n } finally {\n removeBlocker(blockerId);\n }\n },\n [actionId, scope, addBlocker, removeBlocker]\n );\n\n return executeWithBlocking;\n};\n","import { useEffect } from \"react\";\nimport { type BlockerConfig, useUIBlockingStore } from \"../../store\";\n\n/**\n * Hook to automatically add and remove a blocker based on component lifecycle\n *\n * Automatically adds a blocker when the component mounts (or when activated)\n * and removes it when the component unmounts (or when deactivated).\n *\n * @param blockerId - Unique identifier for the blocker\n * @param config - Blocker configuration (scope, reason, priority)\n * @param isActive - Whether the blocker is currently active (default: true)\n *\n */\nexport const useBlocker = (blockerId: string, config: BlockerConfig, isActive = true): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({\n addBlocker: state.addBlocker,\n removeBlocker: state.removeBlocker,\n }));\n\n useEffect(() => {\n if (!isActive || !blockerId) {\n return;\n }\n\n addBlocker(blockerId, config);\n\n return () => {\n removeBlocker(blockerId);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, isActive, addBlocker, removeBlocker]);\n};\n","import { BlockerConfig } from \"../../store\";\n\n/**\n * Creates a blocker configuration object from partial config.\n * Filters out undefined values to create a clean BlockerConfig.\n *\n * @param config - Partial blocker configuration\n * @returns Complete BlockerConfig object\n *\n */\nexport function createBlockerConfig(config: {\n scope?: string | ReadonlyArray<string>;\n reason?: string;\n priority?: number;\n}): BlockerConfig {\n return {\n scope: config.scope,\n reason: config.reason,\n priority: config.priority,\n };\n}\n","import { RefObject, useEffect, useRef } from \"react\";\n\n/**\n * Keeps a ref updated with the latest config value.\n * Useful for accessing current config in callbacks without stale closures.\n *\n * @param config - Configuration object to track\n * @returns Mutable ref that always contains the latest config\n *\n */\nexport function useConfigRef<T>(config: T): RefObject<T> {\n const configRef = useRef(config);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n return configRef;\n}\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useUIBlockingStore } from \"../../store\";\nimport { createBlockerConfig } from \"../useBlocker\";\nimport { useConfigRef } from \"../useConfigRef\";\nimport { ConditionalBlockerConfig } from \"./useConditionalBlocker.types\";\n\n/**\n * Hook for conditional blocking based on a condition function.\n *\n * Periodically checks a condition and blocks/unblocks based on the result.\n * The condition is evaluated at the specified interval (default 1000ms).\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including condition function and check interval\n *\n */\nexport const useConditionalBlocker = <TState = unknown>(\n blockerId: string,\n config: ConditionalBlockerConfig<TState>\n): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({\n addBlocker: state.addBlocker,\n removeBlocker: state.removeBlocker,\n }));\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const isBlockedRef = useRef(false);\n const configRef = useConfigRef(config);\n\n const checkCondition = useCallback(() => {\n const currentConfig = configRef.current;\n const shouldBlock = currentConfig.condition(currentConfig.state);\n\n // No state change needed\n if (shouldBlock === isBlockedRef.current) {\n return;\n }\n\n if (shouldBlock) {\n addBlocker(blockerId, createBlockerConfig(currentConfig));\n } else {\n removeBlocker(blockerId);\n }\n isBlockedRef.current = shouldBlock;\n }, [blockerId, addBlocker, removeBlocker, configRef]);\n\n useEffect(() => {\n checkCondition();\n\n const interval = configRef.current.checkInterval ?? 1000;\n intervalRef.current = setInterval(checkCondition, interval);\n\n return (): void => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n if (isBlockedRef.current) {\n removeBlocker(blockerId);\n isBlockedRef.current = false;\n }\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [checkCondition, blockerId, removeBlocker]);\n};\n","import { useCallback, useState } from \"react\";\nimport { useBlocker } from \"../useBlocker\";\nimport {\n ConfirmableBlockerConfig,\n UseConfirmableBlockerReturn,\n} from \"./useConfirmableBlocker.types\";\n\n/**\n * Hook for creating a confirmable action with blocking UI\n *\n */\nexport const useConfirmableBlocker = (\n blockerId: string,\n config: ConfirmableBlockerConfig\n): UseConfirmableBlockerReturn => {\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n\n useBlocker(\n blockerId,\n {\n ...config,\n reason: config.reason ?? config.confirmMessage,\n },\n isDialogOpen || isExecuting\n );\n\n const execute = useCallback(() => {\n setIsDialogOpen(true);\n }, []);\n\n const onConfirm = useCallback(async () => {\n setIsDialogOpen(false);\n setIsExecuting(true);\n\n try {\n await config.onConfirm();\n } catch (error) {\n console.error(\"[UIBlocking] Confirmable action error:\", error);\n throw error;\n } finally {\n setIsExecuting(false);\n }\n }, [config]);\n\n const onCancel = useCallback(() => {\n setIsDialogOpen(false);\n config.onCancel?.();\n }, [config]);\n\n return {\n execute,\n isDialogOpen,\n isExecuting,\n confirmConfig: {\n title: config.confirmTitle ?? \"Confirm Action\",\n message: config.confirmMessage,\n confirmText: config.confirmButtonText ?? \"Confirm\",\n cancelText: config.cancelButtonText ?? \"Cancel\",\n },\n onConfirm,\n onCancel,\n };\n};\n","import { useUIBlockingStore } from \"../../store\";\n\n/**\n * Hook to check if a scope is currently blocked\n *\n * Returns a boolean indicating whether the specified scope(s) are blocked.\n * Automatically re-renders when the blocking state changes.\n *\n * @param scope - Scope or array of scopes to check (default: \"global\")\n * @returns True if any of the specified scopes are blocked\n *\n */\nexport const useIsBlocked = (scope?: string | ReadonlyArray<string>): boolean => {\n return useUIBlockingStore((state) => state.isBlocked(scope));\n};\n","import type { BlockingSchedule } from \"./useScheduledBlocker.types\";\n\n/**\n * Maximum safe timeout value for setTimeout in JavaScript (~24.8 days).\n * Values larger than this will cause setTimeout to trigger immediately.\n */\nexport const MAX_TIMEOUT_MS = 2147483647;\n\n/**\n * Validates a date value and converts it to timestamp.\n *\n * @param date - Date value as string, Date object, or number\n * @returns Timestamp in milliseconds, or NaN if invalid\n */\nexport function parseDate(date: string | Date | number): number {\n return new Date(date).getTime();\n}\n\n/**\n * Validates if a timestamp is valid (not NaN).\n *\n * @param timestamp - Timestamp to validate\n * @returns True if valid, false otherwise\n */\nexport function isValidTimestamp(timestamp: number): boolean {\n return !Number.isNaN(timestamp);\n}\n\n/**\n * Calculates the end time based on schedule configuration.\n * Duration takes precedence over end time if both are provided.\n *\n * @param schedule - Blocking schedule configuration\n * @param startTime - Start time in milliseconds\n * @returns End time in milliseconds, or undefined if not specified\n */\nexport function calculateEndTime(\n schedule: BlockingSchedule,\n startTime: number\n): number | undefined {\n if (schedule.duration) {\n return startTime + schedule.duration;\n }\n\n if (schedule.end) {\n const endTime = parseDate(schedule.end);\n return isValidTimestamp(endTime) ? endTime : undefined;\n }\n\n return undefined;\n}\n\n/**\n * Validates if a delay is within the safe setTimeout range.\n *\n * @param delay - Delay in milliseconds\n * @returns True if delay is safe to use with setTimeout\n */\nexport function isSafeTimeout(delay: number): boolean {\n return delay > 0 && delay <= MAX_TIMEOUT_MS;\n}\n\n/**\n * Determines if the current time is within a blocking schedule.\n *\n * @param startTime - Schedule start time in milliseconds\n * @param endTime - Schedule end time in milliseconds (optional)\n * @param now - Current time in milliseconds\n * @returns True if currently in the blocking period\n */\nexport function isInBlockingPeriod(\n startTime: number,\n endTime: number | undefined,\n now: number\n): boolean {\n if (startTime > now) {\n return false; // Not started yet\n }\n\n if (!endTime) {\n return true; // No end time means block indefinitely\n }\n\n return endTime > now; // Check if end time hasn't passed\n}\n\n/**\n * Checks if a schedule is completely in the past.\n *\n * @param startTime - Schedule start time in milliseconds\n * @param endTime - Schedule end time in milliseconds (optional)\n * @param now - Current time in milliseconds\n * @returns True if the schedule is in the past\n */\nexport function isScheduleInPast(\n startTime: number,\n endTime: number | undefined,\n now: number\n): boolean {\n if (!endTime) {\n return false; // No end time means it can still be active\n }\n\n return endTime <= now && startTime <= now;\n}\n","import { useCallback, useEffect, useRef } from \"react\";\nimport { useUIBlockingStore } from \"../../store\";\nimport { createBlockerConfig } from \"../useBlocker\";\nimport { useConfigRef } from \"../useConfigRef\";\nimport { ScheduledBlockerConfig } from \"./useScheduledBlocker.types\";\nimport {\n MAX_TIMEOUT_MS,\n calculateEndTime,\n isInBlockingPeriod,\n isSafeTimeout,\n isScheduleInPast,\n isValidTimestamp,\n parseDate,\n} from \"./useScheduledBlocker.utils\";\n\n/**\n * Hook for scheduling UI blocking for a specific time period.\n *\n * Blocks UI during a scheduled maintenance window or time period.\n * Supports start/end times as Date objects, ISO strings, or timestamps.\n * If both duration and end are provided, duration takes precedence.\n *\n * @param blockerId - Unique identifier for this blocker\n * @param config - Configuration including schedule and callbacks\n *\n */\nexport const useScheduledBlocker = (blockerId: string, config: ScheduledBlockerConfig): void => {\n const { addBlocker, removeBlocker } = useUIBlockingStore((state) => ({\n addBlocker: state.addBlocker,\n removeBlocker: state.removeBlocker,\n }));\n const timeoutsRef = useRef<{\n start?: ReturnType<typeof setTimeout>;\n end?: ReturnType<typeof setTimeout>;\n }>({});\n const configRef = useConfigRef(config);\n\n const cleanup = useCallback(() => {\n if (timeoutsRef.current.start) {\n clearTimeout(timeoutsRef.current.start);\n timeoutsRef.current.start = undefined;\n }\n if (timeoutsRef.current.end) {\n clearTimeout(timeoutsRef.current.end);\n timeoutsRef.current.end = undefined;\n }\n }, []);\n\n useEffect(() => {\n const currentConfig = configRef.current;\n const startTime = parseDate(currentConfig.schedule.start);\n const now = Date.now();\n\n // Validate start time\n if (!isValidTimestamp(startTime)) {\n console.error(`[UIBlocking] Invalid start time for blocker \"${blockerId}\"`);\n return;\n }\n\n const scheduleEnd = (): void => {\n const endTime = calculateEndTime(currentConfig.schedule, startTime);\n if (!endTime) {\n return;\n }\n\n const remainingTime = endTime - Date.now();\n if (remainingTime > 0) {\n timeoutsRef.current.end = setTimeout(() => {\n configRef.current.onScheduleEnd?.();\n removeBlocker(blockerId);\n }, remainingTime);\n }\n };\n\n const startBlocking = (): void => {\n const cfg = configRef.current;\n addBlocker(blockerId, createBlockerConfig(cfg));\n cfg.onScheduleStart?.();\n scheduleEnd();\n };\n\n // If start time hasn't arrived yet\n if (startTime > now) {\n const delay = startTime - now;\n\n if (!isSafeTimeout(delay)) {\n console.warn(\n `[UIBlocking] Schedule delay exceeds maximum timeout (${MAX_TIMEOUT_MS.toString()}ms) for blocker \"${blockerId}\"`\n );\n return;\n }\n\n timeoutsRef.current.start = setTimeout(startBlocking, delay);\n }\n // If already in blocking period or should start immediately\n else {\n const endTime = calculateEndTime(currentConfig.schedule, startTime);\n\n if (isInBlockingPeriod(startTime, endTime, now)) {\n startBlocking();\n } else if (isScheduleInPast(startTime, endTime, now)) {\n console.warn(`[UIBlocking] Schedule is in the past for blocker \"${blockerId}\"`);\n }\n }\n\n return (): void => {\n cleanup();\n removeBlocker(blockerId);\n };\n // configRef is stable and doesn't need to be in dependencies\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [blockerId, addBlocker, removeBlocker, cleanup]);\n};\n","import { AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport { MiddlewareContext } from \"./middleware.types\";\n\nconst capitalizeFirst = (str: string): string => {\n return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\nexport const buildEventData = (context: MiddlewareContext): AnalyticsEventData => {\n const eventData: AnalyticsEventData = {\n blocker_id: context.blockerId,\n };\n const { scope, reason, priority } = context.config ?? {};\n\n if (scope !== undefined) {\n eventData.scope = Array.isArray(scope) ? scope.join(\",\") : scope;\n }\n\n if (reason !== undefined) {\n eventData.reason = reason;\n }\n\n if (priority !== undefined) {\n eventData.priority = priority;\n }\n\n return eventData;\n};\n\nexport const trackGoogleAnalytics = (action: string, eventData: AnalyticsEventData): void => {\n if (window.gtag) {\n window.gtag(\"event\", `ui_blocking_${action}`, eventData);\n }\n};\n\nexport const trackMixpanel = (action: string, eventData: AnalyticsEventData): void => {\n if (window.mixpanel) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.mixpanel.track(eventName, eventData);\n }\n};\n\nexport const trackAmplitude = (action: string, eventData: AnalyticsEventData): void => {\n if (window.amplitude) {\n const eventName = `UI Blocking ${action.split(\"_\").map(capitalizeFirst).join(\" \")}`;\n\n window.amplitude.track(eventName, eventData);\n }\n};\n\nexport const trackCustom = (\n trackFn: (event: string, data: AnalyticsEventData) => void,\n action: string,\n eventData: AnalyticsEventData\n): void => {\n try {\n trackFn(`ui_blocking_${action}`, eventData);\n } catch {\n // Silently ignore errors in custom tracker\n }\n};\n","import { AnalyticsConfig, AnalyticsEventData } from \"./analyticsMiddleware.types\";\nimport {\n buildEventData,\n trackAmplitude,\n trackCustom,\n trackGoogleAnalytics,\n trackMixpanel,\n} from \"./analyticsMiddleware.utils\";\nimport { Middleware, MiddlewareContext } from \"./middleware.types\";\n\nconst trackEvent = (\n config: AnalyticsConfig,\n context: MiddlewareContext,\n eventData: AnalyticsEventData\n): void => {\n if (\"provider\" in config) {\n switch (config.provider) {\n case \"ga\":\n trackGoogleAnalytics(context.action, eventData);\n return;\n case \"mixpanel\":\n trackMixpanel(context.action, eventData);\n return;\n case \"amplitude\":\n trackAmplitude(context.action, eventData);\n return;\n }\n }\n\n if (\"track\" in config) {\n trackCustom(config.track, context.action, eventData);\n }\n};\n\nexport const createAnalyticsMiddleware = (config: AnalyticsConfig): Middleware => {\n return (context) => {\n const eventData = buildEventData(context);\n\n trackEvent(config, context, eventData);\n };\n};\n","import { MiddlewareBlockerConfig, MiddlewareContext } from \"./middleware.types\";\n\nexport const getActionEmoji = (action: string): string => {\n switch (action) {\n case \"add\":\n return \"➕\";\n case \"remove\":\n return \"➖\";\n case \"update\":\n return \"🔄\";\n case \"cancel\":\n return \"❌\";\n case \"timeout\":\n return \"⏱️\";\n default:\n return \"❓\";\n }\n};\n\nconst extractConfigDetails = (config?: MiddlewareBlockerConfig): Record<string, unknown> => {\n if (!config) {\n return {};\n }\n\n const { scope, reason, priority } = config;\n\n const details: Record<string, unknown> = {\n scope,\n reason,\n };\n\n if (priority !== undefined) {\n details.priority = priority;\n }\n\n return details;\n};\n\nexport const formatLogData = (context: MiddlewareContext): Record<string, unknown> => {\n const configDetails = extractConfigDetails(context.config);\n\n if (context.prevState) {\n return {\n config: configDetails,\n prevState: extractConfigDetails(context.prevState),\n };\n }\n\n return configDetails;\n};\n","import { formatLogData, getActionEmoji } from \"./loggerMiddleware.utils\";\nimport { Middleware } from \"./middleware.types\";\n\nconst LOG_PREFIX = \"[UIBlocking]\";\n\nexport const loggerMiddleware: Middleware = (context) => {\n const emoji = getActionEmoji(context.action);\n const logData = formatLogData(context);\n\n console.log(LOG_PREFIX, emoji, context.blockerId, logData);\n};\n","export const handleAddAction = (\n blockerId: string,\n timestamp: number,\n blockStartTimes: Map<string, number>\n): void => {\n blockStartTimes.set(blockerId, timestamp);\n};\n","import { Middleware, MiddlewareContext } from \"./middleware.types\";\nimport { PerformanceConfig } from \"./performanceMiddleware.types\";\nimport { handleAddAction } from \"./performanceMiddleware.utils\";\n\nconst DEFAULT_SLOW_BLOCK_THRESHOLD = 3000;\nconst ACTION_ADD = \"add\";\nconst ACTION_REMOVE = \"remove\";\n\nconst handleRemoveAction = (\n context: MiddlewareContext,\n blockStartTimes: Map<string, number>,\n slowBlockThreshold: number,\n onSlowBlock?: (blockerId: string, duration: number) => void\n): void => {\n const startTime = blockStartTimes.get(context.blockerId);\n\n if (startTime === undefined) {\n return;\n }\n\n const duration = context.timestamp - startTime;\n blockStartTimes.delete(context.blockerId);\n\n if (duration >= slowBlockThreshold) {\n console.warn(\n `[UIBlocking] Slow block detected: \"${context.blockerId}\" took ${duration.toString()}ms`\n );\n\n onSlowBlock?.(context.blockerId, duration);\n }\n};\n\nexport const createPerformanceMiddleware = (config: PerformanceConfig = {}): Middleware => {\n const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;\n const blockStartTimes = new Map<string, number>();\n\n return (context) => {\n if (context.action === ACTION_ADD) {\n handleAddAction(context.blockerId, context.timestamp, blockStartTimes);\n } else if (context.action === ACTION_REMOVE) {\n handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);\n }\n };\n};\n"]}