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