Make pl-hooks self-containted

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-10-17 21:08:50 +02:00
parent 3a00f0cea8
commit 9aa8ded6c6
26 changed files with 188 additions and 178 deletions

View file

@ -102,6 +102,7 @@
"multiselect-react-dropdown": "^2.0.25",
"path-browserify": "^1.0.1",
"pl-api": "^0.1.1",
"pl-hooks": "^0.0.1",
"postcss": "^8.4.47",
"process": "^0.11.10",
"punycode": "^2.1.1",

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { PaginatedResponse, Status } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import { fetchRelationships } from './accounts';
import type { Account, ProfileDirectoryParams } from 'pl-api';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { Account, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { StatusEdit } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { Poll } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { Status as BaseStatus, PaginatedResponse } from 'pl-api';
import type { AppDispatch, RootState } from 'pl-fe/store';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import { fetchRelationships } from './accounts';
import { insertSuggestionsIntoTimeline } from './timelines';

View file

@ -2,7 +2,6 @@ import { importEntities } from 'pl-hooks/importer';
import { getClient } from 'pl-fe/api';
import type { AppDispatch, RootState } from 'pl-fe/store';
const TRENDING_STATUSES_FETCH_REQUEST = 'TRENDING_STATUSES_FETCH_REQUEST' as const;

View file

@ -7603,6 +7603,11 @@ pl-api@^0.1.1:
semver "^7.6.3"
valibot "^0.42.1"
pl-hooks@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/pl-hooks/-/pl-hooks-0.0.1.tgz#a9e127242e8868f993e0dd696db1e403e03a1cf7"
integrity sha512-zGyIB7+G/8ZTACytBlLWZcAd0DBBYuP4ocP07UI9FjBNJdplDk4pWitGlttJELAkDBasUTPp7iIEXyadrpIW4A==
possible-typed-array-names@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f"

View file

@ -1,14 +0,0 @@
import { QueryClient } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60000, // 1 minute
gcTime: Infinity,
retry: false,
},
},
});
export { queryClient };

View file

@ -0,0 +1,16 @@
import { PlApiClient } from 'pl-api';
import React from 'react';
const PlHooksApiClientContext = React.createContext<{
client: PlApiClient;
me: string | null | false;
}>({
client: new PlApiClient(''),
me: null,
});
const PlHooksApiClientProvider = PlHooksApiClientContext.Provider;
const usePlHooksApiClient = () => React.useContext(PlHooksApiClientContext);
export { PlHooksApiClientProvider, usePlHooksApiClient };

View file

@ -0,0 +1,21 @@
import { QueryClient } from '@tanstack/react-query';
import React from 'react';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
staleTime: 60000, // 1 minute
gcTime: Infinity,
retry: false,
},
},
});
const PlHooksQueryClientContext = React.createContext<QueryClient>(queryClient);
const PlHooksQueryClientProvider = PlHooksQueryClientContext.Provider;
const usePlHooksQueryClient = () => React.useContext(PlHooksQueryClientContext);
export { queryClient, PlHooksQueryClientProvider, usePlHooksQueryClient };

View file

@ -1,9 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import { useRelationship } from 'pl-fe/api/hooks/accounts/useRelationship';
import { useClient } from 'pl-fe/hooks';
import { queryClient } from 'pl-hooks/client';
import { normalizeAccount } from 'pl-hooks/normalizers/normalizeAccount';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
import { type Account, normalizeAccount } from 'pl-hooks/normalizers/normalizeAccount';
import { useAccountRelationship } from './useAccountRelationship';
interface UseAccountOpts {
withRelationship?: boolean;
@ -12,25 +13,25 @@ interface UseAccountOpts {
}
const useAccount = (accountId?: string, opts: UseAccountOpts = {}) => {
const client = useClient();
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
const accountQuery = useQuery({
queryKey: ['accounts', 'entities', accountId],
queryFn: () => client.accounts.getAccount(accountId!)
.then(normalizeAccount),
enabled: !!accountId,
});
}, queryClient);
const relationshipQuery = useRelationship(accountId, {
enabled: opts.withRelationship,
});
const relationshipQuery = useAccountRelationship(opts.withRelationship ? accountId : undefined);
let data;
if (accountQuery.data) {
data = {
...accountQuery.data,
relationship: relationshipQuery.relationship,
moved: opts.withMoveTarget && queryClient.getQueryData(['accounts', 'entities', accountQuery.data?.moved_id]) as MinifiedAccount || null,
relationship: relationshipQuery.data,
moved: opts.withMoveTarget && queryClient.getQueryData(['accounts', 'entities', accountQuery.data?.moved_id]) as Account || null,
};
} else data = null;

View file

@ -0,0 +1,17 @@
import { useQuery } from '@tanstack/react-query';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
const useAccountRelationship = (accountId?: string) => {
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
return useQuery({
queryKey: ['accounts', 'entities', accountId],
queryFn: async () => (await client.accounts.getRelationships([accountId!]))[0],
enabled: !!accountId,
}, queryClient);
};
export { useAccountRelationship };

View file

@ -1,19 +1,20 @@
import { useQuery } from '@tanstack/react-query';
import { useClient } from 'pl-fe/hooks';
import { queryClient } from 'pl-hooks/client';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { queryClient, usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
import type { PlApiClient } from 'pl-api';
type Timeline = 'home' | 'notifications';
const useMarker = (timeline: Timeline) => {
const client = useClient();
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
return useQuery({
queryKey: ['markers', timeline],
queryFn: () => client.timelines.getMarkers([timeline]).then(markers => markers[timeline]),
});
}, queryClient);
};
const prefetchMarker = (client: PlApiClient, timeline: 'home' | 'notifications') =>

View file

@ -1,13 +1,13 @@
import { useMutation } from '@tanstack/react-query';
import { useClient } from 'pl-fe/hooks';
import { queryClient } from 'pl-hooks/client';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { queryClient } from 'pl-hooks/contexts/query-client';
import type { Timeline } from './useMarkers';
import type { Marker } from 'pl-api';
const useUpdateMarkerMutation = (timeline: Timeline) => {
const client = useClient();
const { client } = usePlHooksApiClient();
return useMutation({
mutationFn: (lastReadId: string) => client.timelines.saveMarkers({

View file

@ -1,13 +1,21 @@
import { useQuery } from '@tanstack/react-query';
import { useAppSelector, useClient } from 'pl-fe/hooks';
import { selectAccount, selectAccounts } from 'pl-fe/selectors';
import { queryClient } from 'pl-hooks/client';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { queryClient, usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
import { type NormalizedNotification, normalizeNotification } from 'pl-hooks/normalizers/normalizeNotifications';
import { useAccount } from '../accounts/useAccount';
import { useStatus } from '../statuses/useStatus';
type Account = ReturnType<typeof selectAccount>;
import type { Account } from 'pl-hooks/normalizers/normalizeAccount';
import type { Status } from 'pl-hooks/normalizers/normalizeStatus';
const getNotificationStatusId = (n: NormalizedNotification) => {
if (['mention', 'status', 'reblog', 'favourite', 'poll', 'update', 'emoji_reaction', 'event_reminder', 'participation_accepted', 'participation_request'].includes(n.type))
// @ts-ignore
return n.status_id;
return null;
};
const importNotification = (notification: NormalizedNotification) => {
queryClient.setQueryData<NormalizedNotification>(
@ -17,32 +25,40 @@ const importNotification = (notification: NormalizedNotification) => {
};
const useNotification = (notificationId: string) => {
const client = useClient();
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
const notificationQuery = useQuery({
queryKey: ['notifications', 'entities', notificationId],
queryFn: () => client.notifications.getNotification(notificationId)
.then(normalizeNotification),
});
}, queryClient);
const notification = notificationQuery.data;
const accountQuery = useAccount(notification?.account_id);
const moveTargetAccountQuery = useAccount(notification?.target_id);
const statusQuery = useStatus(notification?.status_id);
const accountsQuery = queryClient.getQueriesData<Account>({
queryKey: ['accounts', 'entities', notification?.account_ids],
});
const data: Notification | null = useAppSelector((state) => {
if (!notification) return null;
const accounts = selectAccounts(state, notification.account_ids).filter((account): account is Account => account !== undefined);
const moveTargetAccountQuery = useAccount(notification?.type === 'move' ? notification.target_id : undefined);
const statusQuery = useStatus(notification ? getNotificationStatusId(notification) : false);
return {
let data: (NormalizedNotification & {
account: Account;
accounts: Array<Account>;
target: Account | null;
status: Status | null;
}) | null = null;
if (notification) {
data = {
...notification,
account: accountQuery.data,
account: accountsQuery[0][1]!,
accounts: accountsQuery.map(([_, account]) => account!).filter(Boolean),
target: moveTargetAccountQuery.data,
status: statusQuery.data,
accounts,
};
});
}
return { ...notificationQuery, data };
};

View file

@ -1,9 +1,9 @@
import { useInfiniteQuery } from '@tanstack/react-query';
import { useClient } from 'pl-fe/hooks';
import { queryClient } from 'pl-hooks/client';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { queryClient, usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
import { importEntities } from 'pl-hooks/importer';
import { deduplicateNotifications } from 'pl-hooks/normalizers/deduplicateNotifications';
import { deduplicateNotifications } from 'pl-hooks/normalizers/normalizeNotifications';
import { flattenPages } from 'pl-hooks/utils/queries';
import type { Notification as BaseNotification, PaginatedResponse, PlApiClient } from 'pl-api';
@ -34,7 +34,8 @@ const importNotifications = (response: PaginatedResponse<BaseNotification>) => {
};
const useNotificationList = (params: UseNotificationParams) => {
const client = useClient();
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
const notificationsQuery = useInfiniteQuery({
queryKey: getQueryKey(params),
@ -44,7 +45,7 @@ const useNotificationList = (params: UseNotificationParams) => {
})).then(importNotifications),
initialPageParam: { previous: null, next: null } as Pick<PaginatedResponse<BaseNotification>, 'previous' | 'next'>,
getNextPageParam: (response) => response,
});
}, queryClient);
const data = flattenPages<string>(notificationsQuery.data) || [];

View file

@ -1,17 +1,12 @@
import { useQuery } from '@tanstack/react-query';
import { useAppSelector, useClient } from 'pl-fe/hooks';
import { selectAccount, selectAccounts } from 'pl-fe/selectors';
import { useIntl } from 'react-intl';
import { queryClient } from 'pl-hooks/client';
import { useAccount } from 'pl-hooks/hooks/accounts/useAccount';
import { usePlHooksApiClient } from 'pl-hooks/contexts/api-client';
import { queryClient, usePlHooksQueryClient } from 'pl-hooks/contexts/query-client';
import { importEntities } from 'pl-hooks/importer';
import { normalizeStatus, type Status } from '../../normalizers/normalizeStatus';
// import type { Group } from 'pl-fe/normalizers';
type Account = ReturnType<typeof selectAccount>;
import type { Account } from 'pl-hooks/normalizers/normalizeAccount';
// const toServerSideType = (columnType: string): Filter['context'][0] => {
// switch (columnType) {
@ -90,42 +85,43 @@ const importStatus = (status: Status) => {
);
};
const useStatus = (statusId?: string) => {
const client = useClient();
const intl = useIntl();
const useStatus = (statusId?: string, opts: { language?: string } = {}) => {
const queryClient = usePlHooksQueryClient();
const { client } = usePlHooksApiClient();
const statusQuery = useQuery({
queryKey: ['statuses', 'entities', statusId],
queryFn: () => client.statuses.getStatus(statusId!, {
language: intl.locale,
language: opts.language,
})
.then(status => (importEntities({ statuses: [status] }, { withParents: false }), status))
.then(normalizeStatus),
enabled: !!statusId,
});
}, queryClient);
const status = statusQuery.data;
const { data: account } = useAccount(status?.account_id || undefined);
queryClient.getQueriesData({ queryKey: ['test', ['t']] });
// : (Status & {
// account: Account;
// accounts: Array<Account>;
// reblog: Status | null;
// }) | null
const data = useAppSelector((state) => {
if (!status) return null;
const accounts = selectAccounts(state, status.account_ids).filter((account): account is Account => account !== undefined);
const accountsQuery = queryClient.getQueriesData<Account>({
queryKey: ['accounts', 'entities', status?.account_ids],
});
return {
let data: (Status & {
account: Account;
accounts: Array<Account>;
}) | null = null;
if (status) {
data = {
...status,
account: account!,
accounts,
account: accountsQuery[0][1]!,
accounts: accountsQuery.map(([_, account]) => account!).filter(Boolean),
// quote,
// reblog,
// poll
};
});
}
return { ...statusQuery, data };
};

View file

@ -1,7 +1,6 @@
import { queryClient } from 'pl-hooks/client';
import { queryClient } from 'pl-hooks/contexts/query-client';
import { type MinifiedNotification, minifyNotification } from './minifiers/minifyNotification';
import { DeduplicatedNotification } from './normalizers/normalizeNotifications';
import { type DeduplicatedNotification, type NormalizedNotification, normalizeNotification } from './normalizers/normalizeNotifications';
import { normalizeStatus, type Status } from './normalizers/normalizeStatus';
import type {
@ -20,9 +19,9 @@ const importGroup = (group: BaseGroup) => queryClient.setQueryData<BaseGroup>(
['groups', 'entities', group.id], group,
);
const importNotification = (notification: DeduplicatedNotification) => queryClient.setQueryData<MinifiedNotification>(
const importNotification = (notification: DeduplicatedNotification) => queryClient.setQueryData<NormalizedNotification>(
['notifications', 'entities', notification.id],
existingNotification => existingNotification?.duplicate ? existingNotification : minifyNotification(notification),
existingNotification => existingNotification?.duplicate ? existingNotification : normalizeNotification(notification),
);
const importPoll = (poll: BasePoll) => queryClient.setQueryData<BasePoll>(

View file

@ -1,79 +0,0 @@
import omit from 'lodash/omit';
import { DeduplicatedNotification } from '../normalizers/normalizeNotifications';
import type { AccountWarning, RelationshipSeveranceEvent } from 'pl-api';
const minifyNotification = (notification: DeduplicatedNotification) => {
// @ts-ignore
const minifiedNotification: {
duplicate: boolean;
account_id: string;
account_ids: string[];
created_at: string;
id: string;
group_key: string;
} & (
| { type: 'follow' | 'follow_request' | 'admin.sign_up' | 'bite' }
| {
type: 'mention';
subtype?: 'reply';
status_id: string;
}
| {
type: 'status' | 'reblog' | 'favourite' | 'poll' | 'update' | 'event_reminder';
status_id: string;
}
| {
type: 'admin.report';
report: Report;
}
| {
type: 'severed_relationships';
relationship_severance_event: RelationshipSeveranceEvent;
}
| {
type: 'moderation_warning';
moderation_warning: AccountWarning;
}
| {
type: 'move';
target_id: string;
}
| {
type: 'emoji_reaction';
emoji: string;
emoji_url: string | null;
status_id: string;
}
| {
type: 'chat_mention';
chat_message_id: string;
}
| {
type: 'participation_accepted' | 'participation_request';
status_id: string;
participation_message: string | null;
}
) = {
...omit(notification, ['account', 'accounts', 'status', 'target', 'chat_message']),
account_id: notification.account.id,
account_ids: notification.accounts.map(({ id }) => id),
created_at: notification.created_at,
id: notification.id,
type: notification.type,
};
// @ts-ignore
if (notification.status) minifiedNotification.status_id = notification.status.id;
// @ts-ignore
if (notification.target) minifiedNotification.target_id = notification.target.id;
// @ts-ignore
if (notification.chat_message) minifiedNotification.chat_message_id = notification.chat_message.id;
return minifiedNotification;
};
type MinifiedNotification = ReturnType<typeof minifyNotification>;
export { minifyNotification, type MinifiedNotification };

View file

@ -27,7 +27,7 @@ const getNotificationStatus = (n: Pick<BaseNotification, 'type'>) => {
return null;
};
const normalizeNotifications = (notifications: Array<BaseNotification>): Array<NormalizedNotification> => {
const deduplicateNotifications = (notifications: Array<BaseNotification>): Array<DeduplicatedNotification> => {
const deduplicatedNotifications: DeduplicatedNotification[] = [];
for (const notification of notifications) {
@ -50,10 +50,9 @@ const normalizeNotifications = (notifications: Array<BaseNotification>): Array<N
}
}
return deduplicatedNotifications.map(normalizeNotification);
return deduplicatedNotifications;
};
const normalizeNotification = (notification: BaseNotification | DeduplicatedNotification) => {
// @ts-ignore
const minifiedNotification: {
@ -127,5 +126,4 @@ const normalizeNotification = (notification: BaseNotification | DeduplicatedNoti
type NormalizedNotification = ReturnType<typeof normalizeNotification>;
export { normalizeNotifications, normalizeNotification, type NormalizedNotification };
export { deduplicateNotifications, normalizeNotification, type DeduplicatedNotification, type NormalizedNotification };

View file

@ -20,6 +20,7 @@
"devDependencies": {
"@types/lodash": "^4.17.10",
"@types/node": "^20.14.12",
"@types/react": "^18.3.11",
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.49.0",
@ -34,7 +35,8 @@
"dependencies": {
"@tanstack/react-query": "^5.56.2",
"lodash": "^4.17.21",
"pl-api": "^0.0.37"
"pl-api": "^0.0.37",
"react": "^18.3.1"
},
"module": "./dist/main.es.js",
"types": "dist/main.d.ts",

View file

@ -4,7 +4,7 @@
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */

View file

@ -420,6 +420,19 @@
dependencies:
undici-types "~5.26.4"
"@types/prop-types@*":
version "15.7.13"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451"
integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==
"@types/react@^18.3.11":
version "18.3.11"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.11.tgz#9d530601ff843ee0d7030d4227ea4360236bd537"
integrity sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
"@typescript-eslint/eslint-plugin@^7.18.0":
version "7.18.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3"
@ -891,6 +904,11 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
csstype@^3.0.2:
version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
data-view-buffer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2"
@ -1777,6 +1795,11 @@ jju@~1.4.0:
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@ -1873,6 +1896,13 @@ lodash@^4.17.21, lodash@~4.17.15:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@ -2159,6 +2189,13 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
react@^18.3.1:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
dependencies:
loose-envify "^1.1.0"
regexp.prototype.flags@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334"