@trackunit/react-core-hooks 1.17.59-alpha-df0e8d549b3.0 → 1.17.59

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/index.cjs.js CHANGED
@@ -1,11 +1,162 @@
1
1
  'use strict';
2
2
 
3
- var reactCoreContextsApi = require('@trackunit/react-core-contexts-api');
4
3
  var react = require('react');
4
+ var reactCoreContextsApi = require('@trackunit/react-core-contexts-api');
5
5
  var esToolkit = require('es-toolkit');
6
6
  var irisAppRuntimeCore = require('@trackunit/iris-app-runtime-core');
7
7
  var zod = require('zod');
8
8
 
9
+ /**
10
+ * This is a hook to use the FeatureFlagContext.
11
+ *
12
+ * @requires FeatureFlagContext
13
+ * @example
14
+ * import { useFeatureFlags } from "@trackunit/react-core-hooks";
15
+ * const { flags } = useFeatureFlags();
16
+ * // use flags to control visibility/access ...
17
+ * @see (@link FeatureFlagContext)
18
+ */
19
+ const useFeatureFlags = () => {
20
+ const context = react.useContext(reactCoreContextsApi.FeatureFlagContext);
21
+ if (!context) {
22
+ throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
23
+ }
24
+ return context;
25
+ };
26
+
27
+ /**
28
+ * This is a hook to use the UserSubscriptionContext.
29
+ *
30
+ * @requires UserSubscriptionContext
31
+ * @example
32
+ * import { useUserSubscription } from "@trackunit/react-core-hooks";
33
+ * const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
34
+ * // use it for something
35
+ * const data = fetchData(numberOfDaysWithAccessToHistoricalData)
36
+ * @see { UserSubscription}
37
+ */
38
+ const useUserSubscription = () => {
39
+ const context = react.useContext(reactCoreContextsApi.UserSubscriptionContext);
40
+ if (!context) {
41
+ throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
42
+ }
43
+ return context;
44
+ };
45
+
46
+ /**
47
+ * Hook providing the authenticated user's profile, account, and permission data
48
+ * from the CurrentUserContext.
49
+ *
50
+ * ### When to use
51
+ * Use `useCurrentUser` when you need the user's identity or account context — displaying
52
+ * the user's name, resolving the effective account ID, or branching logic based on
53
+ * `isAssuming` or `isTrackunitUser`.
54
+ *
55
+ * ### When not to use
56
+ * - If you only need a boolean check for a single permission — use `useUserPermission`.
57
+ * - To gate entire routes — prefer server-side access control or `hasAccessTo` from
58
+ * `useNavigateInHost`.
59
+ *
60
+ * @example
61
+ * import { useCurrentUser } from "@trackunit/react-core-hooks";
62
+ * const { assumedUser, accountId } = useCurrentUser();
63
+ *
64
+ * const { data, loading } = useGetAccountByIdQuery({
65
+ * variables: { accountId: assumedUser?.accountId || accountId || "" },
66
+ * });
67
+ * @see { CurrentUserContext}
68
+ */
69
+ const useCurrentUser = () => {
70
+ const context = react.useContext(reactCoreContextsApi.CurrentUserContext);
71
+ if (!context) {
72
+ throw new Error("useCurrentUser must be used within the CurrentUserProvider");
73
+ }
74
+ return context;
75
+ };
76
+
77
+ /**
78
+ * Async wrapper around {@link useValidateAccess}. Each helper awaits until the
79
+ * underlying user, subscription, and feature-flag data has finished loading
80
+ * before resolving, so callers such as router guards can check access without
81
+ * having to observe the synchronous `loading` flag themselves.
82
+ */
83
+ const useValidateAccessAsync = () => {
84
+ const { hasAccessTo, hasFeatureFlag, hasPermission, loading } = useValidateAccess();
85
+ const hasAccessToRef = react.useRef(hasAccessTo);
86
+ hasAccessToRef.current = hasAccessTo;
87
+ const hasFeatureFlagRef = react.useRef(hasFeatureFlag);
88
+ hasFeatureFlagRef.current = hasFeatureFlag;
89
+ const hasPermissionRef = react.useRef(hasPermission);
90
+ hasPermissionRef.current = hasPermission;
91
+ const loadingRef = react.useRef(loading);
92
+ loadingRef.current = loading;
93
+ return react.useMemo(() => {
94
+ const waitForLoading = async () => {
95
+ if (loadingRef.current) {
96
+ await new Promise(resolve => {
97
+ const intervalId = setInterval(() => {
98
+ if (!loadingRef.current) {
99
+ clearInterval(intervalId);
100
+ resolve();
101
+ }
102
+ }, 10);
103
+ });
104
+ }
105
+ };
106
+ return {
107
+ hasAccessTo: async (requiredAccessTo) => {
108
+ await waitForLoading();
109
+ return hasAccessToRef.current(requiredAccessTo);
110
+ },
111
+ hasFeatureFlag: async (flag) => {
112
+ await waitForLoading();
113
+ return hasFeatureFlagRef.current(flag);
114
+ },
115
+ hasPermission: async (permission) => {
116
+ await waitForLoading();
117
+ return hasPermissionRef.current(permission);
118
+ },
119
+ };
120
+ }, [loadingRef, hasAccessToRef, hasFeatureFlagRef, hasPermissionRef]);
121
+ };
122
+ /**
123
+ * Synchronously evaluates the current user's access via subscription features,
124
+ * feature flags, and permissions. Returns predicate helpers plus a `loading`
125
+ * flag that stays true until user, subscription, and flag data are all present.
126
+ */
127
+ const useValidateAccess = () => {
128
+ const { isAuthenticated, permissions: userPermissions } = useCurrentUser();
129
+ const { features, loading: featuresLoading } = useUserSubscription();
130
+ const { flags: allFlags } = useFeatureFlags();
131
+ const loading = react.useMemo(() => allFlags === null || featuresLoading || !userPermissions, [allFlags, featuresLoading, userPermissions]);
132
+ const hasFeatureFlag = react.useCallback((flag) => {
133
+ return allFlags?.find(({ key }) => key === flag)?.state ?? null;
134
+ }, [allFlags]);
135
+ const hasAccessTo = react.useCallback((requiredAccessTo) => {
136
+ if (isAuthenticated) {
137
+ return hasAccessToFeature(features, requiredAccessTo);
138
+ }
139
+ return false;
140
+ }, [features, isAuthenticated]);
141
+ const hasPermission = react.useCallback((permission) => {
142
+ if (isAuthenticated) {
143
+ return userPermissions?.includes(permission) ?? false;
144
+ }
145
+ return false;
146
+ }, [isAuthenticated, userPermissions]);
147
+ return react.useMemo(() => {
148
+ return {
149
+ hasAccessTo,
150
+ hasFeatureFlag,
151
+ hasPermission,
152
+ loading,
153
+ };
154
+ }, [hasAccessTo, hasFeatureFlag, hasPermission, loading]);
155
+ };
156
+ const hasAccessToFeature = (myFeatures, feature) => {
157
+ return myFeatures?.some(x => x.id === feature) ?? null;
158
+ };
159
+
9
160
  /**
10
161
  * Hook to get the analytics context.
11
162
  *
@@ -219,21 +370,39 @@ const useExportDataContext = () => {
219
370
  };
220
371
 
221
372
  /**
222
- * This is a hook to use the FeatureFlagContext.
373
+ * Reads a host feature flag by key.
374
+ *
375
+ * Return values:
376
+ * - `null` while the host feature flags have not loaded yet.
377
+ * - the flag's boolean `state` when the flag exists in the loaded set.
378
+ * - `null` when the flags are loaded but the requested flag is not present,
379
+ * unless `options.defaultValueWhenMissing` is provided, in which case that
380
+ * value is returned.
381
+ *
382
+ * The hook is generic so callers can narrow the accepted key. Internal callers
383
+ * pass the strongly-typed `Flag` union for type safety, e.g.
384
+ * `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
385
+ * External callers may pass any string.
386
+ *
387
+ * Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
223
388
  *
224
- * @requires FeatureFlagContext
225
389
  * @example
226
- * import { useFeatureFlags } from "@trackunit/react-core-hooks";
227
- * const { flags } = useFeatureFlags();
228
- * // use flags to control visibility/access ...
229
- * @see (@link FeatureFlagContext)
390
+ * import { useHostFeatureFlag } from "@trackunit/react-core-hooks";
391
+ * const isEnabled = useHostFeatureFlag("my_flag", { defaultValueWhenMissing: true });
392
+ * @see useFeatureFlags
230
393
  */
231
- const useFeatureFlags = () => {
232
- const context = react.useContext(reactCoreContextsApi.FeatureFlagContext);
233
- if (!context) {
234
- throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
235
- }
236
- return context;
394
+ const useHostFeatureFlag = (flag, options) => {
395
+ const { flags } = useFeatureFlags();
396
+ return react.useMemo(() => {
397
+ const found = flags?.find(({ key }) => key === flag);
398
+ if (found) {
399
+ return found.state;
400
+ }
401
+ if (flags && options.defaultValueWhenMissing !== undefined) {
402
+ return options.defaultValueWhenMissing;
403
+ }
404
+ return null;
405
+ }, [flags, flag, options.defaultValueWhenMissing]);
237
406
  };
238
407
 
239
408
  /**
@@ -896,25 +1065,6 @@ const useSiteRuntime = () => {
896
1065
  return react.useMemo(() => ({ siteInfo, loading, error }), [siteInfo, loading, error]);
897
1066
  };
898
1067
 
899
- /**
900
- * This is a hook to use the UserSubscriptionContext.
901
- *
902
- * @requires UserSubscriptionContext
903
- * @example
904
- * import { useUserSubscription } from "@trackunit/react-core-hooks";
905
- * const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
906
- * // use it for something
907
- * const data = fetchData(numberOfDaysWithAccessToHistoricalData)
908
- * @see { UserSubscription}
909
- */
910
- const useUserSubscription = () => {
911
- const context = react.useContext(reactCoreContextsApi.UserSubscriptionContext);
912
- if (!context) {
913
- throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
914
- }
915
- return context;
916
- };
917
-
918
1068
  /**
919
1069
  * This is a hook to use the TimeRangeProvider.
920
1070
  *
@@ -960,37 +1110,6 @@ const useToast = () => {
960
1110
  return toastContext;
961
1111
  };
962
1112
 
963
- /**
964
- * Hook providing the authenticated user's profile, account, and permission data
965
- * from the CurrentUserContext.
966
- *
967
- * ### When to use
968
- * Use `useCurrentUser` when you need the user's identity or account context — displaying
969
- * the user's name, resolving the effective account ID, or branching logic based on
970
- * `isAssuming` or `isTrackunitUser`.
971
- *
972
- * ### When not to use
973
- * - If you only need a boolean check for a single permission — use `useUserPermission`.
974
- * - To gate entire routes — prefer server-side access control or `hasAccessTo` from
975
- * `useNavigateInHost`.
976
- *
977
- * @example
978
- * import { useCurrentUser } from "@trackunit/react-core-hooks";
979
- * const { assumedUser, accountId } = useCurrentUser();
980
- *
981
- * const { data, loading } = useGetAccountByIdQuery({
982
- * variables: { accountId: assumedUser?.accountId || accountId || "" },
983
- * });
984
- * @see { CurrentUserContext}
985
- */
986
- const useCurrentUser = () => {
987
- const context = react.useContext(reactCoreContextsApi.CurrentUserContext);
988
- if (!context) {
989
- throw new Error("useCurrentUser must be used within the CurrentUserProvider");
990
- }
991
- return context;
992
- };
993
-
994
1113
  /**
995
1114
  * This is a hook providing the Current User language.
996
1115
  *
@@ -1429,6 +1548,7 @@ exports.useFeatureFlags = useFeatureFlags;
1429
1548
  exports.useFilterBarContext = useFilterBarContext;
1430
1549
  exports.useGeolocation = useGeolocation;
1431
1550
  exports.useHasAccessTo = useHasAccessTo;
1551
+ exports.useHostFeatureFlag = useHostFeatureFlag;
1432
1552
  exports.useImageUploader = useImageUploader;
1433
1553
  exports.useIrisAppId = useIrisAppId;
1434
1554
  exports.useIrisAppImage = useIrisAppImage;
@@ -1443,5 +1563,7 @@ exports.useToast = useToast;
1443
1563
  exports.useToken = useToken;
1444
1564
  exports.useUserPermission = useUserPermission;
1445
1565
  exports.useUserSubscription = useUserSubscription;
1566
+ exports.useValidateAccess = useValidateAccess;
1567
+ exports.useValidateAccessAsync = useValidateAccessAsync;
1446
1568
  exports.useWidgetConfig = useWidgetConfig;
1447
1569
  exports.useWidgetConfigAsync = useWidgetConfigAsync;
package/index.esm.js CHANGED
@@ -1,9 +1,160 @@
1
- import { AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, EnvironmentContext, ErrorHandlingContext, ExportDataContext, FeatureFlagContext, FilterBarContext, GeolocationContext, TokenContext, ModalDialogContext, NavigationContext, OemBrandingContext, UserSubscriptionContext, TimeRangeContext, ToastContext, CurrentUserContext, CurrentUserPreferenceContext, WidgetConfigContext } from '@trackunit/react-core-contexts-api';
2
- import { useContext, useMemo, useState, useCallback, useEffect, useReducer, useRef } from 'react';
1
+ import { useContext, useMemo, useCallback, useRef, useState, useEffect, useReducer } from 'react';
2
+ import { FeatureFlagContext, UserSubscriptionContext, CurrentUserContext, AnalyticsContext, AssetSortingContext, ConfirmationDialogContext, EnvironmentContext, ErrorHandlingContext, ExportDataContext, FilterBarContext, GeolocationContext, TokenContext, ModalDialogContext, NavigationContext, OemBrandingContext, TimeRangeContext, ToastContext, CurrentUserPreferenceContext, WidgetConfigContext } from '@trackunit/react-core-contexts-api';
3
3
  import { isEqual } from 'es-toolkit';
4
4
  import { AssetRuntime, CustomerRuntime, EventRuntime, ParamsRuntime, SiteRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
5
5
  import { z } from 'zod';
6
6
 
7
+ /**
8
+ * This is a hook to use the FeatureFlagContext.
9
+ *
10
+ * @requires FeatureFlagContext
11
+ * @example
12
+ * import { useFeatureFlags } from "@trackunit/react-core-hooks";
13
+ * const { flags } = useFeatureFlags();
14
+ * // use flags to control visibility/access ...
15
+ * @see (@link FeatureFlagContext)
16
+ */
17
+ const useFeatureFlags = () => {
18
+ const context = useContext(FeatureFlagContext);
19
+ if (!context) {
20
+ throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
21
+ }
22
+ return context;
23
+ };
24
+
25
+ /**
26
+ * This is a hook to use the UserSubscriptionContext.
27
+ *
28
+ * @requires UserSubscriptionContext
29
+ * @example
30
+ * import { useUserSubscription } from "@trackunit/react-core-hooks";
31
+ * const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
32
+ * // use it for something
33
+ * const data = fetchData(numberOfDaysWithAccessToHistoricalData)
34
+ * @see { UserSubscription}
35
+ */
36
+ const useUserSubscription = () => {
37
+ const context = useContext(UserSubscriptionContext);
38
+ if (!context) {
39
+ throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
40
+ }
41
+ return context;
42
+ };
43
+
44
+ /**
45
+ * Hook providing the authenticated user's profile, account, and permission data
46
+ * from the CurrentUserContext.
47
+ *
48
+ * ### When to use
49
+ * Use `useCurrentUser` when you need the user's identity or account context — displaying
50
+ * the user's name, resolving the effective account ID, or branching logic based on
51
+ * `isAssuming` or `isTrackunitUser`.
52
+ *
53
+ * ### When not to use
54
+ * - If you only need a boolean check for a single permission — use `useUserPermission`.
55
+ * - To gate entire routes — prefer server-side access control or `hasAccessTo` from
56
+ * `useNavigateInHost`.
57
+ *
58
+ * @example
59
+ * import { useCurrentUser } from "@trackunit/react-core-hooks";
60
+ * const { assumedUser, accountId } = useCurrentUser();
61
+ *
62
+ * const { data, loading } = useGetAccountByIdQuery({
63
+ * variables: { accountId: assumedUser?.accountId || accountId || "" },
64
+ * });
65
+ * @see { CurrentUserContext}
66
+ */
67
+ const useCurrentUser = () => {
68
+ const context = useContext(CurrentUserContext);
69
+ if (!context) {
70
+ throw new Error("useCurrentUser must be used within the CurrentUserProvider");
71
+ }
72
+ return context;
73
+ };
74
+
75
+ /**
76
+ * Async wrapper around {@link useValidateAccess}. Each helper awaits until the
77
+ * underlying user, subscription, and feature-flag data has finished loading
78
+ * before resolving, so callers such as router guards can check access without
79
+ * having to observe the synchronous `loading` flag themselves.
80
+ */
81
+ const useValidateAccessAsync = () => {
82
+ const { hasAccessTo, hasFeatureFlag, hasPermission, loading } = useValidateAccess();
83
+ const hasAccessToRef = useRef(hasAccessTo);
84
+ hasAccessToRef.current = hasAccessTo;
85
+ const hasFeatureFlagRef = useRef(hasFeatureFlag);
86
+ hasFeatureFlagRef.current = hasFeatureFlag;
87
+ const hasPermissionRef = useRef(hasPermission);
88
+ hasPermissionRef.current = hasPermission;
89
+ const loadingRef = useRef(loading);
90
+ loadingRef.current = loading;
91
+ return useMemo(() => {
92
+ const waitForLoading = async () => {
93
+ if (loadingRef.current) {
94
+ await new Promise(resolve => {
95
+ const intervalId = setInterval(() => {
96
+ if (!loadingRef.current) {
97
+ clearInterval(intervalId);
98
+ resolve();
99
+ }
100
+ }, 10);
101
+ });
102
+ }
103
+ };
104
+ return {
105
+ hasAccessTo: async (requiredAccessTo) => {
106
+ await waitForLoading();
107
+ return hasAccessToRef.current(requiredAccessTo);
108
+ },
109
+ hasFeatureFlag: async (flag) => {
110
+ await waitForLoading();
111
+ return hasFeatureFlagRef.current(flag);
112
+ },
113
+ hasPermission: async (permission) => {
114
+ await waitForLoading();
115
+ return hasPermissionRef.current(permission);
116
+ },
117
+ };
118
+ }, [loadingRef, hasAccessToRef, hasFeatureFlagRef, hasPermissionRef]);
119
+ };
120
+ /**
121
+ * Synchronously evaluates the current user's access via subscription features,
122
+ * feature flags, and permissions. Returns predicate helpers plus a `loading`
123
+ * flag that stays true until user, subscription, and flag data are all present.
124
+ */
125
+ const useValidateAccess = () => {
126
+ const { isAuthenticated, permissions: userPermissions } = useCurrentUser();
127
+ const { features, loading: featuresLoading } = useUserSubscription();
128
+ const { flags: allFlags } = useFeatureFlags();
129
+ const loading = useMemo(() => allFlags === null || featuresLoading || !userPermissions, [allFlags, featuresLoading, userPermissions]);
130
+ const hasFeatureFlag = useCallback((flag) => {
131
+ return allFlags?.find(({ key }) => key === flag)?.state ?? null;
132
+ }, [allFlags]);
133
+ const hasAccessTo = useCallback((requiredAccessTo) => {
134
+ if (isAuthenticated) {
135
+ return hasAccessToFeature(features, requiredAccessTo);
136
+ }
137
+ return false;
138
+ }, [features, isAuthenticated]);
139
+ const hasPermission = useCallback((permission) => {
140
+ if (isAuthenticated) {
141
+ return userPermissions?.includes(permission) ?? false;
142
+ }
143
+ return false;
144
+ }, [isAuthenticated, userPermissions]);
145
+ return useMemo(() => {
146
+ return {
147
+ hasAccessTo,
148
+ hasFeatureFlag,
149
+ hasPermission,
150
+ loading,
151
+ };
152
+ }, [hasAccessTo, hasFeatureFlag, hasPermission, loading]);
153
+ };
154
+ const hasAccessToFeature = (myFeatures, feature) => {
155
+ return myFeatures?.some(x => x.id === feature) ?? null;
156
+ };
157
+
7
158
  /**
8
159
  * Hook to get the analytics context.
9
160
  *
@@ -217,21 +368,39 @@ const useExportDataContext = () => {
217
368
  };
218
369
 
219
370
  /**
220
- * This is a hook to use the FeatureFlagContext.
371
+ * Reads a host feature flag by key.
372
+ *
373
+ * Return values:
374
+ * - `null` while the host feature flags have not loaded yet.
375
+ * - the flag's boolean `state` when the flag exists in the loaded set.
376
+ * - `null` when the flags are loaded but the requested flag is not present,
377
+ * unless `options.defaultValueWhenMissing` is provided, in which case that
378
+ * value is returned.
379
+ *
380
+ * The hook is generic so callers can narrow the accepted key. Internal callers
381
+ * pass the strongly-typed `Flag` union for type safety, e.g.
382
+ * `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
383
+ * External callers may pass any string.
384
+ *
385
+ * Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
221
386
  *
222
- * @requires FeatureFlagContext
223
387
  * @example
224
- * import { useFeatureFlags } from "@trackunit/react-core-hooks";
225
- * const { flags } = useFeatureFlags();
226
- * // use flags to control visibility/access ...
227
- * @see (@link FeatureFlagContext)
388
+ * import { useHostFeatureFlag } from "@trackunit/react-core-hooks";
389
+ * const isEnabled = useHostFeatureFlag("my_flag", { defaultValueWhenMissing: true });
390
+ * @see useFeatureFlags
228
391
  */
229
- const useFeatureFlags = () => {
230
- const context = useContext(FeatureFlagContext);
231
- if (!context) {
232
- throw new Error("useFeatureFlags must be used within a FeatureFlagContext");
233
- }
234
- return context;
392
+ const useHostFeatureFlag = (flag, options) => {
393
+ const { flags } = useFeatureFlags();
394
+ return useMemo(() => {
395
+ const found = flags?.find(({ key }) => key === flag);
396
+ if (found) {
397
+ return found.state;
398
+ }
399
+ if (flags && options.defaultValueWhenMissing !== undefined) {
400
+ return options.defaultValueWhenMissing;
401
+ }
402
+ return null;
403
+ }, [flags, flag, options.defaultValueWhenMissing]);
235
404
  };
236
405
 
237
406
  /**
@@ -894,25 +1063,6 @@ const useSiteRuntime = () => {
894
1063
  return useMemo(() => ({ siteInfo, loading, error }), [siteInfo, loading, error]);
895
1064
  };
896
1065
 
897
- /**
898
- * This is a hook to use the UserSubscriptionContext.
899
- *
900
- * @requires UserSubscriptionContext
901
- * @example
902
- * import { useUserSubscription } from "@trackunit/react-core-hooks";
903
- * const { numberOfDaysWithAccessToHistoricalData, packageType } = useUserSubscription();
904
- * // use it for something
905
- * const data = fetchData(numberOfDaysWithAccessToHistoricalData)
906
- * @see { UserSubscription}
907
- */
908
- const useUserSubscription = () => {
909
- const context = useContext(UserSubscriptionContext);
910
- if (!context) {
911
- throw new Error("useUserSubscription must be used within the UserSubscriptionContext");
912
- }
913
- return context;
914
- };
915
-
916
1066
  /**
917
1067
  * This is a hook to use the TimeRangeProvider.
918
1068
  *
@@ -958,37 +1108,6 @@ const useToast = () => {
958
1108
  return toastContext;
959
1109
  };
960
1110
 
961
- /**
962
- * Hook providing the authenticated user's profile, account, and permission data
963
- * from the CurrentUserContext.
964
- *
965
- * ### When to use
966
- * Use `useCurrentUser` when you need the user's identity or account context — displaying
967
- * the user's name, resolving the effective account ID, or branching logic based on
968
- * `isAssuming` or `isTrackunitUser`.
969
- *
970
- * ### When not to use
971
- * - If you only need a boolean check for a single permission — use `useUserPermission`.
972
- * - To gate entire routes — prefer server-side access control or `hasAccessTo` from
973
- * `useNavigateInHost`.
974
- *
975
- * @example
976
- * import { useCurrentUser } from "@trackunit/react-core-hooks";
977
- * const { assumedUser, accountId } = useCurrentUser();
978
- *
979
- * const { data, loading } = useGetAccountByIdQuery({
980
- * variables: { accountId: assumedUser?.accountId || accountId || "" },
981
- * });
982
- * @see { CurrentUserContext}
983
- */
984
- const useCurrentUser = () => {
985
- const context = useContext(CurrentUserContext);
986
- if (!context) {
987
- throw new Error("useCurrentUser must be used within the CurrentUserProvider");
988
- }
989
- return context;
990
- };
991
-
992
1111
  /**
993
1112
  * This is a hook providing the Current User language.
994
1113
  *
@@ -1404,4 +1523,4 @@ const useWidgetConfig = () => {
1404
1523
  return result;
1405
1524
  };
1406
1525
 
1407
- export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeFormat, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSettingsUtils, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useWidgetConfig, useWidgetConfigAsync };
1526
+ export { fetchAssetBlobUrl, useAnalytics, useAssetRuntime, useAssetSorting, useConfirmationDialog, useCurrentUser, useCurrentUserFavoriteAdvancedSensors, useCurrentUserFavoriteInsights, useCurrentUserLanguage, useCurrentUserSystemOfMeasurement, useCurrentUserTimeFormat, useCurrentUserTimeZonePreference, useCustomerRuntime, useEnvironment, useErrorHandler, useErrorHandlerOrNull, useEventRuntime, useExportDataContext, useFeatureBranchQueryString, useFeatureFlags, useFilterBarContext, useGeolocation, useHasAccessTo, useHostFeatureFlag, useImageUploader, useIrisAppId, useIrisAppImage, useIrisAppName, useModalDialogContext, useNavigateInHost, useOemBrandingContext, useSettingsUtils, useSiteRuntime, useTimeRange, useToast, useToken, useUserPermission, useUserSubscription, useValidateAccess, useValidateAccessAsync, useWidgetConfig, useWidgetConfigAsync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-core-hooks",
3
- "version": "1.17.59-alpha-df0e8d549b3.0",
3
+ "version": "1.17.59",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -8,9 +8,9 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "es-toolkit": "^1.39.10",
11
- "@trackunit/iris-app-runtime-core": "1.17.54-alpha-df0e8d549b3.0",
12
- "@trackunit/iris-app-runtime-core-api": "1.16.53-alpha-df0e8d549b3.0",
13
- "@trackunit/react-core-contexts-api": "1.17.53-alpha-df0e8d549b3.0",
11
+ "@trackunit/iris-app-runtime-core": "1.17.54",
12
+ "@trackunit/iris-app-runtime-core-api": "1.16.53",
13
+ "@trackunit/react-core-contexts-api": "1.17.53",
14
14
  "zod": "^3.25.76"
15
15
  },
16
16
  "peerDependencies": {
@@ -0,0 +1,6 @@
1
+ import type { HasAccess, ValidateAccess } from "./useValidateAccessAsync";
2
+ export interface ExtensionRouterContext {
3
+ hasAccessToRoute: (requiredAccessTo: ValidateAccess) => Promise<HasAccess>;
4
+ hasFeatureFlag: (flag: string) => Promise<HasAccess>;
5
+ hasPermission: (permission: string) => Promise<HasAccess>;
6
+ }
@@ -0,0 +1,25 @@
1
+ import { LegacyFeature } from "@trackunit/iris-app-runtime-core-api";
2
+ export type HasAccess = boolean | null;
3
+ export type ValidateAccess = LegacyFeature;
4
+ /**
5
+ * Async wrapper around {@link useValidateAccess}. Each helper awaits until the
6
+ * underlying user, subscription, and feature-flag data has finished loading
7
+ * before resolving, so callers such as router guards can check access without
8
+ * having to observe the synchronous `loading` flag themselves.
9
+ */
10
+ export declare const useValidateAccessAsync: () => {
11
+ hasAccessTo: (requiredAccessTo: ValidateAccess) => Promise<HasAccess>;
12
+ hasFeatureFlag: (flag: string) => Promise<HasAccess>;
13
+ hasPermission: (permission: string) => Promise<HasAccess>;
14
+ };
15
+ /**
16
+ * Synchronously evaluates the current user's access via subscription features,
17
+ * feature flags, and permissions. Returns predicate helpers plus a `loading`
18
+ * flag that stays true until user, subscription, and flag data are all present.
19
+ */
20
+ export declare const useValidateAccess: () => {
21
+ hasAccessTo: (requiredAccessTo: ValidateAccess) => HasAccess;
22
+ hasFeatureFlag: (flag: string) => HasAccess;
23
+ hasPermission: (permission: string) => HasAccess;
24
+ loading: boolean;
25
+ };
@@ -0,0 +1,34 @@
1
+ export interface UseHostFeatureFlagOptions {
2
+ /**
3
+ * Value returned when the feature flags have loaded but the requested flag is
4
+ * not present in the loaded set. When omitted, a missing flag resolves to
5
+ * `null` (the historical behaviour).
6
+ *
7
+ * Note: this only applies once flags are loaded. While flags are still
8
+ * loading the hook always returns `null` regardless of this option.
9
+ */
10
+ defaultValueWhenMissing?: boolean;
11
+ }
12
+ /**
13
+ * Reads a host feature flag by key.
14
+ *
15
+ * Return values:
16
+ * - `null` while the host feature flags have not loaded yet.
17
+ * - the flag's boolean `state` when the flag exists in the loaded set.
18
+ * - `null` when the flags are loaded but the requested flag is not present,
19
+ * unless `options.defaultValueWhenMissing` is provided, in which case that
20
+ * value is returned.
21
+ *
22
+ * The hook is generic so callers can narrow the accepted key. Internal callers
23
+ * pass the strongly-typed `Flag` union for type safety, e.g.
24
+ * `useHostFeatureFlag<Flag>(Flags.SOME_FLAG, { defaultValueWhenMissing: true })`.
25
+ * External callers may pass any string.
26
+ *
27
+ * Must be used within a `FeatureFlagContext` (provided by `TrackunitProviders`).
28
+ *
29
+ * @example
30
+ * import { useHostFeatureFlag } from "@trackunit/react-core-hooks";
31
+ * const isEnabled = useHostFeatureFlag("my_flag", { defaultValueWhenMissing: true });
32
+ * @see useFeatureFlags
33
+ */
34
+ export declare const useHostFeatureFlag: <TFlag extends string = string>(flag: TFlag, options: UseHostFeatureFlagOptions) => boolean | null;
package/src/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export * from "./access/ExtensionRouterContext";
2
+ export { useValidateAccess, useValidateAccessAsync } from "./access/useValidateAccessAsync";
3
+ export type { ValidateAccess } from "./access/useValidateAccessAsync";
1
4
  export * from "./analytics/useAnalytics";
2
5
  export * from "./assetSorting/useAssetSorting";
3
6
  export * from "./confirmationDialog/useConfirmationDialog";
@@ -5,6 +8,7 @@ export * from "./environment/useEnvironment";
5
8
  export * from "./errorHandling/useErrorHandler";
6
9
  export * from "./exportData/useExportDataContext";
7
10
  export * from "./featureFlags/useFeatureFlags";
11
+ export * from "./featureFlags/useHostFeatureFlag";
8
12
  export * from "./fetchAssetBlobUrl";
9
13
  export * from "./filterBar/useFilterBarContext";
10
14
  export * from "./geolocation/useGeolocation";