@okyrychenko-dev/react-action-guard 0.7.0 → 1.0.1

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";
@@ -55,11 +54,10 @@ var createUIBlockingActions = (set, get) => ({
55
54
  },
56
55
  runMiddlewares: async (context) => {
57
56
  const { middlewares } = get();
58
- for (const [name, middleware] of middlewares) {
57
+ for (const [, middleware] of middlewares) {
59
58
  try {
60
59
  await middleware(context);
61
- } catch (error) {
62
- console.error(`[UIBlocking] Middleware "${name}" error:`, error);
60
+ } catch {
63
61
  }
64
62
  }
65
63
  },
@@ -353,7 +351,8 @@ var createUIBlockingActions = (set, get) => ({
353
351
  for (const [id, blocker] of activeBlockers) {
354
352
  const blockerScopes = normalizeScopeToArray(blocker.scope);
355
353
  if (blocker.scope === DEFAULT_SCOPE || blockerScopes.includes(scope)) {
356
- blockers.push({ id, ...blocker });
354
+ const { timeoutId: _timeoutId, ...publicBlocker } = blocker;
355
+ blockers.push({ id, ...publicBlocker });
357
356
  }
358
357
  }
359
358
  return blockers.sort((a, b) => b.priority - a.priority);
@@ -449,18 +448,24 @@ var createUIBlockingActions = (set, get) => ({
449
448
  });
450
449
  var {
451
450
  Provider: BaseUIBlockingProvider,
452
- useContext: useUIBlockingContext,
453
- useContextStore: useUIBlockingStoreFromContext,
451
+ useContextStoreApi: useUIBlockingContext,
452
+ useContextStore: baseUseUIBlockingStoreFromContext,
454
453
  useIsInsideProvider: useIsInsideUIBlockingProvider,
455
- useOptionalContext
454
+ useContextStoreOptional: useOptionalUIBlockingContext
456
455
  } = createStoreProvider(createUIBlockingActions, "UIBlocking");
456
+ function useUIBlockingStoreFromContext(selector, equalityFn) {
457
+ if (selector) {
458
+ return baseUseUIBlockingStoreFromContext(selector, equalityFn);
459
+ }
460
+ return baseUseUIBlockingStoreFromContext();
461
+ }
457
462
  function UIBlockingProvider({
458
463
  children,
459
464
  enableDevtools = process.env.NODE_ENV === "development",
460
465
  devtoolsName = "UIBlocking",
461
466
  middlewares = []
462
467
  }) {
463
- const handleStoreCreate = (store) => {
468
+ const handleStoreInit = (store) => {
464
469
  middlewares.forEach((mw, index) => {
465
470
  store.getState().registerMiddleware(`provider-middleware-${index.toString()}`, mw);
466
471
  });
@@ -470,7 +475,7 @@ function UIBlockingProvider({
470
475
  {
471
476
  enableDevtools,
472
477
  devtoolsName,
473
- onStoreCreate: handleStoreCreate,
478
+ onStoreInit: handleStoreInit,
474
479
  children
475
480
  }
476
481
  );
@@ -483,24 +488,31 @@ var devtoolsConfig = {
483
488
  };
484
489
 
485
490
  // src/store/uiBlockingStore.store.ts
486
- 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
+ }
487
501
 
488
502
  // src/context/useResolvedStore.ts
489
503
  function useOptionalContextStore() {
490
- return useOptionalContext();
504
+ return useOptionalUIBlockingContext();
491
505
  }
492
- function useResolvedStore() {
506
+ function useResolvedStoreApi() {
493
507
  const contextStore = useOptionalContextStore();
494
508
  return contextStore ?? uiBlockingStoreApi;
495
509
  }
496
- function useResolvedStoreWithSelector(selector) {
497
- const store = useResolvedStore();
510
+ function useResolvedValue(selector) {
511
+ const store = useResolvedStoreApi();
498
512
  return useStore(store, useShallow(selector));
499
513
  }
500
-
501
- // src/hooks/useAsyncAction/useAsyncAction.ts
502
514
  function useAsyncAction(actionId, scope, options) {
503
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
515
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
504
516
  addBlocker: state.addBlocker,
505
517
  removeBlocker: state.removeBlocker
506
518
  }));
@@ -527,19 +539,33 @@ function useAsyncAction(actionId, scope, options) {
527
539
  return executeWithBlocking;
528
540
  }
529
541
  function useBlocker(blockerId, config, isActive = true) {
530
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
542
+ const { addBlocker, removeBlocker, updateBlocker } = useResolvedValue((state) => ({
531
543
  addBlocker: state.addBlocker,
532
- removeBlocker: state.removeBlocker
544
+ removeBlocker: state.removeBlocker,
545
+ updateBlocker: state.updateBlocker
533
546
  }));
547
+ const lastConfigRef = useRef(null);
534
548
  useEffect(() => {
535
549
  if (!isActive || !blockerId) {
536
550
  return;
537
551
  }
538
552
  addBlocker(blockerId, config);
553
+ lastConfigRef.current = config;
539
554
  return () => {
555
+ lastConfigRef.current = null;
540
556
  removeBlocker(blockerId);
541
557
  };
542
558
  }, [blockerId, isActive, addBlocker, removeBlocker]);
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]);
543
569
  }
544
570
 
545
571
  // src/hooks/useBlocker/useBlocker.utils.ts
@@ -551,7 +577,7 @@ function createBlockerConfig(config) {
551
577
  };
552
578
  }
553
579
  function useBlockingInfo(scope = DEFAULT_SCOPE) {
554
- const store = useResolvedStore();
580
+ const store = useResolvedStoreApi();
555
581
  const activeBlockers = useStore(store, (state) => state.activeBlockers);
556
582
  return useMemo(() => {
557
583
  return store.getState().getBlockingInfo(scope);
@@ -567,7 +593,7 @@ function useConfigRef(config) {
567
593
 
568
594
  // src/hooks/useConditionalBlocker/useConditionalBlocker.ts
569
595
  function useConditionalBlocker(blockerId, config) {
570
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
596
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
571
597
  addBlocker: state.addBlocker,
572
598
  removeBlocker: state.removeBlocker
573
599
  }));
@@ -622,9 +648,6 @@ function useConfirmableBlocker(blockerId, config) {
622
648
  setIsExecuting(true);
623
649
  try {
624
650
  await config.onConfirm();
625
- } catch (error) {
626
- console.error("[UIBlocking] Confirmable action error:", error);
627
- throw error;
628
651
  } finally {
629
652
  setIsExecuting(false);
630
653
  }
@@ -650,7 +673,7 @@ function useConfirmableBlocker(blockerId, config) {
650
673
 
651
674
  // src/hooks/useIsBlocked/useIsBlocked.ts
652
675
  function useIsBlocked(scope) {
653
- return useResolvedStoreWithSelector((state) => state.isBlocked(scope));
676
+ return useResolvedValue((state) => state.isBlocked(scope));
654
677
  }
655
678
 
656
679
  // src/hooks/useScheduledBlocker/useScheduledBlocker.utils.ts
@@ -692,7 +715,7 @@ function isScheduleInPast(startTime, endTime, now) {
692
715
 
693
716
  // src/hooks/useScheduledBlocker/useScheduledBlocker.ts
694
717
  function useScheduledBlocker(blockerId, config) {
695
- const { addBlocker, removeBlocker } = useResolvedStoreWithSelector((state) => ({
718
+ const { addBlocker, removeBlocker } = useResolvedValue((state) => ({
696
719
  addBlocker: state.addBlocker,
697
720
  removeBlocker: state.removeBlocker
698
721
  }));
@@ -713,7 +736,6 @@ function useScheduledBlocker(blockerId, config) {
713
736
  const startTime = parseDate(currentConfig.schedule.start);
714
737
  const now = Date.now();
715
738
  if (!isValidTimestamp(startTime)) {
716
- console.error(`[UIBlocking] Invalid start time for blocker "${blockerId}"`);
717
739
  return;
718
740
  }
719
741
  const scheduleEnd = () => {
@@ -738,9 +760,6 @@ function useScheduledBlocker(blockerId, config) {
738
760
  if (startTime > now) {
739
761
  const delay = startTime - now;
740
762
  if (!isSafeTimeout(delay)) {
741
- console.warn(
742
- `[UIBlocking] Schedule delay exceeds maximum timeout (${MAX_TIMEOUT_MS.toString()}ms) for blocker "${blockerId}"`
743
- );
744
763
  return;
745
764
  }
746
765
  timeoutsRef.current.start = setTimeout(startBlocking, delay);
@@ -749,7 +768,7 @@ function useScheduledBlocker(blockerId, config) {
749
768
  if (isInBlockingPeriod(startTime, endTime, now)) {
750
769
  startBlocking();
751
770
  } else if (isScheduleInPast(startTime, endTime, now)) {
752
- console.warn(`[UIBlocking] Schedule is in the past for blocker "${blockerId}"`);
771
+ return;
753
772
  }
754
773
  }
755
774
  return () => {
@@ -763,6 +782,12 @@ function useScheduledBlocker(blockerId, config) {
763
782
  function capitalizeFirst(str) {
764
783
  return str.charAt(0).toUpperCase() + str.slice(1);
765
784
  }
785
+ function getAnalyticsWindow() {
786
+ if (typeof window === "undefined") {
787
+ return void 0;
788
+ }
789
+ return window;
790
+ }
766
791
  function buildEventData(context) {
767
792
  const eventData = {
768
793
  blocker_id: context.blockerId
@@ -780,20 +805,23 @@ function buildEventData(context) {
780
805
  return eventData;
781
806
  }
782
807
  function trackGoogleAnalytics(action, eventData) {
783
- if (window.gtag) {
784
- window.gtag("event", `ui_blocking_${action}`, eventData);
808
+ const analyticsWindow = getAnalyticsWindow();
809
+ if (analyticsWindow?.gtag) {
810
+ analyticsWindow.gtag("event", `ui_blocking_${action}`, eventData);
785
811
  }
786
812
  }
787
813
  function trackMixpanel(action, eventData) {
788
- if (window.mixpanel) {
814
+ const analyticsWindow = getAnalyticsWindow();
815
+ if (analyticsWindow?.mixpanel) {
789
816
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
790
- window.mixpanel.track(eventName, eventData);
817
+ analyticsWindow.mixpanel.track(eventName, eventData);
791
818
  }
792
819
  }
793
820
  function trackAmplitude(action, eventData) {
794
- if (window.amplitude) {
821
+ const analyticsWindow = getAnalyticsWindow();
822
+ if (analyticsWindow?.amplitude) {
795
823
  const eventName = `UI Blocking ${action.split("_").map(capitalizeFirst).join(" ")}`;
796
- window.amplitude.track(eventName, eventData);
824
+ analyticsWindow.amplitude.track(eventName, eventData);
797
825
  }
798
826
  }
799
827
  function trackCustom(trackFn, action, eventData) {
@@ -830,10 +858,16 @@ function createAnalyticsMiddleware(config) {
830
858
  }
831
859
 
832
860
  // src/middleware/configureMiddleware.ts
861
+ var CONFIGURE_MIDDLEWARE_PREFIX = "middleware-";
833
862
  function configureMiddleware(middlewares) {
834
863
  const store = uiBlockingStoreApi.getState();
864
+ store.middlewares.forEach((_, name) => {
865
+ if (name.startsWith(CONFIGURE_MIDDLEWARE_PREFIX)) {
866
+ store.unregisterMiddleware(name);
867
+ }
868
+ });
835
869
  middlewares.forEach((middleware, index) => {
836
- const name = `middleware-${index.toString()}`;
870
+ const name = `${CONFIGURE_MIDDLEWARE_PREFIX}${index.toString()}`;
837
871
  store.registerMiddleware(name, middleware);
838
872
  });
839
873
  }
@@ -917,9 +951,6 @@ function handleRemoveAction(context, blockStartTimes, slowBlockThreshold, onSlow
917
951
  const duration = context.timestamp - startTime;
918
952
  blockStartTimes.delete(context.blockerId);
919
953
  if (duration >= slowBlockThreshold) {
920
- console.warn(
921
- `[UIBlocking] Slow block detected: "${context.blockerId}" took ${duration.toString()}ms`
922
- );
923
954
  onSlowBlock?.(context.blockerId, duration);
924
955
  }
925
956
  }
@@ -951,6 +982,17 @@ function createTypedHooks() {
951
982
  };
952
983
  }
953
984
 
954
- 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 };
955
997
  //# sourceMappingURL=index.js.map
956
998
  //# sourceMappingURL=index.js.map