acai-ui-library 0.1.13 → 0.1.14
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/components/alert.cjs +3 -2
- package/dist/components/alert.cjs.map +1 -1
- package/dist/components/alert.js +3 -2
- package/dist/components/alert.js.map +1 -1
- package/dist/components/card.cjs +1 -1
- package/dist/components/card.cjs.map +1 -1
- package/dist/components/card.js +1 -1
- package/dist/components/card.js.map +1 -1
- package/dist/components/checkbox.cjs +2 -1
- package/dist/components/checkbox.cjs.map +1 -1
- package/dist/components/checkbox.js +2 -1
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/data-table.cjs +2 -1
- package/dist/components/data-table.cjs.map +1 -1
- package/dist/components/data-table.js +2 -1
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/date-picker.cjs.map +1 -1
- package/dist/components/date-picker.js.map +1 -1
- package/dist/components/impersonation-banner.cjs +137 -0
- package/dist/components/impersonation-banner.cjs.map +1 -0
- package/dist/components/impersonation-banner.d.cts +36 -0
- package/dist/components/impersonation-banner.d.ts +36 -0
- package/dist/components/impersonation-banner.js +101 -0
- package/dist/components/impersonation-banner.js.map +1 -0
- package/dist/components/input.cjs +2 -1
- package/dist/components/input.cjs.map +1 -1
- package/dist/components/input.js +2 -1
- package/dist/components/input.js.map +1 -1
- package/dist/components/item.cjs +1 -2
- package/dist/components/item.cjs.map +1 -1
- package/dist/components/item.js +1 -2
- package/dist/components/item.js.map +1 -1
- package/dist/components/pagination.cjs +3 -1
- package/dist/components/pagination.cjs.map +1 -1
- package/dist/components/pagination.d.cts +1 -1
- package/dist/components/pagination.d.ts +1 -1
- package/dist/components/pagination.js +3 -1
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/read-only-guard.cjs +103 -0
- package/dist/components/read-only-guard.cjs.map +1 -0
- package/dist/components/read-only-guard.d.cts +40 -0
- package/dist/components/read-only-guard.d.ts +40 -0
- package/dist/components/read-only-guard.js +64 -0
- package/dist/components/read-only-guard.js.map +1 -0
- package/dist/components/stepper.cjs.map +1 -1
- package/dist/components/stepper.js.map +1 -1
- package/dist/components/typography.cjs +4 -4
- package/dist/components/typography.cjs.map +1 -1
- package/dist/components/typography.js +4 -4
- package/dist/components/typography.js.map +1 -1
- package/dist/index.cjs +236 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +224 -12
- package/dist/index.js.map +1 -1
- package/dist/lib/impersonation.cjs +127 -0
- package/dist/lib/impersonation.cjs.map +1 -0
- package/dist/lib/impersonation.d.cts +45 -0
- package/dist/lib/impersonation.d.ts +45 -0
- package/dist/lib/impersonation.js +86 -0
- package/dist/lib/impersonation.js.map +1 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -62,6 +62,9 @@ export { Toaster } from './components/sonner.cjs';
|
|
|
62
62
|
export { Body, Caption, Code, H1, H2, H3, H4, Lead, Muted, Small, TypographyColor, TypographyWeight } from './components/typography.cjs';
|
|
63
63
|
export { ThemeProvider } from './components/theme-provider.cjs';
|
|
64
64
|
export { Banner, BannerProps } from './components/banner.cjs';
|
|
65
|
+
export { ImpersonationBanner, ImpersonationBannerProps, formatRemaining } from './components/impersonation-banner.cjs';
|
|
66
|
+
export { READ_ONLY_REASON, ReadOnlyGuard, ReadOnlyGuardProps, isGuarded } from './components/read-only-guard.cjs';
|
|
67
|
+
export { INACTIVE_IMPERSONATION, ImpersonationMode, ImpersonationState, OPERATION_CLASSES, OperationClass, approvedOpsOf, decodeJwtPayload, modeOfOps, readImpersonation, useImpersonation } from './lib/impersonation.cjs';
|
|
65
68
|
export { ThemeProviderProps, useTheme } from 'next-themes';
|
|
66
69
|
export { toast } from 'sonner';
|
|
67
70
|
import 'clsx';
|
package/dist/index.d.ts
CHANGED
|
@@ -62,6 +62,9 @@ export { Toaster } from './components/sonner.js';
|
|
|
62
62
|
export { Body, Caption, Code, H1, H2, H3, H4, Lead, Muted, Small, TypographyColor, TypographyWeight } from './components/typography.js';
|
|
63
63
|
export { ThemeProvider } from './components/theme-provider.js';
|
|
64
64
|
export { Banner, BannerProps } from './components/banner.js';
|
|
65
|
+
export { ImpersonationBanner, ImpersonationBannerProps, formatRemaining } from './components/impersonation-banner.js';
|
|
66
|
+
export { READ_ONLY_REASON, ReadOnlyGuard, ReadOnlyGuardProps, isGuarded } from './components/read-only-guard.js';
|
|
67
|
+
export { INACTIVE_IMPERSONATION, ImpersonationMode, ImpersonationState, OPERATION_CLASSES, OperationClass, approvedOpsOf, decodeJwtPayload, modeOfOps, readImpersonation, useImpersonation } from './lib/impersonation.js';
|
|
65
68
|
export { ThemeProviderProps, useTheme } from 'next-themes';
|
|
66
69
|
export { toast } from 'sonner';
|
|
67
70
|
import 'clsx';
|
package/dist/index.js
CHANGED
|
@@ -126,7 +126,8 @@ var Input = React2.forwardRef(
|
|
|
126
126
|
onChange,
|
|
127
127
|
...props
|
|
128
128
|
}, ref) => {
|
|
129
|
-
const
|
|
129
|
+
const generatedId = React2.useId();
|
|
130
|
+
const inputId = id || generatedId;
|
|
130
131
|
const isControlled = value !== void 0;
|
|
131
132
|
const [internalValue, setInternalValue] = React2.useState(defaultValue);
|
|
132
133
|
const innerRef = React2.useRef(null);
|
|
@@ -499,7 +500,8 @@ var checkboxIconSize = {
|
|
|
499
500
|
lg: "size-4"
|
|
500
501
|
};
|
|
501
502
|
var Checkbox = React7.forwardRef(({ className, label, id, variant, size = "default", ...props }, ref) => {
|
|
502
|
-
const
|
|
503
|
+
const generatedId = React7.useId();
|
|
504
|
+
const checkboxId = id || generatedId;
|
|
503
505
|
return /* @__PURE__ */ jsxs6("div", { className: "inline-flex items-center gap-2", children: [
|
|
504
506
|
/* @__PURE__ */ jsx7(
|
|
505
507
|
CheckboxPrimitive.Root,
|
|
@@ -1249,12 +1251,13 @@ var Alert = React17.forwardRef(({ className, variant, ...props }, ref) => /* @__
|
|
|
1249
1251
|
}
|
|
1250
1252
|
));
|
|
1251
1253
|
Alert.displayName = "Alert";
|
|
1252
|
-
var AlertTitle = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
1254
|
+
var AlertTitle = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx19(
|
|
1253
1255
|
"h5",
|
|
1254
1256
|
{
|
|
1255
1257
|
ref,
|
|
1256
1258
|
className: cn("mb-1 font-medium leading-none tracking-tight", className),
|
|
1257
|
-
...props
|
|
1259
|
+
...props,
|
|
1260
|
+
children
|
|
1258
1261
|
}
|
|
1259
1262
|
));
|
|
1260
1263
|
AlertTitle.displayName = "AlertTitle";
|
|
@@ -1626,7 +1629,7 @@ var Item4 = React24.forwardRef(
|
|
|
1626
1629
|
({ className, clickable, onClick, children, ...props }, ref) => {
|
|
1627
1630
|
const baseClasses = "flex items-center gap-3 px-4 py-3 rounded-md";
|
|
1628
1631
|
const interactiveClasses = "cursor-pointer transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--hc-primary-500)] w-full text-start";
|
|
1629
|
-
if (clickable) {
|
|
1632
|
+
if (clickable || onClick) {
|
|
1630
1633
|
return /* @__PURE__ */ jsx27(
|
|
1631
1634
|
"button",
|
|
1632
1635
|
{
|
|
@@ -1643,7 +1646,6 @@ var Item4 = React24.forwardRef(
|
|
|
1643
1646
|
"div",
|
|
1644
1647
|
{
|
|
1645
1648
|
ref,
|
|
1646
|
-
onClick,
|
|
1647
1649
|
className: cn(baseClasses, className),
|
|
1648
1650
|
...props,
|
|
1649
1651
|
children
|
|
@@ -3209,7 +3211,7 @@ var CardHeader = React39.forwardRef(
|
|
|
3209
3211
|
);
|
|
3210
3212
|
CardHeader.displayName = "CardHeader";
|
|
3211
3213
|
var CardTitle = React39.forwardRef(
|
|
3212
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx42("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
|
|
3214
|
+
({ className, children, ...props }, ref) => /* @__PURE__ */ jsx42("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props, children })
|
|
3213
3215
|
);
|
|
3214
3216
|
CardTitle.displayName = "CardTitle";
|
|
3215
3217
|
var CardDescription = React39.forwardRef(
|
|
@@ -3584,6 +3586,7 @@ var PaginationLink = ({
|
|
|
3584
3586
|
className,
|
|
3585
3587
|
isActive,
|
|
3586
3588
|
size = "icon",
|
|
3589
|
+
children,
|
|
3587
3590
|
...props
|
|
3588
3591
|
}) => /* @__PURE__ */ jsx47(
|
|
3589
3592
|
"a",
|
|
@@ -3596,7 +3599,8 @@ var PaginationLink = ({
|
|
|
3596
3599
|
}),
|
|
3597
3600
|
className
|
|
3598
3601
|
),
|
|
3599
|
-
...props
|
|
3602
|
+
...props,
|
|
3603
|
+
children
|
|
3600
3604
|
}
|
|
3601
3605
|
);
|
|
3602
3606
|
PaginationLink.displayName = "PaginationLink";
|
|
@@ -4354,19 +4358,19 @@ function resolveColor(color, defaultColorClass) {
|
|
|
4354
4358
|
return colorMap[color];
|
|
4355
4359
|
}
|
|
4356
4360
|
var H1 = React50.forwardRef(
|
|
4357
|
-
({ className, weight = "semibold", color, ...props }, ref) => /* @__PURE__ */ jsx58("h1", { ref, className: cn("scroll-m-20 text-3xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props })
|
|
4361
|
+
({ className, weight = "semibold", color, children, ...props }, ref) => /* @__PURE__ */ jsx58("h1", { ref, className: cn("scroll-m-20 text-3xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props, children })
|
|
4358
4362
|
);
|
|
4359
4363
|
H1.displayName = "H1";
|
|
4360
4364
|
var H2 = React50.forwardRef(
|
|
4361
|
-
({ className, weight = "semibold", color, ...props }, ref) => /* @__PURE__ */ jsx58("h2", { ref, className: cn("scroll-m-20 text-2xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props })
|
|
4365
|
+
({ className, weight = "semibold", color, children, ...props }, ref) => /* @__PURE__ */ jsx58("h2", { ref, className: cn("scroll-m-20 text-2xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props, children })
|
|
4362
4366
|
);
|
|
4363
4367
|
H2.displayName = "H2";
|
|
4364
4368
|
var H3 = React50.forwardRef(
|
|
4365
|
-
({ className, weight = "semibold", color, ...props }, ref) => /* @__PURE__ */ jsx58("h3", { ref, className: cn("scroll-m-20 text-xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props })
|
|
4369
|
+
({ className, weight = "semibold", color, children, ...props }, ref) => /* @__PURE__ */ jsx58("h3", { ref, className: cn("scroll-m-20 text-xl tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props, children })
|
|
4366
4370
|
);
|
|
4367
4371
|
H3.displayName = "H3";
|
|
4368
4372
|
var H4 = React50.forwardRef(
|
|
4369
|
-
({ className, weight = "semibold", color, ...props }, ref) => /* @__PURE__ */ jsx58("h4", { ref, className: cn("scroll-m-20 text-lg tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props })
|
|
4373
|
+
({ className, weight = "semibold", color, children, ...props }, ref) => /* @__PURE__ */ jsx58("h4", { ref, className: cn("scroll-m-20 text-lg tracking-tight", resolveColor(color, "text-foreground"), weightMap[weight], className), ...props, children })
|
|
4370
4374
|
);
|
|
4371
4375
|
H4.displayName = "H4";
|
|
4372
4376
|
var Body = React50.forwardRef(
|
|
@@ -4451,6 +4455,202 @@ var Banner = React51.forwardRef(
|
|
|
4451
4455
|
)
|
|
4452
4456
|
);
|
|
4453
4457
|
Banner.displayName = "Banner";
|
|
4458
|
+
|
|
4459
|
+
// src/components/impersonation-banner.tsx
|
|
4460
|
+
import * as React52 from "react";
|
|
4461
|
+
import { Fragment as Fragment3, jsx as jsx61, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
4462
|
+
function toMs(value) {
|
|
4463
|
+
if (value instanceof Date) return value.getTime();
|
|
4464
|
+
if (typeof value === "number") return value;
|
|
4465
|
+
const parsed = Date.parse(value);
|
|
4466
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
4467
|
+
}
|
|
4468
|
+
function formatRemaining(ms) {
|
|
4469
|
+
const total = Math.max(0, Math.floor(ms / 1e3));
|
|
4470
|
+
const h = Math.floor(total / 3600);
|
|
4471
|
+
const m = Math.floor(total % 3600 / 60);
|
|
4472
|
+
const s = total % 60;
|
|
4473
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
4474
|
+
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
|
|
4475
|
+
}
|
|
4476
|
+
var ImpersonationBanner = React52.forwardRef(
|
|
4477
|
+
({ actorLabel, mode, expiresAt, onExit, targetLabel, ops, inline = false, onExpire, className }, ref) => {
|
|
4478
|
+
const endMs = toMs(expiresAt);
|
|
4479
|
+
const [now, setNow] = React52.useState(() => Date.now());
|
|
4480
|
+
const remaining = endMs - now;
|
|
4481
|
+
const expired = remaining <= 0;
|
|
4482
|
+
const onExpireRef = React52.useRef(onExpire);
|
|
4483
|
+
onExpireRef.current = onExpire;
|
|
4484
|
+
React52.useEffect(() => {
|
|
4485
|
+
if (expired) return void 0;
|
|
4486
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
4487
|
+
return () => clearInterval(timer);
|
|
4488
|
+
}, [expired]);
|
|
4489
|
+
const firedFor = React52.useRef(null);
|
|
4490
|
+
React52.useEffect(() => {
|
|
4491
|
+
if (expired && firedFor.current !== endMs) {
|
|
4492
|
+
firedFor.current = endMs;
|
|
4493
|
+
onExpireRef.current?.();
|
|
4494
|
+
}
|
|
4495
|
+
}, [expired, endMs]);
|
|
4496
|
+
const isRead = mode === "READ";
|
|
4497
|
+
const modeText = isRead ? "Read-only" : "Changes allowed";
|
|
4498
|
+
const opsText = ops && ops.length > 0 ? ops.join(", ") : null;
|
|
4499
|
+
return /* @__PURE__ */ jsxs41(Fragment3, { children: [
|
|
4500
|
+
inline ? null : /* @__PURE__ */ jsx61("div", { "aria-hidden": "true", className: "h-12", "data-impersonation-spacer": "" }),
|
|
4501
|
+
/* @__PURE__ */ jsxs41(
|
|
4502
|
+
"div",
|
|
4503
|
+
{
|
|
4504
|
+
ref,
|
|
4505
|
+
role: "alert",
|
|
4506
|
+
"data-impersonation-banner": "",
|
|
4507
|
+
"data-mode": mode,
|
|
4508
|
+
className: cn(
|
|
4509
|
+
inline ? "relative" : "fixed inset-x-0 top-0 z-[2147483000]",
|
|
4510
|
+
"flex h-12 items-center gap-3 border-b-2 px-4 text-sm font-medium shadow-md",
|
|
4511
|
+
isRead ? "border-black bg-[#FDE047] text-black" : "border-white bg-[#991B1B] text-white",
|
|
4512
|
+
className
|
|
4513
|
+
),
|
|
4514
|
+
children: [
|
|
4515
|
+
/* @__PURE__ */ jsx61("span", { className: "shrink-0 rounded px-2 py-0.5 text-xs font-bold uppercase tracking-wide ring-1 ring-current", children: "Support session" }),
|
|
4516
|
+
/* @__PURE__ */ jsxs41("p", { className: "min-w-0 flex-1 truncate", children: [
|
|
4517
|
+
/* @__PURE__ */ jsx61("span", { className: "font-semibold", children: actorLabel }),
|
|
4518
|
+
targetLabel ? /* @__PURE__ */ jsxs41(Fragment3, { children: [
|
|
4519
|
+
" is acting as ",
|
|
4520
|
+
/* @__PURE__ */ jsx61("span", { className: "font-semibold", children: targetLabel })
|
|
4521
|
+
] }) : " is acting as this user",
|
|
4522
|
+
" \xB7 ",
|
|
4523
|
+
/* @__PURE__ */ jsx61("span", { "data-testid": "impersonation-mode", children: modeText }),
|
|
4524
|
+
opsText ? /* @__PURE__ */ jsx61("span", { className: "hidden sm:inline", children: ` (${opsText})` }) : null
|
|
4525
|
+
] }),
|
|
4526
|
+
/* @__PURE__ */ jsx61("span", { className: "shrink-0 tabular-nums", "aria-live": "off", title: "Time left in this support session", children: /* @__PURE__ */ jsx61("time", { "data-testid": "impersonation-countdown", dateTime: new Date(endMs).toISOString(), children: expired ? "Expired" : formatRemaining(remaining) }) }),
|
|
4527
|
+
/* @__PURE__ */ jsx61(
|
|
4528
|
+
"button",
|
|
4529
|
+
{
|
|
4530
|
+
type: "button",
|
|
4531
|
+
onClick: onExit,
|
|
4532
|
+
className: cn(
|
|
4533
|
+
"shrink-0 rounded-md px-3 py-1 text-sm font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
4534
|
+
isRead ? "bg-black text-[#FDE047] hover:bg-black/85 focus-visible:ring-black focus-visible:ring-offset-[#FDE047]" : "bg-white text-[#991B1B] hover:bg-white/90 focus-visible:ring-white focus-visible:ring-offset-[#991B1B]"
|
|
4535
|
+
),
|
|
4536
|
+
children: "Exit"
|
|
4537
|
+
}
|
|
4538
|
+
)
|
|
4539
|
+
]
|
|
4540
|
+
}
|
|
4541
|
+
)
|
|
4542
|
+
] });
|
|
4543
|
+
}
|
|
4544
|
+
);
|
|
4545
|
+
ImpersonationBanner.displayName = "ImpersonationBanner";
|
|
4546
|
+
|
|
4547
|
+
// src/components/read-only-guard.tsx
|
|
4548
|
+
import { Fragment as Fragment4, jsx as jsx62, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
4549
|
+
var READ_ONLY_REASON = "Read-only support session: this action is turned off.";
|
|
4550
|
+
function isGuarded({ mode, ops, op = "WRITE" }) {
|
|
4551
|
+
if (ops) return !ops.includes(op);
|
|
4552
|
+
return mode === "READ";
|
|
4553
|
+
}
|
|
4554
|
+
function ReadOnlyGuard({ mode, ops, op = "WRITE", reason = READ_ONLY_REASON, className, children }) {
|
|
4555
|
+
if (!isGuarded({ mode, ops, op })) return /* @__PURE__ */ jsx62(Fragment4, { children });
|
|
4556
|
+
const swallow = (e) => {
|
|
4557
|
+
e.preventDefault();
|
|
4558
|
+
e.stopPropagation();
|
|
4559
|
+
};
|
|
4560
|
+
return /* @__PURE__ */ jsx62(TooltipProvider, { delayDuration: 150, children: /* @__PURE__ */ jsxs42(Tooltip, { children: [
|
|
4561
|
+
/* @__PURE__ */ jsx62(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx62(
|
|
4562
|
+
"span",
|
|
4563
|
+
{
|
|
4564
|
+
tabIndex: 0,
|
|
4565
|
+
"data-read-only": "",
|
|
4566
|
+
"aria-disabled": "true",
|
|
4567
|
+
className: cn("inline-flex cursor-not-allowed", className),
|
|
4568
|
+
onClickCapture: swallow,
|
|
4569
|
+
onSubmitCapture: swallow,
|
|
4570
|
+
children: /* @__PURE__ */ jsx62("fieldset", { disabled: true, className: "contents", children })
|
|
4571
|
+
}
|
|
4572
|
+
) }),
|
|
4573
|
+
/* @__PURE__ */ jsx62(TooltipContent, { children: reason })
|
|
4574
|
+
] }) });
|
|
4575
|
+
}
|
|
4576
|
+
ReadOnlyGuard.displayName = "ReadOnlyGuard";
|
|
4577
|
+
|
|
4578
|
+
// src/lib/impersonation.ts
|
|
4579
|
+
import * as React53 from "react";
|
|
4580
|
+
var OPERATION_CLASSES = ["READ", "WRITE", "DESTRUCTIVE", "FINANCIAL", "SECURITY"];
|
|
4581
|
+
var PLATFORM_BLOCKED = /* @__PURE__ */ new Set(["FINANCIAL", "SECURITY"]);
|
|
4582
|
+
var INACTIVE_IMPERSONATION = Object.freeze({
|
|
4583
|
+
active: false,
|
|
4584
|
+
mode: null,
|
|
4585
|
+
ops: [],
|
|
4586
|
+
actorSub: null,
|
|
4587
|
+
actorUserType: null,
|
|
4588
|
+
sessionId: null,
|
|
4589
|
+
expiresAt: null
|
|
4590
|
+
});
|
|
4591
|
+
function base64UrlDecode(segment) {
|
|
4592
|
+
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
4593
|
+
const padded = b64 + "=".repeat((4 - b64.length % 4) % 4);
|
|
4594
|
+
const binary = atob(padded);
|
|
4595
|
+
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
4596
|
+
return new TextDecoder().decode(bytes);
|
|
4597
|
+
}
|
|
4598
|
+
function decodeJwtPayload(token) {
|
|
4599
|
+
if (typeof token !== "string") return null;
|
|
4600
|
+
const parts = token.split(".");
|
|
4601
|
+
if (parts.length !== 3 || !parts[1]) return null;
|
|
4602
|
+
try {
|
|
4603
|
+
const payload = JSON.parse(base64UrlDecode(parts[1]));
|
|
4604
|
+
return payload !== null && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4605
|
+
} catch {
|
|
4606
|
+
return null;
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
var nonEmpty = (v) => typeof v === "string" && v.trim() !== "" ? v : null;
|
|
4610
|
+
function approvedOpsOf(claims) {
|
|
4611
|
+
let ops;
|
|
4612
|
+
if (Array.isArray(claims.imp_ops)) ops = claims.imp_ops.filter((v) => typeof v === "string");
|
|
4613
|
+
else if (claims.imp_mode === "WRITE") ops = ["READ", "WRITE"];
|
|
4614
|
+
else ops = ["READ"];
|
|
4615
|
+
const set = new Set(ops.filter((op) => !PLATFORM_BLOCKED.has(op)));
|
|
4616
|
+
return OPERATION_CLASSES.filter((c) => set.has(c));
|
|
4617
|
+
}
|
|
4618
|
+
function modeOfOps(ops) {
|
|
4619
|
+
return ops.every((op) => op === "READ") ? "READ" : "WRITE";
|
|
4620
|
+
}
|
|
4621
|
+
function readImpersonation(token, now = Date.now()) {
|
|
4622
|
+
const claims = decodeJwtPayload(token);
|
|
4623
|
+
if (!claims) return INACTIVE_IMPERSONATION;
|
|
4624
|
+
const amr = Array.isArray(claims.amr) ? claims.amr : [];
|
|
4625
|
+
if (!amr.includes("impersonation")) return INACTIVE_IMPERSONATION;
|
|
4626
|
+
const act = claims.act !== null && typeof claims.act === "object" ? claims.act : {};
|
|
4627
|
+
const actorSub = nonEmpty(act.sub);
|
|
4628
|
+
const sessionId = nonEmpty(claims.imp_sid);
|
|
4629
|
+
const expiresAt = typeof claims.exp === "number" ? new Date(claims.exp * 1e3) : null;
|
|
4630
|
+
if (!actorSub || !sessionId || !expiresAt || expiresAt.getTime() <= now) return INACTIVE_IMPERSONATION;
|
|
4631
|
+
const ops = approvedOpsOf(claims);
|
|
4632
|
+
return {
|
|
4633
|
+
active: true,
|
|
4634
|
+
mode: modeOfOps(ops),
|
|
4635
|
+
ops,
|
|
4636
|
+
actorSub,
|
|
4637
|
+
actorUserType: nonEmpty(act.userType),
|
|
4638
|
+
sessionId,
|
|
4639
|
+
expiresAt
|
|
4640
|
+
};
|
|
4641
|
+
}
|
|
4642
|
+
function useImpersonation(token) {
|
|
4643
|
+
const [tick, setTick] = React53.useState(0);
|
|
4644
|
+
const state = React53.useMemo(() => readImpersonation(token), [token, tick]);
|
|
4645
|
+
const expiresAtMs = state.expiresAt?.getTime() ?? null;
|
|
4646
|
+
React53.useEffect(() => {
|
|
4647
|
+
if (expiresAtMs === null) return void 0;
|
|
4648
|
+
const delay = Math.max(0, expiresAtMs - Date.now());
|
|
4649
|
+
const timer = setTimeout(() => setTick((t) => t + 1), Math.min(delay + 50, 2147e6));
|
|
4650
|
+
return () => clearTimeout(timer);
|
|
4651
|
+
}, [expiresAtMs]);
|
|
4652
|
+
return state;
|
|
4653
|
+
}
|
|
4454
4654
|
export {
|
|
4455
4655
|
Accordion,
|
|
4456
4656
|
AccordionContent,
|
|
@@ -4595,7 +4795,9 @@ export {
|
|
|
4595
4795
|
HoverCard,
|
|
4596
4796
|
HoverCardContent,
|
|
4597
4797
|
HoverCardTrigger,
|
|
4798
|
+
INACTIVE_IMPERSONATION,
|
|
4598
4799
|
IconInfoLineIcon,
|
|
4800
|
+
ImpersonationBanner,
|
|
4599
4801
|
Input,
|
|
4600
4802
|
InputAddon,
|
|
4601
4803
|
InputGroup,
|
|
@@ -4637,6 +4839,7 @@ export {
|
|
|
4637
4839
|
NavigationMenuList,
|
|
4638
4840
|
NavigationMenuTrigger,
|
|
4639
4841
|
NavigationMenuViewport,
|
|
4842
|
+
OPERATION_CLASSES,
|
|
4640
4843
|
Pagination,
|
|
4641
4844
|
PaginationContent,
|
|
4642
4845
|
PaginationEllipsis,
|
|
@@ -4649,8 +4852,10 @@ export {
|
|
|
4649
4852
|
PopoverContent,
|
|
4650
4853
|
PopoverTrigger,
|
|
4651
4854
|
Progress,
|
|
4855
|
+
READ_ONLY_REASON,
|
|
4652
4856
|
RadioGroup,
|
|
4653
4857
|
RadioGroupItem,
|
|
4858
|
+
ReadOnlyGuard,
|
|
4654
4859
|
ResizableHandle,
|
|
4655
4860
|
ResizablePanel,
|
|
4656
4861
|
ResizablePanelGroup,
|
|
@@ -4721,17 +4926,24 @@ export {
|
|
|
4721
4926
|
TooltipProvider,
|
|
4722
4927
|
TooltipTrigger,
|
|
4723
4928
|
alertVariants,
|
|
4929
|
+
approvedOpsOf,
|
|
4724
4930
|
buttonVariants,
|
|
4725
4931
|
checkboxVariants,
|
|
4726
4932
|
cn,
|
|
4933
|
+
decodeJwtPayload,
|
|
4727
4934
|
emptyStateVariants,
|
|
4935
|
+
formatRemaining,
|
|
4728
4936
|
inputVariants,
|
|
4937
|
+
isGuarded,
|
|
4938
|
+
modeOfOps,
|
|
4729
4939
|
navigationMenuTriggerStyle,
|
|
4940
|
+
readImpersonation,
|
|
4730
4941
|
spinnerVariants,
|
|
4731
4942
|
switchVariants,
|
|
4732
4943
|
toast,
|
|
4733
4944
|
toggleVariants,
|
|
4734
4945
|
useFormField,
|
|
4946
|
+
useImpersonation,
|
|
4735
4947
|
useSidebar,
|
|
4736
4948
|
useTheme2 as useTheme
|
|
4737
4949
|
};
|