diff --git a/packages/manager/package.json b/packages/manager/package.json
index 676546b1543..bb910a6c8be 100644
--- a/packages/manager/package.json
+++ b/packages/manager/package.json
@@ -45,7 +45,9 @@
"@tanstack/react-query-devtools": "5.51.24",
"@tanstack/react-router": "^1.111.11",
"@xterm/xterm": "^5.5.0",
- "akamai-cds-react-components": "0.1.0",
+ "@akamai/cds-components": "0.0.0-20260407204557",
+ "@akamai/cds-icons": "0.0.0-20260407204557",
+ "@akamai/cds-tokens": "0.0.0-20260407204557",
"algoliasearch": "^4.14.3",
"axios": "~1.13.5",
"braintree-web": "^3.92.2",
diff --git a/packages/manager/src/components/ImageSelect/ImageSelectTable.tsx b/packages/manager/src/components/ImageSelect/ImageSelectTable.tsx
index 46c5456320c..e2296485ece 100644
--- a/packages/manager/src/components/ImageSelect/ImageSelectTable.tsx
+++ b/packages/manager/src/components/ImageSelect/ImageSelectTable.tsx
@@ -17,7 +17,7 @@ import {
useTheme,
} from '@linode/ui';
import useMediaQuery from '@mui/material/useMediaQuery';
-import { Pagination } from 'akamai-cds-react-components/Pagination';
+import { Pagination } from '@akamai/cds-components/react/Pagination';
import {
Table,
TableBody,
@@ -25,7 +25,7 @@ import {
TableHead,
TableHeaderCell,
TableRow,
-} from 'akamai-cds-react-components/Table';
+} from '@akamai/cds-components/react/Table';
import React, { useState } from 'react';
import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField';
diff --git a/packages/manager/src/components/ImageSelect/ImageSelectTableRow.tsx b/packages/manager/src/components/ImageSelect/ImageSelectTableRow.tsx
index 98dd251e9a4..199e6dbb2f9 100644
--- a/packages/manager/src/components/ImageSelect/ImageSelectTableRow.tsx
+++ b/packages/manager/src/components/ImageSelect/ImageSelectTableRow.tsx
@@ -7,7 +7,7 @@ import {
} from '@linode/ui';
import { convertStorageUnit, pluralize } from '@linode/utilities';
import useMediaQuery from '@mui/material/useMediaQuery';
-import { TableCell, TableRow } from 'akamai-cds-react-components/Table';
+import { TableCell, TableRow } from '@akamai/cds-components/react/Table';
import React from 'react';
import CloudInitIcon from 'src/assets/icons/cloud-init.svg';
diff --git a/packages/manager/src/features/Account/SwitchAccounts/ChildAccountsTable.tsx b/packages/manager/src/features/Account/SwitchAccounts/ChildAccountsTable.tsx
index 524960b2cb0..6ccad3cc06f 100644
--- a/packages/manager/src/features/Account/SwitchAccounts/ChildAccountsTable.tsx
+++ b/packages/manager/src/features/Account/SwitchAccounts/ChildAccountsTable.tsx
@@ -1,11 +1,11 @@
import { Box, CircleProgress, LinkButton, useTheme } from '@linode/ui';
-import { Pagination } from 'akamai-cds-react-components/Pagination';
+import { Pagination } from '@akamai/cds-components/react/Pagination';
import {
Table,
TableBody,
TableCell,
TableRow,
-} from 'akamai-cds-react-components/Table';
+} from '@akamai/cds-components/react/Table';
import React from 'react';
import { MIN_PAGE_SIZE } from 'src/components/PaginationFooter/PaginationFooter.constants';
diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPoolRow.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPoolRow.tsx
index a8fe2fe0c43..39e72ae2392 100644
--- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPoolRow.tsx
+++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPoolRow.tsx
@@ -1,5 +1,5 @@
import { Hidden } from '@linode/ui';
-import { TableCell, TableRow } from 'akamai-cds-react-components/Table';
+import { TableCell, TableRow } from '@akamai/cds-components/react/Table';
import * as React from 'react';
import { ActionMenu } from 'src/components/ActionMenu/ActionMenu';
diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPools.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPools.tsx
index b6664b2cc9f..4eee774475b 100644
--- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPools.tsx
+++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseNetworking/DatabaseConnectionPools.tsx
@@ -9,7 +9,7 @@ import {
} from '@linode/ui';
import Grid from '@mui/material/Grid';
import { useTheme } from '@mui/material/styles';
-import { Pagination } from 'akamai-cds-react-components/Pagination';
+import { Pagination } from '@akamai/cds-components/react/Pagination';
import {
Table,
TableBody,
@@ -17,7 +17,7 @@ import {
TableHead,
TableHeaderCell,
TableRow,
-} from 'akamai-cds-react-components/Table';
+} from '@akamai/cds-components/react/Table';
import React from 'react';
import { Link } from 'src/components/Link';
diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx
index 3eaeba8e5e9..938d366b995 100644
--- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx
+++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx
@@ -1,13 +1,13 @@
import { Hidden } from '@linode/ui';
import { useTheme } from '@mui/material/styles';
-import { Pagination } from 'akamai-cds-react-components/Pagination';
+import { Pagination } from '@akamai/cds-components/react/Pagination';
import {
Table,
TableBody,
TableHead,
TableHeaderCell,
TableRow,
-} from 'akamai-cds-react-components/Table';
+} from '@akamai/cds-components/react/Table';
import React from 'react';
import { MIN_PAGE_SIZE } from 'src/components/PaginationFooter/PaginationFooter.constants';
diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx
index decaf48b49a..9734c4219e0 100644
--- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx
+++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx
@@ -5,7 +5,7 @@ import {
} from '@linode/queries';
import { Chip, Hidden } from '@linode/ui';
import { formatStorageUnits } from '@linode/utilities';
-import { TableCell, TableRow } from 'akamai-cds-react-components/Table';
+import { TableCell, TableRow } from '@akamai/cds-components/react/Table';
import * as React from 'react';
import { Link } from 'src/components/Link';
diff --git a/packages/manager/src/features/IAM/Delegations/UpdateDelegationForm.tsx b/packages/manager/src/features/IAM/Delegations/UpdateDelegationForm.tsx
index 332487cdf59..bebb6d5b701 100644
--- a/packages/manager/src/features/IAM/Delegations/UpdateDelegationForm.tsx
+++ b/packages/manager/src/features/IAM/Delegations/UpdateDelegationForm.tsx
@@ -1,27 +1,30 @@
+import {
+ Button,
+ Checkbox,
+ LoadingSpinner,
+ Table,
+ TableBody,
+ TableCell,
+ TableRow,
+ TextField,
+} from '@akamai/cds-components/react';
import {
useAccountUsersInfiniteQuery,
useAllAccountUsersQuery,
useUpdateChildAccountDelegatesQuery,
} from '@linode/queries';
-import {
- ActionsPanel,
- Autocomplete,
- CloseIcon,
- IconButton,
- Notice,
- Paper,
- Stack,
- Typography,
-} from '@linode/ui';
+import { ActionsPanel, Notice, Typography } from '@linode/ui';
import { useDebouncedValue } from '@linode/utilities';
import { useTheme } from '@mui/material';
import { enqueueSnackbar } from 'notistack';
import * as React from 'react';
-import { Controller, FormProvider, useForm } from 'react-hook-form';
+import { FormProvider, useForm } from 'react-hook-form';
import { usePermissions } from '../hooks/usePermissions';
-import { IAM_PARENT_USERS_PENDO_IDS } from '../Shared/constants';
-import { INTERNAL_ERROR_NO_CHANGES_SAVED } from '../Shared/constants';
+import {
+ IAM_PARENT_USERS_PENDO_IDS,
+ INTERNAL_ERROR_NO_CHANGES_SAVED,
+} from '../Shared/constants';
import { getPlaceholder } from '../Shared/Entities/utils';
import type {
@@ -64,8 +67,13 @@ export const UpdateDelegationForm = ({
username: { '+contains': debouncedInputValue },
};
- const { data, error, fetchNextPage, hasNextPage, isFetching } =
- useAccountUsersInfiniteQuery(apiFilter);
+ const {
+ data,
+ error: fetchError,
+ fetchNextPage,
+ hasNextPage,
+ isFetching,
+ } = useAccountUsersInfiniteQuery(apiFilter);
const totalUserCount = data?.pages[0]?.results ?? 0;
@@ -77,8 +85,6 @@ export const UpdateDelegationForm = ({
user_type: 'parent',
});
- const isSelectAllFetching = allUserSelected && isFetchingAllUsers;
-
const { mutateAsync: updateDelegates } =
useUpdateChildAccountDelegatesQuery();
@@ -89,7 +95,6 @@ export const UpdateDelegationForm = ({
});
const {
- control,
formState: { errors, isSubmitting },
handleSubmit,
reset,
@@ -120,10 +125,6 @@ export const UpdateDelegationForm = ({
const isSearching =
inputValue.length > 0 && debouncedInputValue !== inputValue;
- const isLoadingOptions = isFetching || isFetchingAllUsers;
-
- const showNoOptionsText = !isLoadingOptions && !isSearching;
-
const onSubmit = async (values: UpdateDelegationsFormValues) => {
const usersList = values.users.map((user) => user.value);
@@ -158,6 +159,69 @@ export const UpdateDelegationForm = ({
reset();
onClose();
setAllUserSelected(false);
+ setShowOnly(false);
+ };
+
+ const [showOnly, setShowOnly] = React.useState(false);
+
+ const view = React.useMemo((): Array<{
+ name: string;
+ option: UserOption;
+ rank: number;
+ }> => {
+ const source = (showOnly ? selectedUsers : users) as UserOption[];
+ return source.map((u, idx) => ({ rank: idx, name: u.label, option: u }));
+ }, [users, selectedUsers, showOnly]);
+
+ const showNoUsersText =
+ !isFetching && !isSearching && !fetchError && view.length === 0;
+
+ const selected = React.useMemo(() => {
+ const map: Record = {};
+ view.forEach((p) => {
+ if (selectedUsers.some((u) => u.value === p.option.value)) {
+ map[p.rank] = true;
+ }
+ });
+ return map;
+ }, [view, selectedUsers]);
+
+ const clearDisabled = !view.some((p) =>
+ selectedUsers.some((u) => u.value === p.option.value)
+ );
+
+ const clearVisible = () => {
+ const visibleValues = new Set(view.map((p) => p.option.value));
+ setValue(
+ 'users',
+ selectedUsers.filter((u) => !visibleValues.has(u.value))
+ );
+ };
+
+ const handleSelectAll = () => {
+ const allCurrentOptionsSelected =
+ totalUserCount > 0 && selectedUsers.length >= totalUserCount;
+ if (allCurrentOptionsSelected) {
+ setValue('users', []);
+ setAllUserSelected(false);
+ } else {
+ onSelectAllClick();
+ }
+ };
+
+ const setSel = (rank: number, checked: boolean) => {
+ const p = view.find((item) => item.rank === rank);
+ if (!p) return;
+ if (checked) {
+ if (!selectedUsers.some((u) => u.value === p.option.value)) {
+ setValue('users', [...selectedUsers, p.option]);
+ }
+ } else {
+ setValue(
+ 'users',
+ selectedUsers.filter((u) => u.value !== p.option.value)
+ );
+ }
};
return (
@@ -185,113 +249,153 @@ export const UpdateDelegationForm = ({
Update delegation for {delegation.company}:
- (
-
- option.value === value.value
- }
- label="Delegate Users"
- loading={isFetching || isFetchingAllUsers}
- multiple
- noMarginTop
- noOptionsText={showNoOptionsText ? 'No users found' : ' '}
- onChange={(_, newValue) => {
- field.onChange(newValue || []);
- }}
- onInputChange={(_, value) => {
- setInputValue(value);
- }}
- onSelectAllClick={(_event) => {
- const allCurrentOptionsSelected =
- totalUserCount > 0 &&
- selectedUsers.length >= totalUserCount;
- if (allCurrentOptionsSelected) {
- setValue('users', []);
- setAllUserSelected(false);
- } else {
- onSelectAllClick();
- }
- }}
- options={users}
- renderTags={() => null}
- slotProps={{
- listbox: {
- onScroll: (event: React.SyntheticEvent) => {
- const listboxNode = event.currentTarget;
- if (
- listboxNode.scrollTop + listboxNode.clientHeight >=
- listboxNode.scrollHeight &&
- hasNextPage
- ) {
- fetchNextPage();
- }
- },
- },
- }}
- textFieldProps={{
- hideLabel: true,
- helperText: isSelectAllFetching
- ? 'Fetching all users...'
- : undefined,
- InputProps: isSelectAllFetching
- ? { startAdornment: null }
- : undefined,
- placeholder: getPlaceholder(
- 'delegates',
- selectedUsers.length,
- totalUserCount
- ),
- }}
- value={field.value}
- />
- )}
- />
-
- Users in the account delegation
- {isFetchingAllUsers ? '' : ` (${selectedUsers.length})`}:
-
- ({
- backgroundColor: isFetchingAllUsers
- ? theme.tokens.alias.Interaction.Background.Disabled
- : theme.palette.background.paper,
- maxHeight: 370,
- overflowY: 'auto',
- p: 2,
- py: 1,
- })}
- variant="outlined"
+
-
- {selectedUsers.length === 0 && (
-
- No users selected
-
+ ) =>
+ setInputValue(String(e.detail ?? ''))
+ }
+ placeholder={getPlaceholder(
+ 'delegates',
+ selectedUsers.length,
+ totalUserCount
)}
- {selectedUsers.map((user) => (
-
- setValue(
- 'users',
- selectedUsers.filter((u) => u.value !== user.value)
- )
- }
- username={user.label}
- />
- ))}
-
-
+ value={inputValue}
+ />
+
+
+ Items: {showOnly ? view.length : totalUserCount} | Selected:{' '}
+ {selectedUsers.length}
+
+
) => setShowOnly(!!e.detail)}
+ >
+ Show only selected
+
+
+
+
+
+
+
{
+ if (showOnly) return;
+ const { scrollTop, scrollHeight, clientHeight } =
+ e.currentTarget;
+ if (
+ scrollHeight - scrollTop <= clientHeight * 1.5 &&
+ hasNextPage &&
+ !isFetching
+ ) {
+ fetchNextPage();
+ }
+ }}
+ style={{
+ maxHeight: '200px',
+ overflowY: 'auto',
+ overflowX: 'hidden',
+ width: '100%',
+ boxSizing: 'border-box',
+ }}
+ >
+
+
+ {view.map((p) => (
+ {
+ if (isSubmitting) return;
+ const t = e.target as Element;
+ if (t.closest?.('cds-checkbox')) return;
+ setSel(p.rank, !selected[p.rank]);
+ }}
+ rowborder
+ selected={!!selected[p.rank]}
+ >
+
+ ) => {
+ setSel(p.rank, !!e.detail);
+ }}
+ onClick={(e: React.MouseEvent) => e.stopPropagation()}
+ />
+
+ {p.name}
+
+
+
+ ))}
+ {!showOnly && isFetching && (
+
+
+
+
+
+ )}
+ {showNoUsersText && (
+
+
+ No users found
+
+
+ )}
+ {fetchError && (
+
+
+ {fetchError[0]?.reason ?? 'Failed to load users'}
+
+
+ )}
+
+
+
+
);
};
-
-interface DelegationUserRowProps {
- isSubmitting: boolean;
- onRemove: () => void;
- username: string;
-}
-
-const DelegationUserRow = ({
- onRemove,
- username,
- isSubmitting,
-}: DelegationUserRowProps) => {
- return (
-
- {username}
-
-
-
-
- );
-};
diff --git a/packages/manager/src/features/IAM/Roles/RolesTable/RolesTable.tsx b/packages/manager/src/features/IAM/Roles/RolesTable/RolesTable.tsx
index d16de2aa1a9..301c2b89c65 100644
--- a/packages/manager/src/features/IAM/Roles/RolesTable/RolesTable.tsx
+++ b/packages/manager/src/features/IAM/Roles/RolesTable/RolesTable.tsx
@@ -4,7 +4,7 @@ import { useTheme } from '@mui/material';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import { useLocation, useNavigate, useSearch } from '@tanstack/react-router';
-import { Pagination } from 'akamai-cds-react-components/Pagination';
+import { Pagination } from '@akamai/cds-components/react/Pagination';
import {
sortRows,
Table,
@@ -14,7 +14,7 @@ import {
TableHeaderCell,
TableRow,
TableRowExpanded,
-} from 'akamai-cds-react-components/Table';
+} from '@akamai/cds-components/react/Table';
import React, { useState } from 'react';
import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField';
@@ -38,7 +38,7 @@ import {
import type { RoleView } from '../../Shared/types';
import type { SelectOption } from '@linode/ui';
-import type { Order } from 'akamai-cds-react-components/Table';
+import type { Order } from '@akamai/cds-components/react/Table';
const ALL_ROLES_OPTION: SelectOption = {
label: 'All Roles',
diff --git a/packages/manager/src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel.style.ts b/packages/manager/src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel.style.ts
index 1eeecca673e..a424d125f25 100644
--- a/packages/manager/src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel.style.ts
+++ b/packages/manager/src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel.style.ts
@@ -7,6 +7,7 @@ export const StyledPaper = styled(Paper)(({ theme }) => ({
: theme.tokens.color.Neutrals[100],
marginTop: theme.tokens.spacing.S8,
padding: theme.tokens.spacing.S12,
+ boxSizing: 'border-box',
}));
export const StyledTitle = styled(Typography, {
diff --git a/packages/manager/src/features/IAM/Shared/Entities/EntitiesSelect.tsx b/packages/manager/src/features/IAM/Shared/Entities/EntitiesSelect.tsx
index f2bf5ba4f28..e36d32c88cf 100644
--- a/packages/manager/src/features/IAM/Shared/Entities/EntitiesSelect.tsx
+++ b/packages/manager/src/features/IAM/Shared/Entities/EntitiesSelect.tsx
@@ -1,12 +1,14 @@
import {
- Autocomplete,
- CloseIcon,
- IconButton,
- Notice,
- Paper,
- Stack,
- Typography,
-} from '@linode/ui';
+ Button,
+ Checkbox,
+ LoadingSpinner,
+ Table,
+ TableBody,
+ TableCell,
+ TableRow,
+ TextField,
+} from '@akamai/cds-components/react';
+import { Notice, Typography } from '@linode/ui';
import { useTheme } from '@mui/material';
import React from 'react';
@@ -45,13 +47,14 @@ export const EntitiesSelect = ({
type,
value,
}: Props) => {
- const { data: entities, isLoading } = useAllAccountEntities({});
+ const {
+ data: entities,
+ error: fetchError,
+ isLoading,
+ } = useAllAccountEntities({});
const theme = useTheme();
- const [displayCount, setDisplayCount] = React.useState(INITIAL_DISPLAY_COUNT);
- const [inputValue, setInputValue] = React.useState('');
-
- const memoizedEntities = React.useMemo(() => {
+ const entityOptions = React.useMemo(() => {
if (access !== 'entity_access' || !entities) {
return [];
}
@@ -60,29 +63,88 @@ export const EntitiesSelect = ({
return typeEntities ? mapEntitiesToOptions(typeEntities) : [];
}, [entities, access, type]);
- const filteredEntities = React.useMemo(() => {
- if (!inputValue) {
- return memoizedEntities;
+ const [filterText, setFilterText] = React.useState('');
+ const [showSelectedOnly, setShowSelectedOnly] = React.useState(false);
+ const [displayCount, setDisplayCount] = React.useState(INITIAL_DISPLAY_COUNT);
+
+ const filteredRows = React.useMemo(() => {
+ const filtered = filterText
+ ? entityOptions.filter((opt) =>
+ opt.label.toLowerCase().includes(filterText.toLowerCase())
+ )
+ : entityOptions;
+ const withRank = filtered.map((opt, idx) => ({
+ rank: idx,
+ name: opt.label,
+ option: opt,
+ }));
+ if (showSelectedOnly) {
+ return withRank.filter((p) =>
+ value.some((v) => v.value === p.option.value)
+ );
}
+ return withRank;
+ }, [entityOptions, filterText, showSelectedOnly, value]);
- return memoizedEntities.filter((option) =>
- option.label.toLowerCase().includes(inputValue.toLowerCase())
+ React.useEffect(() => {
+ setDisplayCount(INITIAL_DISPLAY_COUNT);
+ }, [filterText, showSelectedOnly]);
+
+ const visibleRows = React.useMemo(() => {
+ const slice = filteredRows.slice(0, displayCount);
+ // Always include selected items even if beyond the display slice
+ const selectedNotVisible = filteredRows.filter(
+ (p) =>
+ value.some((v) => v.value === p.option.value) &&
+ !slice.some((s) => s.rank === p.rank)
);
- }, [memoizedEntities, inputValue]);
+ return [...slice, ...selectedNotVisible];
+ }, [filteredRows, displayCount, value]);
- const visibleOptions = React.useMemo(() => {
- const slice = filteredEntities.slice(0, displayCount);
+ const selectionMap = React.useMemo(() => {
+ const map: Record = {};
+ filteredRows.forEach((p) => {
+ if (value.some((v) => v.value === p.option.value)) {
+ map[p.rank] = true;
+ }
+ });
+ return map;
+ }, [filteredRows, value]);
- const selectedNotVisible = value.filter(
- (selected) => !slice.some((opt) => opt.value === selected.value)
- );
+ const selectedCount = value.length;
+ const clearDisabled = !filteredRows.some((p) =>
+ value.some((v) => v.value === p.option.value)
+ );
+ const selectAllDisabled = filteredRows.every((p) =>
+ value.some((v) => v.value === p.option.value)
+ );
- return [...slice, ...selectedNotVisible];
- }, [filteredEntities, displayCount, value]);
+ const handleClear = () => {
+ const visibleValues = new Set(filteredRows.map((p) => p.option.value));
+ onChange(value.filter((v) => !visibleValues.has(v.value)));
+ };
- React.useEffect(() => {
- setDisplayCount(INITIAL_DISPLAY_COUNT);
- }, [filteredEntities]);
+ const handleSelectAll = () => {
+ const currentValues = new Set(value.map((v) => v.value));
+ const toAdd = filteredRows
+ .filter((p) => !currentValues.has(p.option.value))
+ .map((p) => p.option);
+ onChange([...value, ...toAdd]);
+ };
+
+ const toggleEntity = (rank: number, checked: boolean) => {
+ const p = filteredRows.find((item) => item.rank === rank);
+ if (!p) return;
+ if (checked) {
+ if (!value.some((v) => v.value === p.option.value)) {
+ onChange([...value, p.option]);
+ }
+ } else {
+ onChange(value.filter((v) => v.value !== p.option.value));
+ }
+ };
+
+ const isReadOnly = mode === 'change-role';
if (access === 'account_access') {
return (
@@ -108,93 +170,158 @@ export const EntitiesSelect = ({
return (
<>
- option.label}
- isOptionEqualToValue={(option, value) => option.value === value.value}
- label="Entities"
- loading={isLoading}
- multiple
- noMarginTop
- onChange={(_, newValue, reason) => {
- if (
- reason === 'selectOption' &&
- newValue.length === displayCount &&
- filteredEntities.length > displayCount
- ) {
- onChange(filteredEntities);
- } else {
- onChange(newValue || []);
- }
- }}
- onInputChange={(_, value) => {
- setInputValue(value);
- }}
- options={visibleOptions}
- readOnly={mode === 'change-role'}
- renderTags={() => null}
- slotProps={{
- listbox: {
- onScroll: (e) => {
- const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
- if (scrollHeight - scrollTop <= clientHeight * 1.5) {
- setDisplayCount((prev) =>
- Math.min(prev + 200, filteredEntities.length)
- );
- }
- },
- },
- }}
- textFieldProps={{
- placeholder: getPlaceholder(
- type,
- value.length,
- filteredEntities.length
- ),
+ {errorText && (
+
+ {errorText}
+
+ )}
+
- {memoizedEntities.length > 0 && !isLoading && (
- <>
-
- Selected entities ({value.length}):
-
- ({
- backgroundColor: isLoading
- ? theme.tokens.alias.Interaction.Background.Disabled
- : theme.palette.background.paper,
- maxHeight: 370,
- overflowY: 'auto',
- p: 2,
- py: 1,
- })}
- variant="outlined"
+ >
+
+ Entities
+
+ ) =>
+ setFilterText(String(e.detail ?? ''))
+ }
+ placeholder={getPlaceholder(type, value.length, entityOptions.length)}
+ value={filterText}
+ />
+ {entityOptions.length > 0 && (
+
-
- {value.length === 0 && (
-
- No entities selected
-
- )}
- {value.map((entity) => (
-
- onChange(value.filter((v) => v.value !== entity.value))
- }
- />
+
+ Items: {filteredRows.length} | Selected: {selectedCount}
+
+ ) =>
+ setShowSelectedOnly(!!e.detail)
+ }
+ >
+ Show only selected
+
+
+
+
+
+
+ )}
+ {
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
+ if (scrollHeight - scrollTop <= clientHeight * 1.5) {
+ setDisplayCount((prev) =>
+ Math.min(prev + 200, filteredRows.length)
+ );
+ }
+ }}
+ style={{
+ maxHeight: '200px',
+ overflowY: 'auto',
+ overflowX: 'hidden',
+ width: '100%',
+ boxSizing: 'border-box',
+ }}
+ >
+
+
+ {visibleRows.map((p) => (
+ {
+ if (isReadOnly) return;
+ const t = e.target as Element;
+ if (t.closest?.('cds-menu') || t.closest?.('cds-checkbox'))
+ return;
+ toggleEntity(p.rank, !selectionMap[p.rank]);
+ }}
+ rowborder
+ selected={!!selectionMap[p.rank]}
+ >
+
+ ) => {
+ toggleEntity(p.rank, !!e.detail);
+ }}
+ onClick={(e: React.MouseEvent) => e.stopPropagation()}
+ />
+
+ {p.name}
+
+
+
))}
-
-
- >
- )}
- {!memoizedEntities.length && !isLoading && (
-
+ {isLoading && (
+
+
+
+
+
+ )}
+ {!isLoading && !fetchError && visibleRows.length === 0 && entityOptions.length > 0 && (
+
+
+ No entities found
+
+
+ )}
+ {fetchError && (
+
+
+ {(fetchError as { reason?: string })?.reason ??
+ 'Failed to load entities'}
+
+
+ )}
+
+
+
+
+
+ {!entityOptions.length && !isLoading && (
+
Create {type === 'image' ? `an` : `a`}{' '}
@@ -207,26 +334,3 @@ export const EntitiesSelect = ({
>
);
};
-
-interface EntityRowProps {
- disabled?: boolean;
- label: string;
- onRemove: () => void;
-}
-
-const EntityRow = ({ disabled, label, onRemove }: EntityRowProps) => {
- return (
-
- {label}
- {!disabled && (
-
-
-
- )}
-
- );
-};
diff --git a/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.test.tsx b/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.test.tsx
new file mode 100644
index 00000000000..67dafc318e9
--- /dev/null
+++ b/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.test.tsx
@@ -0,0 +1,21 @@
+import { screen } from '@testing-library/react';
+import React from 'react';
+
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { ERROR_STATE_TEXT_1, ERROR_STATE_TITLE } from '../constants';
+import { ErrorState } from './ErrorState';
+
+describe('ErrorState', () => {
+ it('renders with default error text', async () => {
+ renderWithTheme();
+ expect(screen.getByText(ERROR_STATE_TITLE)).toBeVisible();
+ expect(screen.getByText(ERROR_STATE_TEXT_1)).toBeVisible();
+ });
+
+ it('renders with custom error text', async () => {
+ const customErrorText = 'Custom error message';
+ renderWithTheme();
+ expect(screen.getByText(customErrorText)).toBeVisible();
+ });
+});
diff --git a/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.tsx b/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.tsx
new file mode 100644
index 00000000000..1d8a3438c09
--- /dev/null
+++ b/packages/manager/src/features/IAM/Shared/ErrorState/ErrorState.tsx
@@ -0,0 +1,30 @@
+import {
+ ZeroErrorDescription,
+ ZeroErrorIcon,
+ ZeroErrorState,
+ ZeroErrorTitle,
+} from '@akamai/cds-components/react';
+import React from 'react';
+
+import { ERROR_STATE_TEXT_1, ERROR_STATE_TITLE } from '../constants';
+
+interface Props {
+ errorText?: string;
+}
+export const ErrorState = (props: Props) => {
+ const { errorText } = props;
+
+ return (
+
+
+ {errorText ? (
+ {errorText}
+ ) : (
+ <>
+ {ERROR_STATE_TITLE}
+ {ERROR_STATE_TEXT_1}
+ >
+ )}
+
+ );
+};
diff --git a/packages/manager/src/features/IAM/Shared/NoAssignedRoles/NoAssignedRoles.tsx b/packages/manager/src/features/IAM/Shared/NoAssignedRoles/NoAssignedRoles.tsx
index 285e87bd76d..892b23c4ff4 100644
--- a/packages/manager/src/features/IAM/Shared/NoAssignedRoles/NoAssignedRoles.tsx
+++ b/packages/manager/src/features/IAM/Shared/NoAssignedRoles/NoAssignedRoles.tsx
@@ -1,13 +1,17 @@
-import { Box, Button, Typography, useTheme } from '@linode/ui';
+import {
+ ZeroErrorActions,
+ ZeroErrorDescription,
+ ZeroErrorIcon,
+ ZeroErrorState,
+ ZeroErrorTitle,
+} from '@akamai/cds-components/react';
+import { Button } from '@linode/ui';
import React from 'react';
-import EmptyState from 'src/assets/icons/empty-state-cloud.svg';
-
import { useIsDefaultDelegationRolesForChildAccount } from '../../hooks/useDelegationRole';
import { usePermissions } from '../../hooks/usePermissions';
import { AssignNewRoleDrawer } from '../../Users/UserRoles/AssignNewRoleDrawer';
import { IAM_ROLES_PENDO_IDS } from '../constants';
-
interface Props {
hasAssignNewRoleDrawer: boolean;
text: string;
@@ -15,7 +19,6 @@ interface Props {
export const NoAssignedRoles = (props: Props) => {
const { text, hasAssignNewRoleDrawer } = props;
- const theme = useTheme();
const { data: permissions } = usePermissions('account', [
'is_account_admin',
'update_default_delegate_access',
@@ -31,52 +34,37 @@ export const NoAssignedRoles = (props: Props) => {
React.useState(false);
return (
-
-
- This list is empty
-
- {text}
-
- {hasAssignNewRoleDrawer && (
-
- )}
+
+
+ This list is empty
+ {text}
+
+ {hasAssignNewRoleDrawer && (
+
+ )}
+
setIsAssignNewRoleDrawerOpen(false)}
open={isAssignNewRoleDrawerOpen}
/>
-
+
);
};
diff --git a/packages/manager/src/features/IAM/Shared/NotFound/NotFound.test.tsx b/packages/manager/src/features/IAM/Shared/NotFound/NotFound.test.tsx
new file mode 100644
index 00000000000..99a7fc320ab
--- /dev/null
+++ b/packages/manager/src/features/IAM/Shared/NotFound/NotFound.test.tsx
@@ -0,0 +1,14 @@
+import { screen } from '@testing-library/react';
+import React from 'react';
+
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { NotFound } from './NotFound';
+
+describe('NotFound', () => {
+ it('renders with default error text', async () => {
+ renderWithTheme();
+ expect(screen.getByText('Not Found')).toBeVisible();
+ expect(screen.getByText('This page does not exist.')).toBeVisible();
+ });
+});
diff --git a/packages/manager/src/features/IAM/Shared/NotFound/NotFound.tsx b/packages/manager/src/features/IAM/Shared/NotFound/NotFound.tsx
new file mode 100644
index 00000000000..66ad2dc247e
--- /dev/null
+++ b/packages/manager/src/features/IAM/Shared/NotFound/NotFound.tsx
@@ -0,0 +1,17 @@
+import {
+ ZeroErrorDescription,
+ ZeroErrorIcon,
+ ZeroErrorState,
+ ZeroErrorTitle,
+} from '@akamai/cds-components/react';
+import React from 'react';
+
+export const NotFound = () => {
+ return (
+
+
+ Not Found
+ This page does not exist.
+
+ );
+};
diff --git a/packages/manager/src/features/IAM/Shared/constants.ts b/packages/manager/src/features/IAM/Shared/constants.ts
index c91e73df2d1..73eae2927fb 100644
--- a/packages/manager/src/features/IAM/Shared/constants.ts
+++ b/packages/manager/src/features/IAM/Shared/constants.ts
@@ -73,6 +73,10 @@ export const LAST_ACCOUNT_ADMIN_ERROR =
export const ERROR_STATE_TEXT =
'An unexpected error occurred. Refresh the page or try again later.';
+export const ERROR_STATE_TITLE = 'An unexpected error occurred.';
+
+export const ERROR_STATE_TEXT_1 = 'Refresh the page or try again later.';
+
// Delegation error messages
export const NO_ITEMS_TO_DISPLAY_TEXT = 'No items to display.';
export const NO_DELEGATED_USERS_TEXT = 'No users added.';
diff --git a/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.test.tsx b/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.test.tsx
new file mode 100644
index 00000000000..2898106b94e
--- /dev/null
+++ b/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.test.tsx
@@ -0,0 +1,147 @@
+import { screen } from '@testing-library/react';
+import React from 'react';
+
+import { accountUserFactory } from 'src/factories/accountUsers';
+import { userRolesFactory } from 'src/factories/userRoles';
+import { renderWithTheme } from 'src/utilities/testHelpers';
+
+import { UserProfile } from './UserProfile';
+
+const queryMocks = vi.hoisted(() => ({
+ useAccountUser: vi.fn().mockReturnValue({}),
+ useParams: vi.fn().mockReturnValue({}),
+ usePermissions: vi.fn().mockReturnValue({}),
+ useUserRoles: vi.fn().mockReturnValue({}),
+}));
+
+vi.mock('@linode/queries', async () => {
+ const actual = await vi.importActual('@linode/queries');
+ return {
+ ...actual,
+ useAccountUser: queryMocks.useAccountUser,
+ useUserRoles: queryMocks.useUserRoles,
+ };
+});
+
+vi.mock('@tanstack/react-router', async () => {
+ const actual = await vi.importActual('@tanstack/react-router');
+ return {
+ ...actual,
+ useParams: queryMocks.useParams,
+ };
+});
+
+vi.mock('../../hooks/usePermissions', async () => {
+ const actual = await vi.importActual('../../hooks/usePermissions');
+ return {
+ ...actual,
+ usePermissions: queryMocks.usePermissions,
+ };
+});
+
+describe('UserProfile', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+
+ queryMocks.useParams.mockReturnValue({ username: 'test-user' });
+ queryMocks.usePermissions.mockReturnValue({
+ data: {
+ delete_user: true,
+ list_user_permissions: true,
+ update_user: true,
+ view_user: true,
+ },
+ isLoading: false,
+ });
+ queryMocks.useAccountUser.mockReturnValue({
+ data: accountUserFactory.build({
+ email: 'test-user@example.com',
+ username: 'test-user',
+ }),
+ error: null,
+ isLoading: false,
+ });
+ queryMocks.useUserRoles.mockReturnValue({
+ data: userRolesFactory.build({
+ account_access: ['account_admin'],
+ entity_access: [],
+ }),
+ });
+ });
+
+ it('renders a loading state while the user is loading', () => {
+ queryMocks.useAccountUser.mockReturnValue({
+ data: null,
+ error: null,
+ isLoading: true,
+ });
+
+ renderWithTheme();
+
+ expect(screen.getByTestId('circle-progress')).toBeVisible();
+ });
+
+ it('shows a permission notice when the user cannot view user details', () => {
+ queryMocks.usePermissions.mockReturnValue({
+ data: {
+ delete_user: true,
+ list_user_permissions: false,
+ update_user: true,
+ view_user: false,
+ },
+ isLoading: false,
+ });
+
+ renderWithTheme();
+
+ expect(
+ screen.getByText(
+ "You do not have permission to view this user's details."
+ )
+ ).toBeVisible();
+ });
+
+ it('shows an error state when loading the user fails', () => {
+ queryMocks.useAccountUser.mockReturnValue({
+ data: null,
+ error: [{ reason: 'Unable to load user profile.' }],
+ isLoading: false,
+ });
+
+ renderWithTheme();
+
+ expect(screen.getByText('Unable to load user profile.')).toBeVisible();
+ });
+
+ it('shows a not found state when the user does not exist', () => {
+ queryMocks.useAccountUser.mockReturnValue({
+ data: null,
+ error: null,
+ isLoading: false,
+ });
+
+ renderWithTheme();
+
+ expect(screen.getByText('Not Found')).toBeVisible();
+ expect(screen.getByText('This page does not exist.')).toBeVisible();
+ });
+
+ it('renders the profile panels with the resolved user data and permissions', () => {
+ renderWithTheme();
+
+ expect(queryMocks.usePermissions).toHaveBeenCalledWith('account', [
+ 'view_user',
+ 'update_user',
+ 'delete_user',
+ 'list_user_permissions',
+ ]);
+ expect(queryMocks.useAccountUser).toHaveBeenCalledWith('test-user', true);
+ expect(queryMocks.useUserRoles).toHaveBeenCalledWith('test-user', true);
+
+ expect(screen.getByText('test-user')).toBeVisible();
+
+ expect(screen.getByLabelText('Email')).toHaveDisplayValue(
+ 'test-user@example.com'
+ );
+ });
+});
diff --git a/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.tsx b/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.tsx
index 53b843d9933..e99afc8de4a 100644
--- a/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.tsx
+++ b/packages/manager/src/features/IAM/Users/UserDetails/UserProfile.tsx
@@ -1,8 +1,8 @@
import { useAccountUser, useUserRoles } from '@linode/queries';
import {
CircleProgress,
- ErrorState,
- NotFound,
+ // ErrorState,
+ // NotFound,
Notice,
Stack,
} from '@linode/ui';
@@ -10,6 +10,8 @@ import { useParams } from '@tanstack/react-router';
import React from 'react';
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
+import { ErrorState } from 'src/features/IAM/Shared/ErrorState/ErrorState';
+import { NotFound } from 'src/features/IAM/Shared/NotFound/NotFound';
import { usePermissions } from '../../hooks/usePermissions';
import { DeleteUserPanel } from './DeleteUserPanel';
diff --git a/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx b/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx
index 5c1bd80fb91..17f9fe4f56d 100644
--- a/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx
+++ b/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx
@@ -1,3 +1,8 @@
+import {
+ Button,
+ Drawer,
+ NotificationBanner,
+} from '@akamai/cds-components/react';
import {
delegationQueries,
iamQueries,
@@ -6,22 +11,12 @@ import {
useUpdateDefaultDelegationAccessQuery,
useUserRolesMutation,
} from '@linode/queries';
-import {
- ActionsPanel,
- Drawer,
- LinkButton,
- Notice,
- Typography,
-} from '@linode/ui';
import { useTheme } from '@mui/material';
-import Grid from '@mui/material/Grid';
import { useParams } from '@tanstack/react-router';
import { enqueueSnackbar } from 'notistack';
import React, { useEffect, useState } from 'react';
import { FormProvider, useFieldArray, useForm } from 'react-hook-form';
-import { Link } from 'src/components/Link';
-import { StyledLinkButtonBox } from 'src/components/SelectFirewallPanel/SelectFirewallPanel';
import { AssignSingleRole } from 'src/features/IAM/Users/UserRoles/AssignSingleRole';
import { useIsDefaultDelegationRolesForChildAccount } from '../../hooks/useDelegationRole';
@@ -131,8 +126,8 @@ export const AssignNewRoleDrawer = ({
}
enqueueSnackbar(`Roles added.`, { variant: 'success' });
handleClose();
- } catch (error) {
- setError(error.field ?? 'root', {
+ } catch {
+ setError('root', {
message: INTERNAL_ERROR_NO_CHANGES_SAVED,
});
}
@@ -152,50 +147,51 @@ export const AssignNewRoleDrawer = ({
}, [open, reset]);
return (
-
+
+ {isDefaultDelegationRolesForChildAccount
? 'Add New Default Roles'
- : 'Assign New Roles'
- }
- >
+ : 'Assign New Roles'}
+
+
+
- Roles
+
+ Roles
+
{roles.length > 0 && roles.some((field) => field.role) && (
-
- setAreDetailsHidden(!areDetailsHidden)}
- >
- {areDetailsHidden ? 'Show' : 'Hide'} details
-
-
+
)}
-
+
{!!accountRoles &&
fields.map((field, index) => (
@@ -211,31 +207,50 @@ export const AssignNewRoleDrawer = ({
{/* If all roles are filled, allow them to add another */}
{roles.length > 0 && roles.every((field) => field.role?.value) && (
-
- append({ role: null })}>
- Add another role
-
-
+
)}
-
+ {/* */}
+
+
+ formState.isSubmitting
+ }
+ type="submit"
+ variant="primary"
+ >
+ {isDefaultDelegationRolesForChildAccount ? 'Add' : 'Assign'}
+
+
diff --git a/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx b/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx
index dd842920440..9e1a6863c27 100644
--- a/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx
+++ b/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx
@@ -1,6 +1,6 @@
-import { Autocomplete, Button, DeleteIcon } from '@linode/ui';
+import { Button, Icon } from '@akamai/cds-components/react';
+import { Autocomplete } from '@linode/ui';
import { useTheme } from '@mui/material';
-import Box from '@mui/material/Box';
import React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
@@ -38,8 +38,10 @@ export const AssignSingleRole = ({
const roles = watch('roles');
return (
-
-
+
+
{index !== 0 && (
(
{
@@ -63,6 +66,23 @@ export const AssignSingleRole = ({
textFieldProps={{ hideLabel: true }}
value={value || null}
/>
+
+ // TODO: UIE-10739 - Replace with CDS Select
+ //
);
};
diff --git a/packages/manager/src/features/IAM/Users/UserRoles/UserRoles.tsx b/packages/manager/src/features/IAM/Users/UserRoles/UserRoles.tsx
index 32d4055076a..ce1fd3cbc67 100644
--- a/packages/manager/src/features/IAM/Users/UserRoles/UserRoles.tsx
+++ b/packages/manager/src/features/IAM/Users/UserRoles/UserRoles.tsx
@@ -1,7 +1,7 @@
import { useAccountUser, useUserRoles } from '@linode/queries';
import {
CircleProgress,
- ErrorState,
+ // ErrorState,
Notice,
Paper,
Typography,
@@ -11,6 +11,7 @@ import { useParams } from '@tanstack/react-router';
import React from 'react';
import { DocumentTitleSegment } from 'src/components/DocumentTitle';
+import { ErrorState } from 'src/features/IAM/Shared/ErrorState/ErrorState';
import { usePermissions } from '../../hooks/usePermissions';
import { AssignedRolesTable } from '../../Shared/AssignedRolesTable/AssignedRolesTable';
diff --git a/packages/queries/src/iam/iam.ts b/packages/queries/src/iam/iam.ts
index 97fe41fa5f4..42f37d7a76f 100644
--- a/packages/queries/src/iam/iam.ts
+++ b/packages/queries/src/iam/iam.ts
@@ -1,5 +1,9 @@
import { updateUserRoles } from '@linode/api-v4';
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import {
+ useMutation,
+ useQuery,
+ useQueryClient,
+} from '@tanstack/react-query';
import { queryPresets } from '../base';
import { useProfile } from '../profile';
@@ -14,6 +18,7 @@ import type {
IamUserRoles,
PermissionType,
} from '@linode/api-v4';
+import type { UseQueryResult } from '@tanstack/react-query';
export const useUserRoles = (username?: string, enabled: boolean = true) => {
return useQuery
({
@@ -23,7 +28,9 @@ export const useUserRoles = (username?: string, enabled: boolean = true) => {
});
};
-export const useAccountRoles = (enabled = true) => {
+export const useAccountRoles = (
+ enabled = true
+): UseQueryResult => {
return useQuery({
...iamQueries.accountRoles,
...queryPresets.oneTimeFetch,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0ae58ab823d..d0b120e85fa 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -123,6 +123,15 @@ importers:
packages/manager:
dependencies:
+ '@akamai/cds-components':
+ specifier: 0.0.0-20260407204557
+ version: 0.0.0-20260407204557(@akamai/cds-icons@0.0.0-20260407204557(lit@3.3.2)(react@19.1.0))(@akamai/cds-tokens@0.0.0-20260407204557)(lit@3.3.2)(luxon@3.4.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@akamai/cds-icons':
+ specifier: 0.0.0-20260407204557
+ version: 0.0.0-20260407204557(lit@3.3.2)(react@19.1.0)
+ '@akamai/cds-tokens':
+ specifier: 0.0.0-20260407204557
+ version: 0.0.0-20260407204557
'@braintree/sanitize-url':
specifier: ^7.1.0
version: 7.1.0
@@ -216,9 +225,6 @@ importers:
'@xterm/xterm':
specifier: ^5.5.0
version: 5.5.0
- akamai-cds-react-components:
- specifier: 0.1.0
- version: 0.1.0(@linode/design-language-system@5.3.2)(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
algoliasearch:
specifier: ^4.14.3
version: 4.24.0
@@ -447,7 +453,7 @@ importers:
version: 4.0.1(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
'@vitest/coverage-v8':
specifier: ^3.2.4
- version: 3.2.4(vitest@3.2.4(@types/node@22.18.1)(@vitest/ui@4.1.2(vitest@4.1.2))(jiti@2.4.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
+ version: 3.2.4(vitest@4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)))
'@vueless/storybook-dark-mode':
specifier: ^9.0.5
version: 9.0.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -812,6 +818,46 @@ packages:
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+ '@akamai/cds-components@0.0.0-20260407204557':
+ resolution: {integrity: sha512-/p5GJy7/OLTm7ylh/mtcnFS7XtHGEYzyGcDGTZzr7OWoqhDr7G66EkrTaRhw7+jVShMvF4xEAnNA7h2MEkdeKw==, tarball: https://repos.akamai.com:443/artifactory/api/npm/npmjs-repos/@akamai/cds-components/-/@akamai/cds-components-0.0.0-20260407204557.tgz}
+ peerDependencies:
+ '@akamai/cds-icons': 0.0.0-20260407204557
+ '@akamai/cds-tokens': 0.0.0-20260407204557
+ '@angular/core': ^21.2.5
+ '@angular/forms': ^21.2.5
+ '@lit/react': ^1.0.0
+ lit: ^3.2.1
+ luxon: ^3.0.0
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ peerDependenciesMeta:
+ '@angular/core':
+ optional: true
+ '@angular/forms':
+ optional: true
+ '@lit/react':
+ optional: true
+ luxon:
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+
+ '@akamai/cds-icons@0.0.0-20260407204557':
+ resolution: {integrity: sha512-NKI1tGqT6UzRy+RuRTVEVbkPJLWLxZ+CkSC5ZRajdLm6MDA6/b5m+TgpD3ccjeNZwJLg4y3r30K3iUi0dmOkFw==, tarball: https://repos.akamai.com:443/artifactory/api/npm/npmjs-repos/@akamai/cds-icons/-/@akamai/cds-icons-0.0.0-20260407204557.tgz}
+ peerDependencies:
+ lit: ^3.2.1
+ react: ^18.0.0
+ peerDependenciesMeta:
+ lit:
+ optional: true
+ react:
+ optional: true
+
+ '@akamai/cds-tokens@0.0.0-20260407204557':
+ resolution: {integrity: sha512-jVEMS3bw/E1kJrJv/Is2Zb+Bp0JW9kUU9laoh3a4GaJQTEs82U+Au2d85M393h5gsS+iM81YbCRP48cnKWXOAg==, tarball: https://repos.akamai.com:443/artifactory/api/npm/npmjs-repos/@akamai/cds-tokens/-/@akamai/cds-tokens-0.0.0-20260407204557.tgz}
+
'@algolia/cache-browser-local-storage@4.24.0':
resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==}
@@ -1888,11 +1934,6 @@ packages:
'@lit-labs/ssr-dom-shim@1.5.1':
resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==}
- '@lit/react@1.0.8':
- resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==}
- peerDependencies:
- '@types/react': 17 || 18 || 19
-
'@lit/reactive-element@2.1.2':
resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==}
@@ -3213,17 +3254,6 @@ packages:
'@vitest/expect@4.1.2':
resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==}
- '@vitest/mocker@3.2.4':
- resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==}
- peerDependencies:
- msw: ^2.4.9
- vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
- peerDependenciesMeta:
- msw:
- optional: true
- vite:
- optional: true
-
'@vitest/mocker@4.1.2':
resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==}
peerDependencies:
@@ -3241,15 +3271,9 @@ packages:
'@vitest/pretty-format@4.1.2':
resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==}
- '@vitest/runner@3.2.4':
- resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==}
-
'@vitest/runner@4.1.2':
resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==}
- '@vitest/snapshot@3.2.4':
- resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==}
-
'@vitest/snapshot@4.1.2':
resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==}
@@ -3303,17 +3327,6 @@ packages:
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
- akamai-cds-react-components@0.1.0:
- resolution: {integrity: sha512-3j4RUS2gsEvTvGVihG48oxfZ+sFRGM+6zfB04UNIVrxsIa5QLYhA4C6RKqmuEqKlsQhgSeg/XkcKJh4vKmW3Qw==}
- peerDependencies:
- react: ^18.0.0
- react-dom: ^18.0.0
-
- akamai-cds-web-components@0.1.0:
- resolution: {integrity: sha512-Ia8JIqr0dV1yuFAaEiP1KfTOZVNLIc0OhWIcnmPb7lnUUOtKfLSB3bbUyYhcm0xag+hLCBxo+Ds+axE9ZL55uw==}
- peerDependencies:
- '@linode/design-language-system': ^5.3.2
-
algoliasearch@4.24.0:
resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==}
@@ -4137,9 +4150,6 @@ packages:
resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==}
engines: {node: '>= 0.4'}
- es-module-lexer@1.7.0:
- resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
-
es-module-lexer@2.0.0:
resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==}
@@ -6268,9 +6278,6 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
- strip-literal@3.1.0:
- resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
-
stylis@4.2.0:
resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==}
@@ -6369,10 +6376,6 @@ packages:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
- tinypool@1.1.1:
- resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
- engines: {node: ^18.0.0 || >=20.0.0}
-
tinyrainbow@2.0.0:
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
engines: {node: '>=14.0.0'}
@@ -6654,11 +6657,6 @@ packages:
victory-vendor@36.9.2:
resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==}
- vite-node@3.2.4:
- resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
-
vite-plugin-svgr@4.5.0:
resolution: {integrity: sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==}
peerDependencies:
@@ -6704,34 +6702,6 @@ packages:
yaml:
optional: true
- vitest@3.2.4:
- resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
- hasBin: true
- peerDependencies:
- '@edge-runtime/vm': '*'
- '@types/debug': ^4.1.12
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
- '@vitest/browser': 3.2.4
- '@vitest/ui': 3.2.4
- happy-dom: '*'
- jsdom: '*'
- peerDependenciesMeta:
- '@edge-runtime/vm':
- optional: true
- '@types/debug':
- optional: true
- '@types/node':
- optional: true
- '@vitest/browser':
- optional: true
- '@vitest/ui':
- optional: true
- happy-dom:
- optional: true
- jsdom:
- optional: true
-
vitest@4.1.2:
resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
@@ -6951,6 +6921,23 @@ snapshots:
'@adobe/css-tools@4.4.4': {}
+ '@akamai/cds-components@0.0.0-20260407204557(@akamai/cds-icons@0.0.0-20260407204557(lit@3.3.2)(react@19.1.0))(@akamai/cds-tokens@0.0.0-20260407204557)(lit@3.3.2)(luxon@3.4.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@akamai/cds-icons': 0.0.0-20260407204557(lit@3.3.2)(react@19.1.0)
+ '@akamai/cds-tokens': 0.0.0-20260407204557
+ lit: 3.3.2
+ optionalDependencies:
+ luxon: 3.4.4
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@akamai/cds-icons@0.0.0-20260407204557(lit@3.3.2)(react@19.1.0)':
+ optionalDependencies:
+ lit: 3.3.2
+ react: 19.1.0
+
+ '@akamai/cds-tokens@0.0.0-20260407204557': {}
+
'@algolia/cache-browser-local-storage@4.24.0':
dependencies:
'@algolia/cache-common': 4.24.0
@@ -7928,10 +7915,6 @@ snapshots:
'@lit-labs/ssr-dom-shim@1.5.1': {}
- '@lit/react@1.0.8(@types/react@19.1.6)':
- dependencies:
- '@types/react': 19.1.6
-
'@lit/reactive-element@2.1.2':
dependencies:
'@lit-labs/ssr-dom-shim': 1.5.1
@@ -9161,7 +9144,7 @@ snapshots:
transitivePeerDependencies:
- '@swc/helpers'
- '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@22.18.1)(@vitest/ui@4.1.2(vitest@4.1.2))(jiti@2.4.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))':
+ '@vitest/coverage-v8@3.2.4(vitest@4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
@@ -9176,7 +9159,7 @@ snapshots:
std-env: 3.9.0
test-exclude: 7.0.1
tinyrainbow: 2.0.0
- vitest: 3.2.4(@types/node@22.18.1)(@vitest/ui@4.1.2(vitest@4.1.2))(jiti@2.4.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
+ vitest: 4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
transitivePeerDependencies:
- supports-color
@@ -9197,22 +9180,22 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@3.2.4(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))':
+ '@vitest/mocker@4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))':
dependencies:
- '@vitest/spy': 3.2.4
+ '@vitest/spy': 4.1.2
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- msw: 2.6.5(@types/node@22.18.1)(typescript@5.9.3)
+ msw: 2.6.5(@types/node@22.18.1)(typescript@5.7.3)
vite: 7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
- '@vitest/mocker@4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))':
+ '@vitest/mocker@4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))':
dependencies:
'@vitest/spy': 4.1.2
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- msw: 2.6.5(@types/node@22.18.1)(typescript@5.7.3)
+ msw: 2.6.5(@types/node@22.18.1)(typescript@5.9.3)
vite: 7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
'@vitest/pretty-format@3.2.4':
@@ -9223,23 +9206,11 @@ snapshots:
dependencies:
tinyrainbow: 3.1.0
- '@vitest/runner@3.2.4':
- dependencies:
- '@vitest/utils': 3.2.4
- pathe: 2.0.3
- strip-literal: 3.1.0
-
'@vitest/runner@4.1.2':
dependencies:
'@vitest/utils': 4.1.2
pathe: 2.0.3
- '@vitest/snapshot@3.2.4':
- dependencies:
- '@vitest/pretty-format': 3.2.4
- magic-string: 0.30.21
- pathe: 2.0.3
-
'@vitest/snapshot@4.1.2':
dependencies:
'@vitest/pretty-format': 4.1.2
@@ -9262,7 +9233,7 @@ snapshots:
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.1.0
- vitest: 4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
+ vitest: 4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
'@vitest/utils@3.2.4':
dependencies:
@@ -9314,21 +9285,6 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
- akamai-cds-react-components@0.1.0(@linode/design-language-system@5.3.2)(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
- dependencies:
- '@lit/react': 1.0.8(@types/react@19.1.6)
- akamai-cds-web-components: 0.1.0(@linode/design-language-system@5.3.2)
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- transitivePeerDependencies:
- - '@linode/design-language-system'
- - '@types/react'
-
- akamai-cds-web-components@0.1.0(@linode/design-language-system@5.3.2):
- dependencies:
- '@linode/design-language-system': 5.3.2
- lit: 3.3.2
-
algoliasearch@4.24.0:
dependencies:
'@algolia/cache-browser-local-storage': 4.24.0
@@ -10251,8 +10207,6 @@ snapshots:
iterator.prototype: 1.1.5
safe-array-concat: 1.1.3
- es-module-lexer@1.7.0: {}
-
es-module-lexer@2.0.0: {}
es-object-atoms@1.1.1:
@@ -12762,10 +12716,6 @@ snapshots:
strip-json-comments@3.1.1: {}
- strip-literal@3.1.0:
- dependencies:
- js-tokens: 9.0.1
-
stylis@4.2.0: {}
sucrase@3.35.0:
@@ -12858,8 +12808,6 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
- tinypool@1.1.1: {}
-
tinyrainbow@2.0.0: {}
tinyrainbow@3.1.0: {}
@@ -13156,27 +13104,6 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
- vite-node@3.2.4(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1):
- dependencies:
- cac: 6.7.14
- debug: 4.4.3(supports-color@8.1.1)
- es-module-lexer: 1.7.0
- pathe: 2.0.3
- vite: 7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
- transitivePeerDependencies:
- - '@types/node'
- - jiti
- - less
- - lightningcss
- - sass
- - sass-embedded
- - stylus
- - sugarss
- - supports-color
- - terser
- - tsx
- - yaml
-
vite-plugin-svgr@4.5.0(rollup@4.60.1)(typescript@5.9.3)(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)):
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.60.1)
@@ -13204,53 +13131,39 @@ snapshots:
tsx: 4.19.3
yaml: 2.6.1
- vitest@3.2.4(@types/node@22.18.1)(@vitest/ui@4.1.2(vitest@4.1.2))(jiti@2.4.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1):
+ vitest@4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)):
dependencies:
- '@types/chai': 5.2.3
- '@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
- '@vitest/pretty-format': 3.2.4
- '@vitest/runner': 3.2.4
- '@vitest/snapshot': 3.2.4
- '@vitest/spy': 3.2.4
- '@vitest/utils': 3.2.4
- chai: 5.3.3
- debug: 4.4.3(supports-color@8.1.1)
+ '@vitest/expect': 4.1.2
+ '@vitest/mocker': 4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
+ '@vitest/pretty-format': 4.1.2
+ '@vitest/runner': 4.1.2
+ '@vitest/snapshot': 4.1.2
+ '@vitest/spy': 4.1.2
+ '@vitest/utils': 4.1.2
+ es-module-lexer: 2.0.0
expect-type: 1.3.0
magic-string: 0.30.21
+ obug: 2.1.1
pathe: 2.0.3
picomatch: 4.0.4
- std-env: 3.9.0
+ std-env: 4.0.0
tinybench: 2.9.0
- tinyexec: 0.3.2
+ tinyexec: 1.0.4
tinyglobby: 0.2.15
- tinypool: 1.1.1
- tinyrainbow: 2.0.0
+ tinyrainbow: 3.1.0
vite: 7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
- vite-node: 3.2.4(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.18.1
'@vitest/ui': 4.1.2(vitest@4.1.2)
jsdom: 24.1.3
transitivePeerDependencies:
- - jiti
- - less
- - lightningcss
- msw
- - sass
- - sass-embedded
- - stylus
- - sugarss
- - supports-color
- - terser
- - tsx
- - yaml
- vitest@4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)):
+ vitest@4.1.2(@types/node@22.18.1)(@vitest/ui@4.1.2)(jsdom@24.1.3)(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1)):
dependencies:
'@vitest/expect': 4.1.2
- '@vitest/mocker': 4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.7.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
+ '@vitest/mocker': 4.1.2(msw@2.6.5(@types/node@22.18.1)(typescript@5.9.3))(vite@7.3.2(@types/node@22.18.1)(jiti@2.4.2)(terser@5.36.0)(tsx@4.19.3)(yaml@2.6.1))
'@vitest/pretty-format': 4.1.2
'@vitest/runner': 4.1.2
'@vitest/snapshot': 4.1.2