@okyrychenko-dev/react-action-guard 0.6.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,12 @@
1
- import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
2
1
  import { createStoreProvider, createShallowStore } from '@okyrychenko-dev/react-zustand-toolkit';
3
- export { createShallowStore } from '@okyrychenko-dev/react-zustand-toolkit';
4
2
  import { useStore } from 'zustand';
5
3
  import 'zustand/react/shallow';
6
4
  import { jsx } from 'react/jsx-runtime';
7
5
  import { useShallow } from 'zustand/shallow';
8
6
  import { devtools } from 'zustand/middleware';
7
+ import { useRef, useCallback, useEffect, useMemo, useState } from 'react';
9
8
 
10
- // src/hooks/useAsyncAction/useAsyncAction.ts
9
+ // src/context/UIBlockingContext.tsx
11
10
 
12
11
  // src/store/uiBlockingStore.constants.ts
13
12
  var DEFAULT_SCOPE = "global";
@@ -23,16 +22,18 @@ function normalizeScopeToArray(scope) {
23
22
  }
24
23
 
25
24
  // src/store/uiBlockingStore.actions.ts
26
- var normalizePriority = (priority, fallback = DEFAULT_PRIORITY) => {
25
+ function normalizePriority(priority, fallback = DEFAULT_PRIORITY) {
27
26
  return priority !== void 0 ? Math.max(0, priority) : fallback;
28
- };
29
- var createMiddlewareContext = (action, blockerId, config, prevState) => ({
30
- action,
31
- blockerId,
32
- config,
33
- timestamp: Date.now(),
34
- ...prevState && { prevState }
35
- });
27
+ }
28
+ function createMiddlewareContext(action, blockerId, config, prevState) {
29
+ return {
30
+ action,
31
+ blockerId,
32
+ config,
33
+ timestamp: Date.now(),
34
+ ...prevState && { prevState }
35
+ };
36
+ }
36
37
  var createUIBlockingActions = (set, get) => ({
37
38
  // State
38
39
  activeBlockers: /* @__PURE__ */ new Map(),
@@ -53,25 +54,24 @@ var createUIBlockingActions = (set, get) => ({
53
54
  },
54
55
  runMiddlewares: async (context) => {
55
56
  const { middlewares } = get();
56
- for (const [name, middleware] of middlewares) {
57
+ for (const [, middleware] of middlewares) {
57
58
  try {
58
59
  await middleware(context);
59
- } catch (error) {
60
- console.error(`[UIBlocking] Middleware "${name}" error:`, error);
60
+ } catch {
61
61
  }
62
62
  }
63
63
  },
64
64
  // Actions
65
65
  /**
66
66
  * Adds a new blocker or overwrites an existing one.
67
- *
67
+ *
68
68
  * If a blocker with the same ID already exists, it will be replaced and any
69
69
  * existing timeout will be cleared. The blocker is active immediately after
70
70
  * being added. Triggers middleware with 'add' action.
71
- *
71
+ *
72
72
  * @param id - Unique identifier for the blocker. Must be unique within the store.
73
73
  * @param config - Blocker configuration. All fields are optional with defaults applied.
74
- *
74
+ *
75
75
  * @example
76
76
  * Basic usage
77
77
  * ```ts
@@ -81,7 +81,7 @@ var createUIBlockingActions = (set, get) => ({
81
81
  * priority: 70
82
82
  * });
83
83
  * ```
84
- *
84
+ *
85
85
  * @example
86
86
  * With timeout
87
87
  * ```ts
@@ -92,7 +92,7 @@ var createUIBlockingActions = (set, get) => ({
92
92
  * onTimeout: (id) => console.warn(`${id} timed out`)
93
93
  * });
94
94
  * ```
95
- *
95
+ *
96
96
  * @see {@link removeBlocker} to remove a blocker
97
97
  * @see {@link updateBlocker} to update an existing blocker
98
98
  */
@@ -143,13 +143,13 @@ var createUIBlockingActions = (set, get) => ({
143
143
  },
144
144
  /**
145
145
  * Updates an existing blocker or creates it if it doesn't exist.
146
- *
146
+ *
147
147
  * Merges the provided configuration with existing blocker data. If the timeout
148
148
  * is changed, the timeout timer is restarted. Triggers middleware with 'update' action.
149
- *
149
+ *
150
150
  * @param id - Identifier of the blocker to update
151
151
  * @param config - Partial configuration to merge with existing blocker data
152
- *
152
+ *
153
153
  * @example
154
154
  * Update blocker reason
155
155
  * ```ts
@@ -157,7 +157,7 @@ var createUIBlockingActions = (set, get) => ({
157
157
  * reason: 'Saving additional data...'
158
158
  * });
159
159
  * ```
160
- *
160
+ *
161
161
  * @example
162
162
  * Extend timeout
163
163
  * ```ts
@@ -165,7 +165,7 @@ var createUIBlockingActions = (set, get) => ({
165
165
  * timeout: 60000 // Extend to 60 seconds
166
166
  * });
167
167
  * ```
168
- *
168
+ *
169
169
  * @see {@link addBlocker} to create a new blocker
170
170
  */
171
171
  updateBlocker: (id, config = {}) => {
@@ -241,12 +241,12 @@ var createUIBlockingActions = (set, get) => ({
241
241
  },
242
242
  /**
243
243
  * Removes a blocker from the store.
244
- *
244
+ *
245
245
  * Clears any associated timeout and triggers middleware with 'remove' action.
246
246
  * Safe to call even if the blocker doesn't exist (no-op in that case).
247
- *
247
+ *
248
248
  * @param id - Identifier of the blocker to remove
249
- *
249
+ *
250
250
  * @example
251
251
  * ```ts
252
252
  * // Add and remove a blocker
@@ -254,7 +254,7 @@ var createUIBlockingActions = (set, get) => ({
254
254
  * // ... later
255
255
  * store.getState().removeBlocker('temp');
256
256
  * ```
257
- *
257
+ *
258
258
  * @see {@link addBlocker} to add a blocker
259
259
  * @see {@link clearAllBlockers} to remove all blockers at once
260
260
  */
@@ -274,28 +274,28 @@ var createUIBlockingActions = (set, get) => ({
274
274
  },
275
275
  /**
276
276
  * Checks if one or more scopes are currently blocked.
277
- *
277
+ *
278
278
  * Returns true if ANY blocker affects the specified scope(s). Global scope
279
279
  * blockers will cause this to return true for any scope check.
280
- *
280
+ *
281
281
  * @param scope - Scope(s) to check. Defaults to "global" if not specified.
282
282
  * Can be a single scope string or array of scopes.
283
283
  * @returns true if any of the specified scopes are currently blocked
284
- *
284
+ *
285
285
  * @example
286
286
  * Single scope check
287
287
  * ```ts
288
288
  * const isFormBlocked = store.getState().isBlocked('form');
289
289
  * console.log(isFormBlocked); // true or false
290
290
  * ```
291
- *
291
+ *
292
292
  * @example
293
293
  * Multiple scopes check
294
294
  * ```ts
295
295
  * const isBlocked = store.getState().isBlocked(['form', 'navigation']);
296
296
  * // Returns true if either 'form' OR 'navigation' has active blockers
297
297
  * ```
298
- *
298
+ *
299
299
  * @see {@link getBlockingInfo} for detailed blocker information
300
300
  */
301
301
  isBlocked: (scope = DEFAULT_SCOPE) => {
@@ -314,14 +314,14 @@ var createUIBlockingActions = (set, get) => ({
314
314
  },
315
315
  /**
316
316
  * Gets detailed information about all blockers affecting a specific scope.
317
- *
317
+ *
318
318
  * Returns an array of blocker objects sorted by priority (highest first).
319
319
  * Includes global blockers and scope-specific blockers. Each blocker object
320
320
  * contains id, reason, priority, scope, timestamp, and timeout information.
321
- *
321
+ *
322
322
  * @param scope - Scope to get blocking information for
323
323
  * @returns Array of {@link BlockerInfo} objects, sorted by priority descending
324
- *
324
+ *
325
325
  * @example
326
326
  * Display blocker details
327
327
  * ```ts
@@ -331,7 +331,7 @@ var createUIBlockingActions = (set, get) => ({
331
331
  * console.log(`Priority: ${blocker.priority}`);
332
332
  * });
333
333
  * ```
334
- *
334
+ *
335
335
  * @example
336
336
  * Get highest priority blocker
337
337
  * ```ts
@@ -341,7 +341,7 @@ var createUIBlockingActions = (set, get) => ({
341
341
  * console.log(`Top blocker: ${topBlocker.reason}`);
342
342
  * }
343
343
  * ```
344
- *
344
+ *
345
345
  * @see {@link isBlocked} for a simple boolean check
346
346
  * @see {@link BlockerInfo} for the structure of returned objects
347
347
  */
@@ -351,23 +351,24 @@ var createUIBlockingActions = (set, get) => ({
351
351
  for (const [id, blocker] of activeBlockers) {
352
352
  const blockerScopes = normalizeScopeToArray(blocker.scope);
353
353
  if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {
354
- blockers.push({ id, ...blocker });
354
+ const { timeoutId: _timeoutId, ...publicBlocker } = blocker;
355
+ blockers.push({ id, ...publicBlocker });
355
356
  }
356
357
  }
357
358
  return blockers.sort((a, b) => b.priority - a.priority);
358
359
  },
359
360
  /**
360
361
  * Removes all active blockers from the store.
361
- *
362
+ *
362
363
  * Clears all timeouts and triggers middleware with 'clear' action.
363
364
  * Useful for resetting the blocking state.
364
- *
365
+ *
365
366
  * @example
366
367
  * ```ts
367
368
  * // Clear all blockers on logout
368
369
  * store.getState().clearAllBlockers();
369
370
  * ```
370
- *
371
+ *
371
372
  * @see {@link clearBlockersForScope} to clear blockers for a specific scope
372
373
  */
373
374
  clearAllBlockers: () => {
@@ -390,26 +391,26 @@ var createUIBlockingActions = (set, get) => ({
390
391
  },
391
392
  /**
392
393
  * Removes all blockers that affect a specific scope.
393
- *
394
+ *
394
395
  * Clears timeouts for removed blockers and triggers middleware with
395
396
  * 'clear_scope' action. Global scope blockers are NOT cleared by this method.
396
- *
397
+ *
397
398
  * @param scope - Scope to clear blockers for
398
- *
399
+ *
399
400
  * @example
400
401
  * Clear form blockers
401
402
  * ```ts
402
403
  * // Clear all blockers affecting 'form' scope
403
404
  * store.getState().clearBlockersForScope('form');
404
405
  * ```
405
- *
406
+ *
406
407
  * @example
407
408
  * Clear on navigation
408
409
  * ```ts
409
410
  * // Clear navigation blockers when user navigates away
410
411
  * store.getState().clearBlockersForScope('navigation');
411
412
  * ```
412
- *
413
+ *
413
414
  * @see {@link clearAllBlockers} to clear all blockers
414
415
  */
415
416
  clearBlockersForScope: (scope) => {
@@ -447,18 +448,24 @@ var createUIBlockingActions = (set, get) => ({
447
448
  });
448
449
  var {
449
450
  Provider: BaseUIBlockingProvider,
450
- useContext: useUIBlockingContext,
451
- useContextStore: useUIBlockingStoreFromContext,
451
+ useContextStoreApi: useUIBlockingContext,
452
+ useContextStore: baseUseUIBlockingStoreFromContext,
452
453
  useIsInsideProvider: useIsInsideUIBlockingProvider,
453
- useOptionalContext
454
+ useContextStoreOptional: useOptionalUIBlockingContext
454
455
  } = createStoreProvider(createUIBlockingActions, "UIBlocking");
456
+ function useUIBlockingStoreFromContext(selector, equalityFn) {
457
+ if (selector) {
458
+ return baseUseUIBlockingStoreFromContext(selector, equalityFn);
459
+ }
460
+ return baseUseUIBlockingStoreFromContext();
461
+ }
455
462
  function UIBlockingProvider({
456
463
  children,
457
464
  enableDevtools = process.env.NODE_ENV === "development",
458
465
  devtoolsName = "UIBlocking",
459
466
  middlewares = []
460
467
  }) {
461
- const handleStoreCreate = (store) => {
468
+ const handleStoreInit = (store) => {
462
469
  middlewares.forEach((mw, index) => {
463
470
  store.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
464
471
  });
@@ -468,7 +475,7 @@ function UIBlockingProvider({
468
475
  {
469
476
  enableDevtools,
470
477
  devtoolsName,
471
- onStoreCreate: handleStoreCreate,
478
+ onStoreInit: handleStoreInit,
472
479
  children
473
480
  }
474
481
  );
@@ -481,24 +488,31 @@ var devtoolsConfig = {
481
488
  };
482
489
 
483
490
  // src/store/uiBlockingStore.store.ts
484
- var { useStore: useUIBlockingStore, useStoreApi: uiBlockingStoreApi } = createShallowStore(devtools(createUIBlockingActions, devtoolsConfig));
491
+ var storeBindings = createShallowStore(
492
+ devtools(createUIBlockingActions, devtoolsConfig)
493
+ );
494
+ var uiBlockingStoreApi = storeBindings.useStoreApi;
495
+ function useUIBlockingStore(selector, equalityFn) {
496
+ if (selector) {
497
+ return storeBindings.useStore(selector, equalityFn);
498
+ }
499
+ return storeBindings.useStore();
500
+ }
485
501
 
486
502
  // src/context/useResolvedStore.ts
487
503
  function useOptionalContextStore() {
488
- return useOptionalContext();
504
+ return useOptionalUIBlockingContext();
489
505
  }
490
- function useResolvedStore() {
506
+ function useResolvedStoreApi() {
491
507
  const contextStore = useOptionalContextStore();
492
508
  return contextStore ?? uiBlockingStoreApi;
493
509
  }
494
- function useResolvedStoreWithSelector(selector) {
495
- const store = useResolvedStore();
510
+ function useResolvedValue(selector) {
511
+ const store = useResolvedStoreApi();
496
512
  return useStore(store, useShallow(selector));
497
513
  }
498
-
499
- // src/hooks/useAsyncAction/useAsyncAction.ts
500
- var useAsyncAction = (actionId, scope, options) => {
501
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
514
+ function useAsyncAction(actionId, scope, options) {
515
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
502
516
  addBlocker: state.addBlocker,
503
517
  removeBlocker: state.removeBlocker
504
518
  }));
@@ -523,22 +537,36 @@ var useAsyncAction = (actionId, scope, options) => {
523
537
  [actionId, scope, options?.timeout, options?.onTimeout, addBlocker, removeBlocker]
524
538
  );
525
539
  return executeWithBlocking;
526
- };
527
- var useBlocker = (blockerId, config, isActive = true) => {
528
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
540
+ }
541
+ function useBlocker(blockerId, config, isActive = true) {
542
+ const { addBlocker, removeBlocker, updateBlocker } = useResolvedValue((state) => ({
529
543
  addBlocker: state.addBlocker,
530
- removeBlocker: state.removeBlocker
544
+ removeBlocker: state.removeBlocker,
545
+ updateBlocker: state.updateBlocker
531
546
  }));
547
+ const lastConfigRef = useRef(null);
532
548
  useEffect(() => {
533
549
  if (!isActive || !blockerId) {
534
550
  return;
535
551
  }
536
552
  addBlocker(blockerId, config);
553
+ lastConfigRef.current = config;
537
554
  return () => {
555
+ lastConfigRef.current = null;
538
556
  removeBlocker(blockerId);
539
557
  };
540
558
  }, [blockerId, isActive, addBlocker, removeBlocker]);
541
- };
559
+ useEffect(() => {
560
+ if (!isActive || !blockerId) {
561
+ return;
562
+ }
563
+ if (lastConfigRef.current === config) {
564
+ return;
565
+ }
566
+ updateBlocker(blockerId, config);
567
+ lastConfigRef.current = config;
568
+ }, [blockerId, config, isActive, updateBlocker]);
569
+ }
542
570
 
543
571
  // src/hooks/useBlocker/useBlocker.utils.ts
544
572
  function createBlockerConfig(config) {
@@ -548,13 +576,13 @@ function createBlockerConfig(config) {
548
576
  priority: config.priority
549
577
  };
550
578
  }
551
- var useBlockingInfo = (scope = DEFAULT_SCOPE) => {
552
- const store = useResolvedStore();
579
+ function useBlockingInfo(scope = DEFAULT_SCOPE) {
580
+ const store = useResolvedStoreApi();
553
581
  const activeBlockers = useStore(store, (state) => state.activeBlockers);
554
582
  return useMemo(() => {
555
583
  return store.getState().getBlockingInfo(scope);
556
584
  }, [activeBlockers, scope, store]);
557
- };
585
+ }
558
586
  function useConfigRef(config) {
559
587
  const configRef = useRef(config);
560
588
  useEffect(() => {
@@ -564,8 +592,8 @@ function useConfigRef(config) {
564
592
  }
565
593
 
566
594
  // src/hooks/useConditionalBlocker/useConditionalBlocker.ts
567
- var useConditionalBlocker = (blockerId, config) => {
568
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
595
+ function useConditionalBlocker(blockerId, config) {
596
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
569
597
  addBlocker: state.addBlocker,
570
598
  removeBlocker: state.removeBlocker
571
599
  }));
@@ -600,8 +628,8 @@ var useConditionalBlocker = (blockerId, config) => {
600
628
  }
601
629
  };
602
630
  }, [checkCondition, blockerId, removeBlocker]);
603
- };
604
- var useConfirmableBlocker = (blockerId, config) => {
631
+ }
632
+ function useConfirmableBlocker(blockerId, config) {
605
633
  const [isDialogOpen, setIsDialogOpen] = useState(false);
606
634
  const [isExecuting, setIsExecuting] = useState(false);
607
635
  useBlocker(
@@ -620,9 +648,6 @@ var useConfirmableBlocker = (blockerId, config) => {
620
648
  setIsExecuting(true);
621
649
  try {
622
650
  await config.onConfirm();
623
- } catch (error) {
624
- console.error("[UIBlocking] Confirmable action error:", error);
625
- throw error;
626
651
  } finally {
627
652
  setIsExecuting(false);
628
653
  }
@@ -644,12 +669,12 @@ var useConfirmableBlocker = (blockerId, config) => {
644
669
  onConfirm,
645
670
  onCancel
646
671
  };
647
- };
672
+ }
648
673
 
649
674
  // src/hooks/useIsBlocked/useIsBlocked.ts
650
- var useIsBlocked = (scope) => {
651
- return useResolvedStoreWithSelector((state) => state.isBlocked(scope));
652
- };
675
+ function useIsBlocked(scope) {
676
+ return useResolvedValue((state) => state.isBlocked(scope));
677
+ }
653
678
 
654
679
  // src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
655
680
  var MAX_TIMEOUT_MS = 2147483647;
@@ -689,8 +714,8 @@ function isScheduleInPast(startTime, endTime, now) {
689
714
  }
690
715
 
691
716
  // src/hooks/useScheduledBlocker/useScheduledBlocker.ts
692
- var useScheduledBlocker = (blockerId, config) => {
693
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
717
+ function useScheduledBlocker(blockerId, config) {
718
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
694
719
  addBlocker: state.addBlocker,
695
720
  removeBlocker: state.removeBlocker
696
721
  }));
@@ -711,7 +736,6 @@ var useScheduledBlocker = (blockerId, config) => {
711
736
  const startTime = parseDate(currentConfig.schedule.start);
712
737
  const now = Date.now();
713
738
  if (!isValidTimestamp(startTime)) {
714
- console.error(`[UIBlocking] Invalid start time for blocker "${blockerId}"`);
715
739
  return;
716
740
  }
717
741
  const scheduleEnd = () => {
@@ -736,9 +760,6 @@ var useScheduledBlocker = (blockerId, config) => {
736
760
  if (startTime > now) {
737
761
  const delay = startTime - now;
738
762
  if (!isSafeTimeout(delay)) {
739
- console.warn(
740
- `[UIBlocking] Schedule delay exceeds maximum timeout (${MAX_TIMEOUT_MS.toString()}ms) for blocker "${blockerId}"`
741
- );
742
763
  return;
743
764
  }
744
765
  timeoutsRef.current.start = setTimeout(startBlocking, delay);
@@ -747,7 +768,7 @@ var useScheduledBlocker = (blockerId, config) => {
747
768
  if (isInBlockingPeriod(startTime, endTime, now)) {
748
769
  startBlocking();
749
770
  } else if (isScheduleInPast(startTime, endTime, now)) {
750
- console.warn(`[UIBlocking] Schedule is in the past for blocker "${blockerId}"`);
771
+ return;
751
772
  }
752
773
  }
753
774
  return () => {
@@ -755,13 +776,19 @@ var useScheduledBlocker = (blockerId, config) => {
755
776
  removeBlocker(blockerId);
756
777
  };
757
778
  }, [blockerId, addBlocker, removeBlocker, cleanup]);
758
- };
779
+ }
759
780
 
760
781
  // src/middleware/analyticsMiddleware.utils.ts
761
- var capitalizeFirst = (str) => {
782
+ function capitalizeFirst(str) {
762
783
  return str.charAt(0).toUpperCase() + str.slice(1);
763
- };
764
- var buildEventData = (context) => {
784
+ }
785
+ function getAnalyticsWindow() {
786
+ if (typeof window === "undefined") {
787
+ return void 0;
788
+ }
789
+ return window;
790
+ }
791
+ function buildEventData(context) {
765
792
  const eventData = {
766
793
  blocker_id: context.blockerId
767
794
  };
@@ -776,33 +803,36 @@ var buildEventData = (context) => {
776
803
  eventData.priority = priority;
777
804
  }
778
805
  return eventData;
779
- };
780
- var trackGoogleAnalytics = (action, eventData) => {
781
- if (window.gtag) {
782
- window.gtag("event", `ui_blocking_${action}`, eventData);
806
+ }
807
+ function trackGoogleAnalytics(action, eventData) {
808
+ const analyticsWindow = getAnalyticsWindow();
809
+ if (analyticsWindow?.gtag) {
810
+ analyticsWindow.gtag("event", `ui_blocking_${action}`, eventData);
783
811
  }
784
- };
785
- var trackMixpanel = (action, eventData) => {
786
- if (window.mixpanel) {
812
+ }
813
+ function trackMixpanel(action, eventData) {
814
+ const analyticsWindow = getAnalyticsWindow();
815
+ if (analyticsWindow?.mixpanel) {
787
816
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
788
- window.mixpanel.track(eventName, eventData);
817
+ analyticsWindow.mixpanel.track(eventName, eventData);
789
818
  }
790
- };
791
- var trackAmplitude = (action, eventData) => {
792
- if (window.amplitude) {
819
+ }
820
+ function trackAmplitude(action, eventData) {
821
+ const analyticsWindow = getAnalyticsWindow();
822
+ if (analyticsWindow?.amplitude) {
793
823
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
794
- window.amplitude.track(eventName, eventData);
824
+ analyticsWindow.amplitude.track(eventName, eventData);
795
825
  }
796
- };
797
- var trackCustom = (trackFn, action, eventData) => {
826
+ }
827
+ function trackCustom(trackFn, action, eventData) {
798
828
  try {
799
829
  trackFn(`ui_blocking_${action}`, eventData);
800
830
  } catch {
801
831
  }
802
- };
832
+ }
803
833
 
804
834
  // src/middleware/analyticsMiddleware.ts
805
- var trackEvent = (config, context, eventData) => {
835
+ function trackEvent(config, context, eventData) {
806
836
  if ("provider" in config) {
807
837
  switch (config.provider) {
808
838
  case "ga":
@@ -819,25 +849,31 @@ var trackEvent = (config, context, eventData) => {
819
849
  if ("track" in config) {
820
850
  trackCustom(config.track, context.action, eventData);
821
851
  }
822
- };
823
- var createAnalyticsMiddleware = (config) => {
852
+ }
853
+ function createAnalyticsMiddleware(config) {
824
854
  return (context) => {
825
855
  const eventData = buildEventData(context);
826
856
  trackEvent(config, context, eventData);
827
857
  };
828
- };
858
+ }
829
859
 
830
860
  // src/middleware/configureMiddleware.ts
861
+ var CONFIGURE_MIDDLEWARE_PREFIX = "middleware-";
831
862
  function configureMiddleware(middlewares) {
832
863
  const store = uiBlockingStoreApi.getState();
864
+ store.middlewares.forEach((_, name) => {
865
+ if (name.startsWith(CONFIGURE_MIDDLEWARE_PREFIX)) {
866
+ store.unregisterMiddleware(name);
867
+ }
868
+ });
833
869
  middlewares.forEach((middleware, index) => {
834
- const name = `middleware-${index.toString()}`;
870
+ const name = `${CONFIGURE_MIDDLEWARE_PREFIX}${index.toString()}`;
835
871
  store.registerMiddleware(name, middleware);
836
872
  });
837
873
  }
838
874
 
839
875
  // src/middleware/loggerMiddleware.utils.ts
840
- var getActionEmoji = (action) => {
876
+ function getActionEmoji(action) {
841
877
  switch (action) {
842
878
  case "add":
843
879
  return "\u2795";
@@ -854,8 +890,8 @@ var getActionEmoji = (action) => {
854
890
  default:
855
891
  return "\u2753";
856
892
  }
857
- };
858
- var extractConfigDetails = (config) => {
893
+ }
894
+ function extractConfigDetails(config) {
859
895
  if (!config) {
860
896
  return {};
861
897
  }
@@ -868,8 +904,8 @@ var extractConfigDetails = (config) => {
868
904
  details.priority = priority;
869
905
  }
870
906
  return details;
871
- };
872
- var formatLogData = (context) => {
907
+ }
908
+ function formatLogData(context) {
873
909
  const configDetails = extractConfigDetails(context.config);
874
910
  const logData = {};
875
911
  if (context.scope !== void 0) {
@@ -888,26 +924,26 @@ var formatLogData = (context) => {
888
924
  return configDetails;
889
925
  }
890
926
  return Object.keys(logData).length === 1 && logData.config ? configDetails : logData;
891
- };
927
+ }
892
928
 
893
929
  // src/middleware/loggerMiddleware.ts
894
930
  var LOG_PREFIX = "[UIBlocking]";
895
- var loggerMiddleware = (context) => {
931
+ function loggerMiddleware(context) {
896
932
  const emoji = getActionEmoji(context.action);
897
933
  const logData = formatLogData(context);
898
934
  console.log(LOG_PREFIX, emoji, context.blockerId, logData);
899
- };
935
+ }
900
936
 
901
937
  // src/middleware/performanceMiddleware.utils.ts
902
- var handleAddAction = (blockerId, timestamp, blockStartTimes) => {
938
+ function handleAddAction(blockerId, timestamp, blockStartTimes) {
903
939
  blockStartTimes.set(blockerId, timestamp);
904
- };
940
+ }
905
941
 
906
942
  // src/middleware/performanceMiddleware.ts
907
943
  var DEFAULT_SLOW_BLOCK_THRESHOLD = 3e3;
908
944
  var ACTION_ADD = "add";
909
945
  var ACTION_REMOVE = "remove";
910
- var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBlock) => {
946
+ function handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock) {
911
947
  const startTime = blockStartTimes.get(context.blockerId);
912
948
  if (startTime === void 0) {
913
949
  return;
@@ -915,13 +951,10 @@ var handleRemoveAction = (context, blockStartTimes, slowBlockThreshold, onSlowBl
915
951
  const duration = context.timestamp - startTime;
916
952
  blockStartTimes.delete(context.blockerId);
917
953
  if (duration >= slowBlockThreshold) {
918
- console.warn(
919
- `[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`
920
- );
921
954
  onSlowBlock?.(context.blockerId, duration);
922
955
  }
923
- };
924
- var createPerformanceMiddleware = (config = {}) => {
956
+ }
957
+ function createPerformanceMiddleware(config = {}) {
925
958
  const { slowBlockThreshold = DEFAULT_SLOW_BLOCK_THRESHOLD, onSlowBlock } = config;
926
959
  const blockStartTimes = /* @__PURE__ */ new Map();
927
960
  return (context) => {
@@ -931,7 +964,7 @@ var createPerformanceMiddleware = (config = {}) => {
931
964
  handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlowBlock);
932
965
  }
933
966
  };
934
- };
967
+ }
935
968
 
936
969
  // src/createTypedHooks.ts
937
970
  function createTypedHooks() {
@@ -949,6 +982,17 @@ function createTypedHooks() {
949
982
  };
950
983
  }
951
984
 
952
- export { UIBlockingProvider, configureMiddleware, createAnalyticsMiddleware, createPerformanceMiddleware, createTypedHooks, loggerMiddleware, uiBlockingStoreApi, useAsyncAction, useBlocker, useBlockingInfo, useConditionalBlocker, useConfirmableBlocker, useIsBlocked, useIsInsideUIBlockingProvider, useScheduledBlocker, useUIBlockingContext, useUIBlockingStore, useUIBlockingStoreFromContext };
985
+ // src/index.ts
986
+ function useOptionalContext() {
987
+ return useOptionalUIBlockingContext();
988
+ }
989
+ function useResolvedStore() {
990
+ return useResolvedStoreApi();
991
+ }
992
+ function useResolvedStoreWithSelector(selector) {
993
+ return useResolvedValue(selector);
994
+ }
995
+
996
+ export { UIBlockingProvider, configureMiddleware, createAnalyticsMiddleware, createPerformanceMiddleware, createTypedHooks, loggerMiddleware, uiBlockingStoreApi, useAsyncAction, useBlocker, useBlockingInfo, useConditionalBlocker, useConfirmableBlocker, useIsBlocked, useIsInsideUIBlockingProvider, useOptionalContext, useOptionalUIBlockingContext, useResolvedStore, useResolvedStoreApi, useResolvedStoreWithSelector, useResolvedValue, useScheduledBlocker, useUIBlockingContext, useUIBlockingStore, useUIBlockingStoreFromContext };
953
997
  //# sourceMappingURL=index.js.map
954
998
  //# sourceMappingURL=index.js.map