@eintrek/erp-shell 0.1.22 → 0.1.25
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/auth/permission-provider.d.ts +6 -8
- package/dist/auth/permission-utils.d.ts +24 -7
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +154 -49
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +153 -47
- package/dist/index.js.map +1 -1
- package/dist/navigation/nav-user.d.ts +13 -1
- package/dist/navigation/navigation-data-helpers.d.ts +6 -2
- package/dist/providers/permission-rules-context.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -623,12 +623,19 @@ SidebarMenuSubButton.displayName = "SidebarMenuSubButton";
|
|
|
623
623
|
|
|
624
624
|
/**
|
|
625
625
|
* True when the user's child-group segments include at least one of the
|
|
626
|
-
* groups required for `permissionRouteKey`.
|
|
627
|
-
*
|
|
626
|
+
* groups required for `permissionRouteKey`.
|
|
627
|
+
*
|
|
628
|
+
* Fail-closed:
|
|
629
|
+
* - Nav item without `permissionRouteKey` → deny (must declare a key)
|
|
630
|
+
* - Key absent from `rulesMap` → deny (unknown route is not public)
|
|
631
|
+
* - Key present with `required_child_groups: []` → allow (explicit public)
|
|
628
632
|
*/
|
|
629
633
|
function canAccessNavPermissionRoute(permissionRouteKey, rulesMap, userChildSegments) {
|
|
630
634
|
if (!permissionRouteKey)
|
|
631
|
-
return
|
|
635
|
+
return false;
|
|
636
|
+
if (!Object.prototype.hasOwnProperty.call(rulesMap, permissionRouteKey)) {
|
|
637
|
+
return false;
|
|
638
|
+
}
|
|
632
639
|
const required = rulesMap[permissionRouteKey] ?? [];
|
|
633
640
|
if (required.length === 0)
|
|
634
641
|
return true;
|
|
@@ -759,7 +766,10 @@ function AppBreadcrumb({ labels = {}, homeLabel = "แดชบอร์ด", ro
|
|
|
759
766
|
})] }) }));
|
|
760
767
|
}
|
|
761
768
|
|
|
762
|
-
function
|
|
769
|
+
function isExternalHref(href) {
|
|
770
|
+
return /^https?:\/\//i.test(href) || href.startsWith("//");
|
|
771
|
+
}
|
|
772
|
+
function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref = "/select-organization", signInRedirectHref = "/auth/signin", systemLinks, } = {}) {
|
|
763
773
|
const { data: session, status } = react.useSession();
|
|
764
774
|
const router = navigation.useRouter();
|
|
765
775
|
const { theme, setTheme } = nextThemes.useTheme();
|
|
@@ -811,6 +821,14 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
811
821
|
if (profileHref)
|
|
812
822
|
router.push(profileHref);
|
|
813
823
|
};
|
|
824
|
+
const handleSystemLink = (href) => {
|
|
825
|
+
setIsOpen(false);
|
|
826
|
+
if (isExternalHref(href)) {
|
|
827
|
+
window.location.assign(href);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
router.push(href);
|
|
831
|
+
};
|
|
814
832
|
const handleThemeChange = (newTheme) => {
|
|
815
833
|
setTheme(newTheme);
|
|
816
834
|
setShowThemeMenu(false);
|
|
@@ -843,6 +861,7 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
843
861
|
}
|
|
844
862
|
if (!user)
|
|
845
863
|
return null;
|
|
864
|
+
const visibleSystemLinks = (systemLinks ?? []).filter(link => link.href && link.label);
|
|
846
865
|
const dropdownContent = isOpen ? (jsxRuntime.jsxs("div", { "data-dropdown": "nav-user", className: "fixed w-52 sm:w-60 md:w-80 rounded-lg bg-sidebar border border-sidebar-border shadow-xl z-50 overflow-hidden\n -translate-x-[65%] -translate-y-[110%] sm:translate-x-0 sm:-translate-y-full", style: { top: `${position.top}px`, left: `${position.left}px` }, children: [organization && (jsxRuntime.jsx("div", { className: "p-4 border-b border-sidebar-border bg-sidebar-accent/30", children: jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [organization.logoUrl ? (
|
|
847
866
|
// eslint-disable-next-line @next/next/no-img-element
|
|
848
867
|
jsxRuntime.jsx("img", { src: organization.logoUrl, alt: organization.name ?? "โลโก้บริษัท", className: "h-8 w-8 flex-shrink-0 rounded object-contain bg-white",
|
|
@@ -850,7 +869,7 @@ function NavUser({ organization, avatarSrc, profileHref, selectOrganizationHref
|
|
|
850
869
|
// sidebar; drop back to the generic icon's empty slot.
|
|
851
870
|
onError: e => {
|
|
852
871
|
e.currentTarget.style.display = "none";
|
|
853
|
-
} })) : (jsxRuntime.jsx(lucideReact.Building2, { className: "h-5 w-5 text-sidebar-foreground/70 flex-shrink-0" })), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsx("div", { className: "font-medium text-sm sm:truncate", children: organization.name }), organization.description && (jsxRuntime.jsx("div", { className: "text-xs text-sidebar-foreground/70 truncate mt-0.5", children: organization.description }))] })] }) })), jsxRuntime.jsxs("div", { className: "p-2", children: [jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { onClick: () => {
|
|
872
|
+
} })) : (jsxRuntime.jsx(lucideReact.Building2, { className: "h-5 w-5 text-sidebar-foreground/70 flex-shrink-0" })), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [jsxRuntime.jsx("div", { className: "font-medium text-sm sm:truncate", children: organization.name }), organization.description && (jsxRuntime.jsx("div", { className: "text-xs text-sidebar-foreground/70 truncate mt-0.5", children: organization.description }))] })] }) })), jsxRuntime.jsxs("div", { className: "p-2", children: [visibleSystemLinks.length > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [visibleSystemLinks.map(link => (jsxRuntime.jsxs("button", { type: "button", onClick: () => handleSystemLink(link.href), className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [link.icon ?? jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: link.label })] }, `${link.label}:${link.href}`))), jsxRuntime.jsx("div", { className: "my-1 border-t border-sidebar-border" })] })) : null, jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("button", { onClick: () => {
|
|
854
873
|
setShowThemeMenu(!showThemeMenu);
|
|
855
874
|
setShowLanguageMenu(false);
|
|
856
875
|
}, className: "flex w-full items-center gap-3 px-3 py-2.5 text-sm text-sidebar-foreground hover:bg-sidebar-accent rounded-md transition-colors", children: [jsxRuntime.jsx(lucideReact.Palette, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "flex-1 text-left", children: "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19\u0E18\u0E35\u0E21" }), jsxRuntime.jsx(lucideReact.ChevronUp, { className: `h-4 w-4 transition-transform ${showThemeMenu ? "rotate-180" : ""}` })] }), showThemeMenu && (jsxRuntime.jsx("div", { className: "mt-1 ml-4 space-y-1", children: [
|
|
@@ -1204,8 +1223,8 @@ function PermissionRulesProvider({ children, data, isLoading = false, isError =
|
|
|
1204
1223
|
return (jsxRuntime.jsx(PermissionRulesContext.Provider, { value: value, children: children }));
|
|
1205
1224
|
}
|
|
1206
1225
|
/**
|
|
1207
|
-
* Read the permission rules.
|
|
1208
|
-
*
|
|
1226
|
+
* Read the permission rules. Outside the provider, report loading so
|
|
1227
|
+
* fail-closed consumers (nav / gates) do not treat empty maps as "allow all".
|
|
1209
1228
|
*/
|
|
1210
1229
|
function usePermissionRules() {
|
|
1211
1230
|
const ctx = React.useContext(PermissionRulesContext);
|
|
@@ -1213,7 +1232,7 @@ function usePermissionRules() {
|
|
|
1213
1232
|
return {
|
|
1214
1233
|
rulesMap: {},
|
|
1215
1234
|
policiesMap: {},
|
|
1216
|
-
isLoading:
|
|
1235
|
+
isLoading: true,
|
|
1217
1236
|
isError: false,
|
|
1218
1237
|
updatedAt: null,
|
|
1219
1238
|
};
|
|
@@ -1235,20 +1254,22 @@ function normalizePathnameForPermissionRules(pathname) {
|
|
|
1235
1254
|
const path = parts.length <= 1 ? "/" : "/" + parts.slice(1).join("/");
|
|
1236
1255
|
return path.replace(/\/user-role\/(purchase-doc|sales-doc)$/, "/user-role/members");
|
|
1237
1256
|
}
|
|
1257
|
+
function hasRule(rulesMap, routeKey) {
|
|
1258
|
+
return Object.prototype.hasOwnProperty.call(rulesMap, routeKey);
|
|
1259
|
+
}
|
|
1238
1260
|
/**
|
|
1239
|
-
*
|
|
1240
|
-
* JWT must contain at least one matching group). An exact match on the
|
|
1241
|
-
* normalized path wins; otherwise the function falls back to a segment-by-
|
|
1242
|
-
* segment pattern match where `[param]` placeholders accept any non-empty
|
|
1243
|
-
* value. Returns an empty array when no rule covers the path.
|
|
1261
|
+
* Resolve UI route permissions for a pathname.
|
|
1244
1262
|
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1263
|
+
* - Exact / `[param]` pattern match → matched
|
|
1264
|
+
* - Parent-prefix inherit only when the parent rule has a **non-empty**
|
|
1265
|
+
* role list (restrictive inherit). Explicitly public parents (`[]`) do
|
|
1266
|
+
* not open all children.
|
|
1267
|
+
* - No match → `{ matched: false }` (fail closed — never treat as public)
|
|
1247
1268
|
*/
|
|
1248
|
-
function
|
|
1269
|
+
function resolveUiRoutePermission(pathname, rulesMap) {
|
|
1249
1270
|
const normalized = normalizePathnameForPermissionRules(pathname);
|
|
1250
|
-
if (rulesMap
|
|
1251
|
-
return rulesMap[normalized];
|
|
1271
|
+
if (hasRule(rulesMap, normalized)) {
|
|
1272
|
+
return { matched: true, roles: rulesMap[normalized] ?? [] };
|
|
1252
1273
|
}
|
|
1253
1274
|
const pathSegments = normalized.split("/").filter(Boolean);
|
|
1254
1275
|
for (const [pattern, roles] of Object.entries(rulesMap)) {
|
|
@@ -1271,25 +1292,43 @@ function getRequiredChildGroups(pathname, rulesMap) {
|
|
|
1271
1292
|
}
|
|
1272
1293
|
}
|
|
1273
1294
|
if (matches) {
|
|
1274
|
-
return roles;
|
|
1295
|
+
return { matched: true, roles: roles ?? [] };
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
// Parent-prefix: /leave/types/abc → /leave/types (non-empty roles only)
|
|
1299
|
+
for (let len = pathSegments.length - 1; len >= 1; len--) {
|
|
1300
|
+
const parent = "/" + pathSegments.slice(0, len).join("/");
|
|
1301
|
+
if (hasRule(rulesMap, parent) && (rulesMap[parent]?.length ?? 0) > 0) {
|
|
1302
|
+
return { matched: true, roles: rulesMap[parent] ?? [] };
|
|
1275
1303
|
}
|
|
1276
1304
|
}
|
|
1277
|
-
return [];
|
|
1305
|
+
return { matched: false, roles: [] };
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Returns the required child-group segments for a page pathname.
|
|
1309
|
+
*
|
|
1310
|
+
* Prefer {@link resolveUiRoutePermission} when you need to distinguish
|
|
1311
|
+
* "explicitly public (`[]`)" from "no rule (deny)". This helper returns
|
|
1312
|
+
* `[]` for both cases and is kept for backward compatibility.
|
|
1313
|
+
*/
|
|
1314
|
+
function getRequiredChildGroups(pathname, rulesMap) {
|
|
1315
|
+
return resolveUiRoutePermission(pathname, rulesMap).roles;
|
|
1278
1316
|
}
|
|
1279
1317
|
|
|
1318
|
+
function DeniedCard() {
|
|
1319
|
+
return (jsxRuntime.jsx(erpTheme.Card, { className: "p-6", children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center gap-3 text-center", children: [jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-8 w-8 text-destructive" }), jsxRuntime.jsxs("div", { className: "space-y-1", children: [jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-destructive", children: "\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07" }), jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "\u0E04\u0E38\u0E13\u0E44\u0E21\u0E48\u0E21\u0E35\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E43\u0E19\u0E01\u0E32\u0E23\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E2B\u0E19\u0E49\u0E32\u0E19\u0E35\u0E49 \u0E2B\u0E32\u0E01\u0E04\u0E34\u0E14\u0E27\u0E48\u0E32\u0E04\u0E27\u0E23\u0E40\u0E02\u0E49\u0E32\u0E16\u0E36\u0E07\u0E44\u0E14\u0E49 \u0E01\u0E23\u0E38\u0E13\u0E32\u0E15\u0E34\u0E14\u0E15\u0E48\u0E2D\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25\u0E23\u0E30\u0E1A\u0E1A\u0E02\u0E2D\u0E07\u0E2D\u0E07\u0E04\u0E4C\u0E01\u0E23" })] })] }) }));
|
|
1320
|
+
}
|
|
1280
1321
|
/**
|
|
1281
1322
|
* Permission gate for a page subtree. Looks up the current pathname in the
|
|
1282
1323
|
* permission rules map (live rules from `PermissionRulesProvider`, layered
|
|
1283
1324
|
* over the optional `fallbackRulesMap`), passes the resolved required roles
|
|
1284
|
-
* into the host's `usePermission` probe, and renders one of
|
|
1285
|
-
*
|
|
1286
|
-
* 1. while rules or probe are still loading → centered spinner card
|
|
1287
|
-
* 2. probe says no access → centered "ไม่มีสิทธิ์" card with required vs.
|
|
1288
|
-
* user roles for debugging
|
|
1289
|
-
* 3. otherwise → children
|
|
1325
|
+
* into the host's `usePermission` probe, and renders one of:
|
|
1290
1326
|
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1327
|
+
* 1. rules / probe still loading → `null` (let Suspense / loading.tsx show)
|
|
1328
|
+
* 2. no rule covers this path → deny (fail closed)
|
|
1329
|
+
* 3. rule is explicit public (`[]`) → children
|
|
1330
|
+
* 4. probe says no access → deny card
|
|
1331
|
+
* 5. otherwise → children
|
|
1293
1332
|
*/
|
|
1294
1333
|
function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
|
|
1295
1334
|
const pathname = navigation.usePathname();
|
|
@@ -1300,37 +1339,103 @@ function PermissionProvider({ children, usePermission, fallbackRulesMap, }) {
|
|
|
1300
1339
|
// live rules win over fallback when both define the same key
|
|
1301
1340
|
return { ...fallbackRulesMap, ...rulesMap };
|
|
1302
1341
|
}, [rulesMap, fallbackRulesMap]);
|
|
1303
|
-
const
|
|
1304
|
-
// Hook must run unconditionally (rules of hooks)
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
requiredRoles,
|
|
1342
|
+
const lookup = React.useMemo(() => resolveUiRoutePermission(pathname, mergedMap), [pathname, mergedMap]);
|
|
1343
|
+
// Hook must run unconditionally (rules of hooks).
|
|
1344
|
+
const { hasPermission, isLoading } = usePermission({
|
|
1345
|
+
requiredRoles: lookup.roles,
|
|
1308
1346
|
});
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1347
|
+
if (rulesLoading) {
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
1350
|
+
// Unknown route → deny. Only explicit `required_child_groups: []` is public.
|
|
1351
|
+
if (!lookup.matched) {
|
|
1352
|
+
return jsxRuntime.jsx(DeniedCard, {});
|
|
1353
|
+
}
|
|
1354
|
+
if (lookup.roles.length === 0) {
|
|
1313
1355
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
|
|
1314
1356
|
}
|
|
1315
|
-
|
|
1316
|
-
// Next.js loading.tsx / Suspense fallback show. The old behaviour wrapped
|
|
1317
|
-
// a big "กำลังตรวจสอบสิทธิ์..." card over the top, which both duplicated
|
|
1318
|
-
// the app's own loader and — worse — caused a "ไม่มีสิทธิ์" flash on
|
|
1319
|
-
// fast loads because the probe transiently returned `hasPermission:false,
|
|
1320
|
-
// isLoading:false` before its dependencies (org context, role-segments)
|
|
1321
|
-
// arrived.
|
|
1322
|
-
const isLoadingCombined = rulesLoading || isLoading;
|
|
1357
|
+
const isLoadingCombined = isLoading;
|
|
1323
1358
|
if (isLoadingCombined) {
|
|
1324
1359
|
return null;
|
|
1325
1360
|
}
|
|
1326
1361
|
if (!hasPermission) {
|
|
1327
|
-
return
|
|
1362
|
+
return jsxRuntime.jsx(DeniedCard, {});
|
|
1328
1363
|
}
|
|
1329
1364
|
return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
|
|
1330
1365
|
}
|
|
1331
1366
|
|
|
1367
|
+
/** Cookie name for the cross-subdomain theme choice. */
|
|
1368
|
+
const THEME_COOKIE = "erp-theme";
|
|
1369
|
+
/**
|
|
1370
|
+
* Parent domain to scope the cookie to, so every `*.antniti.com` app reads the
|
|
1371
|
+
* same value. Returns `null` on localhost / bare IPs (host-only cookie).
|
|
1372
|
+
*/
|
|
1373
|
+
function sharedCookieDomain() {
|
|
1374
|
+
if (typeof window === "undefined")
|
|
1375
|
+
return null;
|
|
1376
|
+
const host = window.location.hostname;
|
|
1377
|
+
if (host === "localhost" || /^[0-9.]+$/.test(host))
|
|
1378
|
+
return null;
|
|
1379
|
+
const parts = host.split(".");
|
|
1380
|
+
if (parts.length < 2)
|
|
1381
|
+
return null;
|
|
1382
|
+
return "." + parts.slice(-2).join(".");
|
|
1383
|
+
}
|
|
1384
|
+
function readThemeCookie() {
|
|
1385
|
+
if (typeof document === "undefined")
|
|
1386
|
+
return null;
|
|
1387
|
+
const match = document.cookie.match(new RegExp("(?:^|; )" + THEME_COOKIE + "=([^;]*)"));
|
|
1388
|
+
return match ? decodeURIComponent(match[1]) : null;
|
|
1389
|
+
}
|
|
1390
|
+
function writeThemeCookie(value) {
|
|
1391
|
+
if (typeof document === "undefined")
|
|
1392
|
+
return;
|
|
1393
|
+
const domain = sharedCookieDomain();
|
|
1394
|
+
const parts = [
|
|
1395
|
+
`${THEME_COOKIE}=${encodeURIComponent(value)}`,
|
|
1396
|
+
"path=/",
|
|
1397
|
+
"max-age=31536000", // 1 year
|
|
1398
|
+
"SameSite=Lax",
|
|
1399
|
+
];
|
|
1400
|
+
if (domain)
|
|
1401
|
+
parts.push(`domain=${domain}`);
|
|
1402
|
+
document.cookie = parts.join("; ");
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Bridges next-themes (per-origin localStorage) with a cookie scoped to the
|
|
1406
|
+
* parent domain, so the light/dark/system choice is shared across the
|
|
1407
|
+
* accounting / HR / platform subdomains. On load it adopts the shared cookie;
|
|
1408
|
+
* on every later change it writes the cookie back.
|
|
1409
|
+
*/
|
|
1410
|
+
function ThemeCookieSync() {
|
|
1411
|
+
const { theme, setTheme } = nextThemes.useTheme();
|
|
1412
|
+
const skipFirstWrite = React__namespace.useRef(true);
|
|
1413
|
+
React__namespace.useEffect(() => {
|
|
1414
|
+
const cookie = readThemeCookie();
|
|
1415
|
+
if (cookie) {
|
|
1416
|
+
if (cookie !== theme)
|
|
1417
|
+
setTheme(cookie);
|
|
1418
|
+
}
|
|
1419
|
+
else if (theme) {
|
|
1420
|
+
writeThemeCookie(theme); // seed the shared cookie the first time
|
|
1421
|
+
}
|
|
1422
|
+
// Run once on mount — `theme` is intentionally not a dependency here.
|
|
1423
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1424
|
+
}, []);
|
|
1425
|
+
React__namespace.useEffect(() => {
|
|
1426
|
+
// Skip the initial render; only persist genuine changes (incl. the
|
|
1427
|
+
// setTheme() triggered by adopting the cookie above — harmless re-write).
|
|
1428
|
+
if (skipFirstWrite.current) {
|
|
1429
|
+
skipFirstWrite.current = false;
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
if (theme)
|
|
1433
|
+
writeThemeCookie(theme);
|
|
1434
|
+
}, [theme]);
|
|
1435
|
+
return null;
|
|
1436
|
+
}
|
|
1332
1437
|
function ThemeProvider({ children, ...props }) {
|
|
1333
|
-
return jsxRuntime.
|
|
1438
|
+
return (jsxRuntime.jsxs(nextThemes.ThemeProvider, { ...props, children: [jsxRuntime.jsx(ThemeCookieSync, {}), children] }));
|
|
1334
1439
|
}
|
|
1335
1440
|
|
|
1336
1441
|
const defaultFlags = {
|
|
@@ -1796,6 +1901,7 @@ exports.getRequiredChildGroups = getRequiredChildGroups;
|
|
|
1796
1901
|
exports.isLikelyTransientNetworkError = isLikelyTransientNetworkError;
|
|
1797
1902
|
exports.isPathActive = isPathActive;
|
|
1798
1903
|
exports.normalizePathnameForPermissionRules = normalizePathnameForPermissionRules;
|
|
1904
|
+
exports.resolveUiRoutePermission = resolveUiRoutePermission;
|
|
1799
1905
|
exports.runGlobalRequestPrecheck = runGlobalRequestPrecheck;
|
|
1800
1906
|
exports.runWithTransientRetry = runWithTransientRetry;
|
|
1801
1907
|
exports.setOrganizationId = setOrganizationId;
|