From 74c7175c36d2f5ec0c2d247f75faf695346e472a Mon Sep 17 00:00:00 2001
From: Morgan Ridel <mail@morganridel.fr>
Date: Thu, 11 May 2023 14:57:31 +0200
Subject: [PATCH 1/5] feat: handle favorite queries

---
 package.json                       |   2 +-
 src/api/index.ts                   |   1 +
 src/api/itemFavorite.ts            |  37 ++++++
 src/api/routes.ts                  |   5 +
 src/config/keys.ts                 |   8 +-
 src/hooks/index.ts                 |   2 +
 src/hooks/itemFavorite.test.ts     |  48 +++++++
 src/hooks/itemFavorite.ts          |  27 ++++
 src/mutations/index.ts             |   2 +
 src/mutations/itemFavorite.test.ts | 156 +++++++++++++++++++++++
 src/mutations/itemFavorite.ts      |  50 ++++++++
 src/mutations/member.test.ts       | 196 +----------------------------
 src/mutations/member.ts            | 135 --------------------
 src/routines/index.ts              |   1 +
 src/routines/itemFavorite.ts       |   4 +
 src/routines/member.ts             |   3 -
 test/constants.ts                  |   8 ++
 yarn.lock                          |   8 +-
 18 files changed, 354 insertions(+), 339 deletions(-)
 create mode 100644 src/api/itemFavorite.ts
 create mode 100644 src/hooks/itemFavorite.test.ts
 create mode 100644 src/hooks/itemFavorite.ts
 create mode 100644 src/mutations/itemFavorite.test.ts
 create mode 100644 src/mutations/itemFavorite.ts
 create mode 100644 src/routines/itemFavorite.ts

diff --git a/package.json b/package.json
index 4e3c335f5..c38292e8d 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "Alexandre Chau"
   ],
   "dependencies": {
-    "@graasp/sdk": "1.0.0-rc1",
+    "@graasp/sdk": "github:graasp/graasp-sdk#refactor-favorite",
     "@graasp/translations": "1.13.0",
     "axios": "0.27.2",
     "crypto-js": "4.1.1",
diff --git a/src/api/index.ts b/src/api/index.ts
index da7c984a7..9b7498082 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -17,3 +17,4 @@ export * from './action';
 export * from './invitation';
 export * from './subscription';
 export * from './itemPublish';
+export * from './itemFavorite'
diff --git a/src/api/itemFavorite.ts b/src/api/itemFavorite.ts
new file mode 100644
index 000000000..5c6ae9a55
--- /dev/null
+++ b/src/api/itemFavorite.ts
@@ -0,0 +1,37 @@
+import {Item, UUID} from '@graasp/sdk';
+
+import { QueryClientConfig } from '../types';
+import configureAxios, { verifyAuthentication } from './axios';
+import {
+  GET_FAVORITE_ITEMS_ROUTE, buildFavoriteItemRoute
+} from './routes';
+
+const axios = configureAxios();
+
+
+export const getFavoriteItems = async ({ API_HOST }: QueryClientConfig): Promise<Item[]> =>
+  verifyAuthentication(() =>
+    axios
+      .get(`${API_HOST}/${GET_FAVORITE_ITEMS_ROUTE}`)
+      .then(({ data }) => data),
+  );
+
+export const favoriteItem = async (
+  id: UUID,
+  { API_HOST }: QueryClientConfig,
+) =>
+  verifyAuthentication(() =>
+    axios
+      .post(`${API_HOST}/${buildFavoriteItemRoute(id)}`)
+      .then(({ data }) => data),
+  );
+
+export const unfavoriteItem = async (
+  id: UUID,
+  { API_HOST }: QueryClientConfig,
+) =>
+  verifyAuthentication(() =>
+    axios
+      .delete(`${API_HOST}/${buildFavoriteItemRoute(id)}`)
+      .then(({ data }) => data),
+  );
diff --git a/src/api/routes.ts b/src/api/routes.ts
index 0ad85bc74..e936d8533 100644
--- a/src/api/routes.ts
+++ b/src/api/routes.ts
@@ -13,6 +13,7 @@ export const SUBSCRIPTION_ROUTE = 'subscriptions';
 export const GET_OWN_ITEMS_ROUTE = `${ITEMS_ROUTE}/own`;
 export const INVITATIONS_ROUTE = `invitations`;
 export const GET_RECYCLED_ITEMS_DATA_ROUTE = `${ITEMS_ROUTE}/recycled`;
+export const GET_FAVORITE_ITEMS_ROUTE = `${ITEMS_ROUTE}/favorite`;
 export const SHARED_ITEM_WITH_ROUTE = `${ITEMS_ROUTE}/shared-with`;
 export const CATEGORIES_ROUTE = `${ITEMS_ROUTE}/categories`;
 export const ETHERPAD_ROUTE = `${ITEMS_ROUTE}/etherpad`;
@@ -206,6 +207,9 @@ export const buildRecycleItemsRoute = (ids: UUID[]) =>
     },
   )}`;
 
+export const buildFavoriteItemRoute = (itemId: UUID) =>
+  `${GET_FAVORITE_ITEMS_ROUTE}/${itemId}`;
+
 export const buildRestoreItemsRoute = (ids: UUID[]) =>
   `${ITEMS_ROUTE}/restore${qs.stringify(
     { id: ids },
@@ -241,6 +245,7 @@ export const buildDeleteItemCategoryRoute = (args: {
   itemId: UUID;
   itemCategoryId: UUID;
 }) => `${ITEMS_ROUTE}/${args.itemId}/categories/${args.itemCategoryId}`;
+
 export const buildGetApiAccessTokenRoute = (id: UUID) =>
   `${APPS_ROUTE}/${id}/api-access-token`;
 
diff --git a/src/config/keys.ts b/src/config/keys.ts
index 152a2cf6c..6d63a8866 100644
--- a/src/config/keys.ts
+++ b/src/config/keys.ts
@@ -110,6 +110,9 @@ export const buildSearchByKeywordKey = (fields: SearchFields) => [
 ];
 
 export const RECYCLED_ITEMS_DATA_KEY = 'recycledItemsData';
+
+export const FAVORITE_ITEMS_KEY = 'favoriteItems';
+
 export const buildItemThumbnailKey = ({
   id,
   size = DEFAULT_THUMBNAIL_SIZE,
@@ -251,6 +254,7 @@ export const DATA_KEYS = {
   buildItemsByCategoriesKey,
   buildSearchByKeywordKey,
   RECYCLED_ITEMS_DATA_KEY,
+  FAVORITE_ITEMS_KEY,
   buildItemThumbnailKey,
   buildAvatarKey,
   buildGetLikesForMemberKey,
@@ -302,6 +306,8 @@ export const MUTATION_KEYS = {
   RECYCLE_ITEM: 'recycleItem',
   RECYCLE_ITEMS: 'recycleItems',
   RESTORE_ITEMS: 'restoreItems',
+  FAVORITE_ITEM: 'favoriteItem',
+  UNFAVORITE_ITEM: 'unfavoriteItem',
   POST_ITEM_CATEGORY: 'postItemCategory',
   DELETE_ITEM_CATEGORY: 'deleteItemCategory',
   UPLOAD_ITEM_THUMBNAIL: 'uploadItemThumbnail',
@@ -312,8 +318,6 @@ export const MUTATION_KEYS = {
   POST_ETHERPAD: 'postEtherpad',
   POST_ITEM_LIKE: 'postItemLike',
   DELETE_ITEM_LIKE: 'deleteItemLike',
-  ADD_FAVORITE_ITEM: 'addFavoriteItem',
-  DELETE_FAVORITE_ITEM: 'deleteFavoriteItem',
   POST_ITEM_VALIDATION: 'postItemValidation',
   UPDATE_ITEM_VALIDATION_REVIEW: 'updateItemValidationReview',
   EXPORT_ACTIONS: 'exportActions',
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index 98e738202..4bbd6f0fa 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -14,6 +14,7 @@ import configureItemLoginHooks from './itemLogin';
 import configureItemPublishedHooks from './itemPublish';
 import configureItemTagHooks from './itemTag';
 import configureItemValidationHooks from './itemValidation';
+import configureItemFavoriteHooks from './itemFavorite'
 import configureMemberHooks from './member';
 import configureMembershipHooks from './membership';
 import configureMentionsHooks from './mention';
@@ -50,6 +51,7 @@ export default (
     ...configureItemLoginHooks(queryConfig),
     ...configureItemPublishedHooks(queryConfig),
     ...configureItemValidationHooks(queryConfig),
+    ...configureItemFavoriteHooks(queryConfig),
     ...configureAppsHooks(queryConfig),
     ...configureActionHooks(queryConfig),
     ...configureInvitationHooks(queryConfig),
diff --git a/src/hooks/itemFavorite.test.ts b/src/hooks/itemFavorite.test.ts
new file mode 100644
index 000000000..30e01a5e6
--- /dev/null
+++ b/src/hooks/itemFavorite.test.ts
@@ -0,0 +1,48 @@
+import { StatusCodes } from 'http-status-codes';
+import Cookies from 'js-cookie';
+
+import { FAVORITE_ITEM, UNAUTHORIZED_RESPONSE } from '../../test/constants';
+import { mockHook, setUpTest } from '../../test/utils';
+import {
+  FAVORITE_ITEMS_KEY,
+} from '../config/keys';
+import { GET_FAVORITE_ITEMS_ROUTE } from '../api/routes';
+
+const { hooks, wrapper, queryClient } = setUpTest();
+jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });
+
+  describe('useLikesForItem', () => {
+    const route = `/${GET_FAVORITE_ITEMS_ROUTE}`;
+    const key = FAVORITE_ITEMS_KEY;
+
+    const hook = () => hooks.useFavoriteItems();
+
+    it(`Retrieve favorite items`, async () => {
+      const response = FAVORITE_ITEM;
+      const endpoints = [{ route, response: response.toJS() }];
+      await mockHook({ endpoints, hook, wrapper });
+
+      // verify cache keys
+      expect(queryClient.getQueryData(key)).toEqualImmutable(response);
+    });
+
+    it(`Unauthorized`, async () => {
+      const endpoints = [
+        {
+          route,
+          response: UNAUTHORIZED_RESPONSE,
+          statusCode: StatusCodes.UNAUTHORIZED,
+        },
+      ];
+      const { data, isError } = await mockHook({
+        hook,
+        wrapper,
+        endpoints,
+      });
+
+      expect(data).toBeFalsy();
+      expect(isError).toBeTruthy();
+      // verify cache keys
+      expect(queryClient.getQueryData(key)).toBeFalsy();
+    });
+});
diff --git a/src/hooks/itemFavorite.ts b/src/hooks/itemFavorite.ts
new file mode 100644
index 000000000..2f11077a5
--- /dev/null
+++ b/src/hooks/itemFavorite.ts
@@ -0,0 +1,27 @@
+import { List } from 'immutable';
+import { useQuery } from 'react-query';
+
+import { convertJs } from '@graasp/sdk';
+import { ItemFavoriteRecord } from '@graasp/sdk/frontend';
+
+import * as Api from '../api';
+import {
+  FAVORITE_ITEMS_KEY,
+} from '../config/keys';
+import { QueryClientConfig } from '../types';
+
+export default (queryConfig: QueryClientConfig) => {
+  const { defaultQueryOptions } = queryConfig;
+
+  const useFavoriteItems = () =>
+    useQuery<List<ItemFavoriteRecord>, Error>({
+      queryKey: FAVORITE_ITEMS_KEY,
+      queryFn: () =>
+        Api.getFavoriteItems(queryConfig).then((data) => convertJs(data)),
+      ...defaultQueryOptions,
+    });
+
+  return {
+    useFavoriteItems
+  };
+};
diff --git a/src/mutations/index.ts b/src/mutations/index.ts
index 553b96fd4..ef1140ed0 100644
--- a/src/mutations/index.ts
+++ b/src/mutations/index.ts
@@ -7,6 +7,7 @@ import chatMutations from './chat';
 import invitationMutations from './invitation';
 import itemMutations from './item';
 import itemCategoryMutations from './itemCategory';
+import itemFavoriteMutations from './itemFavorite';
 import itemExportMutations from './itemExport';
 import flagsMutations from './itemFlag';
 import itemLikeMutations from './itemLike';
@@ -32,6 +33,7 @@ const configureMutations = (
   ...chatMutations(queryClient, queryConfig),
   ...mentionMutations(queryClient, queryConfig),
   ...itemCategoryMutations(queryClient, queryConfig),
+  ...itemFavoriteMutations(queryClient, queryConfig),
   ...itemExportMutations(queryClient, queryConfig),
   ...itemLikeMutations(queryClient, queryConfig),
   ...itemValidationMutations(queryClient, queryConfig),
diff --git a/src/mutations/itemFavorite.test.ts b/src/mutations/itemFavorite.test.ts
new file mode 100644
index 000000000..63579dd91
--- /dev/null
+++ b/src/mutations/itemFavorite.test.ts
@@ -0,0 +1,156 @@
+import { act } from '@testing-library/react-hooks';
+import Cookies from 'js-cookie';
+import nock from 'nock';
+
+import { HttpMethod } from '@graasp/sdk';
+
+
+import { mockMutation, setUpTest, waitForMutation } from '../../test/utils';
+import {
+  buildFavoriteItemRoute,
+} from '../api/routes';
+import { FAVORITE_ITEMS_KEY } from '../config/keys';
+import { addFavoriteItemRoutine, deleteFavoriteItemRoutine } from '../routines';
+import { UNAUTHORIZED_RESPONSE } from '../../test/constants';
+import { StatusCodes } from 'http-status-codes';
+
+jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });
+
+const mockedNotifier = jest.fn();
+const { wrapper, queryClient, mutations } = setUpTest({
+  notifier: mockedNotifier,
+});
+describe('Favorite Mutations', () => {
+  afterEach(() => {
+    queryClient.clear();
+    nock.cleanAll();
+  });
+
+  describe('useFavoriteItem', () => {
+    const itemId = 'item-id';
+    const route = `/${buildFavoriteItemRoute(itemId)}`;
+    const mutation = mutations.useFavoriteItem;
+
+    it(`Successfully add favorite item`, async () => {
+      // set random data in cache
+      queryClient.setQueryData(FAVORITE_ITEMS_KEY, "mock");
+      const endpoints = [
+        {
+          response: itemId,
+          method: HttpMethod.POST,
+          route,
+        },
+      ];
+      const mockedMutation = await mockMutation({
+        mutation,
+        wrapper,
+        endpoints,
+      });
+
+      await act(async () => {
+        await mockedMutation.mutate(itemId);
+        await waitForMutation();
+      });
+
+      expect(
+        queryClient.getQueryState(FAVORITE_ITEMS_KEY)?.isInvalidated,
+      ).toBeTruthy();
+      expect(mockedNotifier).toHaveBeenCalledWith({
+        type: addFavoriteItemRoutine.SUCCESS,
+      });
+    });
+
+    it(`Unauthorized`, async () => {
+        const endpoints = [
+            {
+              response: UNAUTHORIZED_RESPONSE,
+              statusCode: StatusCodes.UNAUTHORIZED,
+              method: HttpMethod.POST,
+              route,
+            },
+          ];
+    
+          const mockedMutation = await mockMutation({
+            endpoints,
+            mutation,
+            wrapper,
+          });
+    
+          await act(async () => {
+            await mockedMutation.mutate(itemId);
+            await waitForMutation();
+          });
+    
+          expect(mockedNotifier).toHaveBeenCalledWith(
+            expect.objectContaining({
+              type: addFavoriteItemRoutine.FAILURE,
+            }),
+          );
+        });
+    });
+
+    describe('useDeleteItemLike', () => {
+        const itemId = 'item-id';
+        const route = `/${buildFavoriteItemRoute(itemId)}`;
+        const mutation = mutations.useUnfavoriteItem;
+    
+        it('Delete item like', async () => {
+          queryClient.setQueryData(FAVORITE_ITEMS_KEY, itemId);
+    
+    
+          const endpoints = [
+            {
+              response: itemId,
+              method: HttpMethod.DELETE,
+              route,
+            },
+          ];
+    
+          const mockedMutation = await mockMutation({
+            endpoints,
+            mutation,
+            wrapper,
+          });
+    
+          await act(async () => {
+            await mockedMutation.mutate(itemId);
+            await waitForMutation();
+          });
+    
+          expect(
+            queryClient.getQueryState(FAVORITE_ITEMS_KEY)?.isInvalidated,
+          ).toBeTruthy();
+          expect(mockedNotifier).toHaveBeenCalledWith({
+            type: deleteFavoriteItemRoutine.SUCCESS,
+          });
+        });
+    
+        it('Unauthorized to delete item like', async () => {
+          const endpoints = [
+            {
+              response: UNAUTHORIZED_RESPONSE,
+              statusCode: StatusCodes.UNAUTHORIZED,
+              method: HttpMethod.DELETE,
+              route,
+            },
+          ];
+    
+          const mockedMutation = await mockMutation({
+            endpoints,
+            mutation,
+            wrapper,
+          });
+    
+          await act(async () => {
+            await mockedMutation.mutate(itemId);
+            await waitForMutation();
+          });
+    
+          expect(mockedNotifier).toHaveBeenCalledWith(
+            expect.objectContaining({
+              type: deleteFavoriteItemRoutine.FAILURE,
+            }),
+          );
+        });
+    });
+});
diff --git a/src/mutations/itemFavorite.ts b/src/mutations/itemFavorite.ts
new file mode 100644
index 000000000..7090539c1
--- /dev/null
+++ b/src/mutations/itemFavorite.ts
@@ -0,0 +1,50 @@
+import { QueryClient, useMutation } from 'react-query';
+
+import { UUID } from '@graasp/sdk';
+
+import * as Api from '../api';
+import { FAVORITE_ITEMS_KEY, MUTATION_KEYS } from '../config/keys';
+import {
+  addFavoriteItemRoutine,
+  deleteFavoriteItemRoutine
+} from '../routines';
+import { QueryClientConfig } from '../types';
+
+export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
+  const { notifier } = queryConfig;
+
+  queryClient.setMutationDefaults(MUTATION_KEYS.FAVORITE_ITEM, {
+    mutationFn: (itemId) => Api.favoriteItem(itemId, queryConfig),
+    onSuccess: () => {
+      notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
+    },
+    onError: (error) => {
+      notifier?.({ type: addFavoriteItemRoutine.FAILURE, payload: { error } });
+    },
+    onSettled: () => {
+      queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
+    },
+  });
+  const useFavoriteItem = () =>
+    useMutation<void, unknown, UUID>(MUTATION_KEYS.FAVORITE_ITEM);
+
+  queryClient.setMutationDefaults(MUTATION_KEYS.UNFAVORITE_ITEM, {
+    mutationFn: (itemId) => Api.unfavoriteItem(itemId, queryConfig),
+    onSuccess: () => {
+      notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
+    },
+    onError: (error) => {
+      notifier?.({ type: deleteFavoriteItemRoutine.FAILURE, payload: { error } });
+    },
+    onSettled: () => {
+      queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
+    },
+  });
+  const useUnfavoriteItem = () =>
+    useMutation<void, unknown, UUID>(MUTATION_KEYS.UNFAVORITE_ITEM);
+
+  return {
+    useFavoriteItem,
+    useUnfavoriteItem
+  };
+};
diff --git a/src/mutations/member.test.ts b/src/mutations/member.test.ts
index 3c524ee00..4f64c36dd 100644
--- a/src/mutations/member.test.ts
+++ b/src/mutations/member.test.ts
@@ -3,7 +3,7 @@ import { StatusCodes } from 'http-status-codes';
 import Cookies from 'js-cookie';
 import nock from 'nock';
 
-import { HttpMethod, ThumbnailSize, convertJs } from '@graasp/sdk';
+import { HttpMethod, ThumbnailSize } from '@graasp/sdk';
 import { MemberRecord } from '@graasp/sdk/frontend';
 import { SUCCESS_MESSAGES } from '@graasp/translations';
 
@@ -21,7 +21,7 @@ import {
   buildUploadAvatarRoute,
 } from '../api/routes';
 import { CURRENT_MEMBER_KEY, buildAvatarKey } from '../config/keys';
-import { addFavoriteItemRoutine, uploadAvatarRoutine } from '../routines';
+import { uploadAvatarRoutine } from '../routines';
 
 jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });
 
@@ -308,196 +308,4 @@ describe('Member Mutations', () => {
       });
     });
   });
-
-  describe('useAddFavoriteItem', () => {
-    const id = 'member-id';
-    const itemId = 'item-id';
-    const extra = {
-      favoriteItems: [],
-    };
-    const route = `/${buildPatchMember(id)}`;
-    const mutation = mutations.useAddFavoriteItem;
-
-    it(`Successfully add favorite item`, async () => {
-      const response = MEMBER_RESPONSE.set(
-        'extra',
-        convertJs({ favoriteItems: ['item-id'] }),
-      );
-      // set random data in cache
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, MEMBER_RESPONSE);
-      const endpoints = [
-        {
-          response,
-          method: HttpMethod.PATCH,
-          route,
-        },
-      ];
-      const mockedMutation = await mockMutation({
-        mutation,
-        wrapper,
-        endpoints,
-      });
-
-      await act(async () => {
-        await mockedMutation.mutate({ memberId: id, itemId, extra });
-        await waitForMutation();
-      });
-
-      expect(
-        queryClient.getQueryState(CURRENT_MEMBER_KEY)?.isInvalidated,
-      ).toBeTruthy();
-      expect(mockedNotifier).toHaveBeenCalledWith({
-        type: addFavoriteItemRoutine.SUCCESS,
-      });
-
-      // verify cache keys
-      const newData = queryClient.getQueryData(
-        CURRENT_MEMBER_KEY,
-      ) as MemberRecord;
-      expect(newData).toEqualImmutable(response);
-    });
-
-    it(`Adding duplicated favorite item should dedupe`, async () => {
-      const response = MEMBER_RESPONSE.set(
-        'extra',
-        convertJs({ favoriteItems: ['item-id'] }),
-      );
-      // set random data in cache
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, response);
-      const endpoints = [
-        {
-          response,
-          method: HttpMethod.PATCH,
-          route,
-        },
-      ];
-      const mockedMutation = await mockMutation({
-        mutation,
-        wrapper,
-        endpoints,
-      });
-
-      await act(async () => {
-        await mockedMutation.mutate({
-          memberId: id,
-          itemId,
-          extra: { favoriteItems: ['item-id'] },
-        });
-        await waitForMutation();
-      });
-
-      expect(
-        queryClient.getQueryState(CURRENT_MEMBER_KEY)?.isInvalidated,
-      ).toBeTruthy();
-      expect(mockedNotifier).toHaveBeenCalledWith({
-        type: addFavoriteItemRoutine.SUCCESS,
-      });
-
-      // verify cache keys
-      const newData = queryClient.getQueryData(
-        CURRENT_MEMBER_KEY,
-      ) as MemberRecord;
-      expect(newData).toEqualImmutable(response);
-    });
-
-    it(`Unauthorized`, async () => {
-      // set random data in cache
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, MEMBER_RESPONSE);
-      const endpoints = [
-        {
-          response: UNAUTHORIZED_RESPONSE,
-          statusCode: StatusCodes.UNAUTHORIZED,
-          method: HttpMethod.PATCH,
-          route,
-        },
-      ];
-      const mockedMutation = await mockMutation({
-        mutation,
-        wrapper,
-        endpoints,
-      });
-
-      await act(async () => {
-        await mockedMutation.mutate({ memberId: id, itemId, extra });
-        await waitForMutation();
-      });
-
-      // verify cache keys
-      const oldData = queryClient.getQueryData(
-        CURRENT_MEMBER_KEY,
-      ) as MemberRecord;
-      expect(oldData).toEqualImmutable(MEMBER_RESPONSE);
-    });
-  });
-
-  describe('useDeleteFavoriteItem', () => {
-    const id = 'member-id';
-    const route = `/${buildPatchMember(id)}`;
-    const itemId = 'item-id';
-    const extra = {
-      favoriteItems: ['item-id', 'item-id2'],
-    };
-    const mutation = mutations.useDeleteFavoriteItem;
-
-    it(`Successfully delete favorite item`, async () => {
-      const response = MEMBER_RESPONSE.set(
-        'extra',
-        convertJs({ favoriteItems: ['item-id2'] }),
-      );
-      // set random data in cache
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, MEMBER_RESPONSE);
-      const endpoints = [
-        {
-          response,
-          method: HttpMethod.PATCH,
-          route,
-        },
-      ];
-      const mockedMutation = await mockMutation({
-        mutation,
-        wrapper,
-        endpoints,
-      });
-
-      await act(async () => {
-        await mockedMutation.mutate({ memberId: id, itemId, extra });
-        await waitForMutation();
-      });
-
-      // verify cache keys
-      const newData = queryClient.getQueryData(
-        CURRENT_MEMBER_KEY,
-      ) as MemberRecord;
-      expect(newData).toEqualImmutable(response);
-    });
-
-    it(`Unauthorized`, async () => {
-      // set random data in cache
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, MEMBER_RESPONSE);
-      const endpoints = [
-        {
-          response: UNAUTHORIZED_RESPONSE,
-          statusCode: StatusCodes.UNAUTHORIZED,
-          method: HttpMethod.PATCH,
-          route,
-        },
-      ];
-      const mockedMutation = await mockMutation({
-        mutation,
-        wrapper,
-        endpoints,
-      });
-
-      await act(async () => {
-        await mockedMutation.mutate({ memberId: id, itemId, extra });
-        await waitForMutation();
-      });
-
-      // verify cache keys
-      const oldData = queryClient.getQueryData(
-        CURRENT_MEMBER_KEY,
-      ) as MemberRecord;
-      expect(oldData).toEqualImmutable(MEMBER_RESPONSE);
-    });
-  });
 });
diff --git a/src/mutations/member.ts b/src/mutations/member.ts
index 3264781c9..60f6c657b 100644
--- a/src/mutations/member.ts
+++ b/src/mutations/member.ts
@@ -12,8 +12,6 @@ import {
   buildAvatarKey,
 } from '../config/keys';
 import {
-  addFavoriteItemRoutine,
-  deleteFavoriteItemRoutine,
   deleteMemberRoutine,
   editMemberRoutine,
   uploadAvatarRoutine,
@@ -24,8 +22,6 @@ const {
   DELETE_MEMBER,
   EDIT_MEMBER,
   UPLOAD_AVATAR,
-  ADD_FAVORITE_ITEM,
-  DELETE_FAVORITE_ITEM,
 } = MUTATION_KEYS;
 
 export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
@@ -137,139 +133,8 @@ export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
       UPLOAD_AVATAR,
     );
 
-  // mutation to update favorite items of given member
-  queryClient.setMutationDefaults(ADD_FAVORITE_ITEM, {
-    mutationFn: ({ memberId, itemId, extra: prevExtra }) => {
-      const favoriteItems = prevExtra.favoriteItems
-        ? prevExtra.favoriteItems.concat(
-            prevExtra.favoriteItems.includes(itemId) ? [] : [itemId],
-          )
-        : [itemId];
-      return Api.editMember(
-        {
-          id: memberId,
-          extra: {
-            ...prevExtra,
-            favoriteItems,
-          },
-        },
-        queryConfig,
-      ).then((member) => convertJs(member));
-    },
-    onMutate: async (payload) => {
-      // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
-      await queryClient.cancelQueries(CURRENT_MEMBER_KEY);
-
-      // Snapshot the previous value
-      const previousMember =
-        queryClient.getQueryData<MemberRecord>(CURRENT_MEMBER_KEY);
-
-      // Optimistically update to the new value
-      const { itemId, extra } = payload;
-      const favoriteItems = extra.favoriteItems
-        ? extra.favoriteItems.concat(
-            extra.favoriteItems.includes(itemId) ? [] : [itemId],
-          )
-        : [itemId];
-
-      const member = convertJs({
-        extra: { ...extra, favoriteItems },
-      });
-
-      queryClient.setQueryData(
-        CURRENT_MEMBER_KEY,
-        previousMember?.merge(member),
-      );
-
-      // Return a context object with the snapshotted value
-      return { previousMember };
-    },
-    onSuccess: () => {
-      notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
-    },
-    // If the mutation fails, use the context returned from onMutate to roll back
-    onError: (error, _, context) => {
-      notifier?.({ type: addFavoriteItemRoutine.FAILURE, payload: { error } });
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, context.previousMember);
-    },
-    // Always refetch after error or success:
-    onSettled: () => {
-      // invalidate all queries
-      queryClient.invalidateQueries(CURRENT_MEMBER_KEY);
-    },
-  });
-  const useAddFavoriteItem = () =>
-    useMutation<
-      void,
-      unknown,
-      { memberId: UUID; itemId: UUID; extra: MemberExtra }
-    >(ADD_FAVORITE_ITEM);
-
-  queryClient.setMutationDefaults(DELETE_FAVORITE_ITEM, {
-    mutationFn: (payload) => {
-      const { memberId, itemId, extra: prevExtra } = payload;
-      const newFavoriteItems = prevExtra.favoriteItems?.filter(
-        (id: UUID) => id !== itemId,
-      );
-      return Api.editMember(
-        {
-          id: memberId,
-          extra: { ...prevExtra, favoriteItems: newFavoriteItems },
-        },
-        queryConfig,
-      ).then((member) => convertJs(member));
-    },
-    onMutate: async (payload) => {
-      // Cancel any outgoing refetches (so they don't overwrite our optimistic update)
-      await queryClient.cancelQueries(CURRENT_MEMBER_KEY);
-
-      // Snapshot the previous value
-      const previousMember =
-        queryClient.getQueryData<MemberRecord>(CURRENT_MEMBER_KEY);
-
-      // Optimistically update to the new value
-      const { itemId, extra } = payload;
-      extra.favoriteItems = extra.favoriteItems?.filter(
-        (id: UUID) => id !== itemId,
-      );
-      const member = convertJs({ extra });
-
-      queryClient.setQueryData(
-        CURRENT_MEMBER_KEY,
-        previousMember?.merge(member),
-      );
-
-      // Return a context object with the snapshotted value
-      return { previousMember };
-    },
-    onSuccess: () => {
-      notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
-    },
-    // If the mutation fails, use the context returned from onMutate to roll back
-    onError: (error, _, context) => {
-      notifier?.({
-        type: deleteFavoriteItemRoutine.FAILURE,
-        payload: { error },
-      });
-      queryClient.setQueryData(CURRENT_MEMBER_KEY, context.previousMember);
-    },
-    // Always refetch after error or success:
-    onSettled: () => {
-      // invalidate all queries
-      queryClient.invalidateQueries(CURRENT_MEMBER_KEY);
-    },
-  });
-  const useDeleteFavoriteItem = () =>
-    useMutation<
-      void,
-      unknown,
-      { memberId: UUID; itemId: UUID; extra: MemberExtra }
-    >(DELETE_FAVORITE_ITEM);
-
   return {
     useDeleteMember,
-    useDeleteFavoriteItem,
-    useAddFavoriteItem,
     useUploadAvatar,
     useEditMember,
   };
diff --git a/src/routines/index.ts b/src/routines/index.ts
index 4090c8211..6ac8f607b 100644
--- a/src/routines/index.ts
+++ b/src/routines/index.ts
@@ -7,6 +7,7 @@ export * from './itemFlag';
 export * from './chat';
 export * from './mentions';
 export * from './itemCategory';
+export * from './itemFavorite'
 export * from './itemExport';
 export * from './itemLike';
 export * from './itemValidation';
diff --git a/src/routines/itemFavorite.ts b/src/routines/itemFavorite.ts
new file mode 100644
index 000000000..3f6e8104a
--- /dev/null
+++ b/src/routines/itemFavorite.ts
@@ -0,0 +1,4 @@
+import createRoutine from "./utils";
+
+export const addFavoriteItemRoutine = createRoutine('ADD_FAVORITE_ITEM');
+export const deleteFavoriteItemRoutine = createRoutine('DELETE_FAVORITE_ITEM');
\ No newline at end of file
diff --git a/src/routines/member.ts b/src/routines/member.ts
index 5b7fc2f1f..7d9fd06d0 100644
--- a/src/routines/member.ts
+++ b/src/routines/member.ts
@@ -1,10 +1,7 @@
 import createRoutine from './utils';
 
 export const getMembersRoutine = createRoutine('GET_MEMBERS');
-
 export const editMemberRoutine = createRoutine('EDIT_MEMBER');
 export const deleteMemberRoutine = createRoutine('DELETE_MEMBER');
 export const uploadAvatarRoutine = createRoutine('UPLOAD_AVATAR');
-export const addFavoriteItemRoutine = createRoutine('ADD_FAVORITE_ITEM');
-export const deleteFavoriteItemRoutine = createRoutine('DELETE_FAVORITE_ITEM');
 export const shareItemRoutine = createRoutine('SHARE_ITEM');
diff --git a/test/constants.ts b/test/constants.ts
index fa98d6091..634ed79ff 100644
--- a/test/constants.ts
+++ b/test/constants.ts
@@ -46,6 +46,7 @@ import {
   InvitationRecord,
   ItemCategoryRecord,
   ItemChatRecord,
+  ItemFavoriteRecord,
   ItemFlagRecord,
   ItemLikeRecord,
   ItemLoginSchemaRecord,
@@ -211,6 +212,13 @@ export const RECYCLED_ITEM_DATA: List<RecycledItemDataRecord> = convertJs([
   },
 ]);
 
+export const FAVORITE_ITEM: ItemFavoriteRecord = convertJs([
+  {
+    id: `favorite-item-id`,
+    item: ITEM_1,
+  },
+]);
+
 const MEMBER_RESPONSE_2: MemberRecord = createMockMember({
   id: '421',
   name: 'username1',
diff --git a/yarn.lock b/yarn.lock
index 66bfc9aff..33870ddb3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2479,7 +2479,7 @@ __metadata:
     "@babel/preset-typescript": 7.21.5
     "@commitlint/cli": 17.6.1
     "@commitlint/config-conventional": 17.6.1
-    "@graasp/sdk": 1.0.0-rc1
+    "@graasp/sdk": "github:graasp/graasp-sdk#refactor-favorite"
     "@graasp/translations": 1.13.0
     "@testing-library/jest-dom": 5.16.5
     "@testing-library/react": 14.0.0
@@ -2535,9 +2535,9 @@ __metadata:
   languageName: unknown
   linkType: soft
 
-"@graasp/sdk@npm:1.0.0-rc1":
+"@graasp/sdk@github:graasp/graasp-sdk#refactor-favorite":
   version: 1.0.0-rc1
-  resolution: "@graasp/sdk@npm:1.0.0-rc1"
+  resolution: "@graasp/sdk@https://github.com/graasp/graasp-sdk.git#commit=d98d8976d9301d3732c993b76ac417e30cfeccf0"
   dependencies:
     "@fastify/secure-session": 5.3.0
     "@graasp/etherpad-api": 2.1.1
@@ -2550,7 +2550,7 @@ __metadata:
     typeorm: 0.3.15
     uuid: 9.0.0
     validator: 13.9.0
-  checksum: 7d542c1c92d0846adade40b98c4c9915a3a7c1bf2abc404eae062e2b0dd249bf10d81e19d465bbf5bdfe2d2f4667277b03636ea032ebe616dc90203be7771845
+  checksum: 7286ca0f9e10a785bb756ce1bc5dc75d74fac3b0b0285da2e2a5b220a041e61944788bfc17e5387c26247826fadcfced5497ee152edeb9d379778d759f7a43d2
   languageName: node
   linkType: hard
 

From 0e7647f3bd02498f9b8c0f8eda850f93a661e2b8 Mon Sep 17 00:00:00 2001
From: Morgan Ridel <mail@morganridel.fr>
Date: Mon, 19 Jun 2023 09:45:41 +0000
Subject: [PATCH 2/5] fix: add favorites to exported API_ROUTES

---
 src/api/routes.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/api/routes.ts b/src/api/routes.ts
index e936d8533..26b4cc5a0 100644
--- a/src/api/routes.ts
+++ b/src/api/routes.ts
@@ -449,6 +449,8 @@ export const API_ROUTES = {
   GET_ITEM_VALIDATION_STATUSES_ROUTE,
   GET_OWN_ITEMS_ROUTE,
   GET_RECYCLED_ITEMS_DATA_ROUTE,
+  GET_FAVORITE_ITEMS_ROUTE,
+  buildFavoriteItemRoute,
   GET_TAGS_ROUTE,
   ITEMS_ROUTE,
   SHARED_ITEM_WITH_ROUTE,

From 7f43b555ad600b7949d8007e3c0461069352d3a8 Mon Sep 17 00:00:00 2001
From: Morgan Ridel <mail@morganridel.fr>
Date: Mon, 19 Jun 2023 13:15:17 +0000
Subject: [PATCH 3/5] chore: prettier and review fixes

---
 src/api/index.ts                   |   2 +-
 src/api/itemFavorite.ts            |  15 ++-
 src/config/keys.ts                 |   4 +-
 src/hooks/index.ts                 |   2 +-
 src/hooks/itemFavorite.test.ts     |  70 +++++------
 src/hooks/itemFavorite.ts          |   6 +-
 src/mutations/index.ts             |   2 +-
 src/mutations/itemFavorite.test.ts | 195 ++++++++++++++---------------
 src/mutations/itemFavorite.ts      |  30 ++---
 src/mutations/member.ts            |   6 +-
 src/routines/index.ts              |   2 +-
 src/routines/itemFavorite.ts       |   4 +-
 12 files changed, 160 insertions(+), 178 deletions(-)

diff --git a/src/api/index.ts b/src/api/index.ts
index 9b7498082..92fe82c4d 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -17,4 +17,4 @@ export * from './action';
 export * from './invitation';
 export * from './subscription';
 export * from './itemPublish';
-export * from './itemFavorite'
+export * from './itemFavorite';
diff --git a/src/api/itemFavorite.ts b/src/api/itemFavorite.ts
index 5c6ae9a55..4d3a8abba 100644
--- a/src/api/itemFavorite.ts
+++ b/src/api/itemFavorite.ts
@@ -1,22 +1,21 @@
-import {Item, UUID} from '@graasp/sdk';
+import { Item, UUID } from '@graasp/sdk';
 
 import { QueryClientConfig } from '../types';
 import configureAxios, { verifyAuthentication } from './axios';
-import {
-  GET_FAVORITE_ITEMS_ROUTE, buildFavoriteItemRoute
-} from './routes';
+import { GET_FAVORITE_ITEMS_ROUTE, buildFavoriteItemRoute } from './routes';
 
 const axios = configureAxios();
 
-
-export const getFavoriteItems = async ({ API_HOST }: QueryClientConfig): Promise<Item[]> =>
+export const getFavoriteItems = async ({
+  API_HOST,
+}: QueryClientConfig): Promise<Item[]> =>
   verifyAuthentication(() =>
     axios
       .get(`${API_HOST}/${GET_FAVORITE_ITEMS_ROUTE}`)
       .then(({ data }) => data),
   );
 
-export const favoriteItem = async (
+export const addFavoriteItem = async (
   id: UUID,
   { API_HOST }: QueryClientConfig,
 ) =>
@@ -26,7 +25,7 @@ export const favoriteItem = async (
       .then(({ data }) => data),
   );
 
-export const unfavoriteItem = async (
+export const removeFavoriteItem = async (
   id: UUID,
   { API_HOST }: QueryClientConfig,
 ) =>
diff --git a/src/config/keys.ts b/src/config/keys.ts
index 6d63a8866..b9c4aefdf 100644
--- a/src/config/keys.ts
+++ b/src/config/keys.ts
@@ -306,8 +306,8 @@ export const MUTATION_KEYS = {
   RECYCLE_ITEM: 'recycleItem',
   RECYCLE_ITEMS: 'recycleItems',
   RESTORE_ITEMS: 'restoreItems',
-  FAVORITE_ITEM: 'favoriteItem',
-  UNFAVORITE_ITEM: 'unfavoriteItem',
+  ADD_FAVORITE_ITEM: 'addFavoriteItem',
+  REMOVE_FAVORITE_ITEM: 'removeFavoriteItem',
   POST_ITEM_CATEGORY: 'postItemCategory',
   DELETE_ITEM_CATEGORY: 'deleteItemCategory',
   UPLOAD_ITEM_THUMBNAIL: 'uploadItemThumbnail',
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index 4bbd6f0fa..60326d600 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -8,13 +8,13 @@ import configureCategoryHooks from './category';
 import configureChatHooks from './chat';
 import configureInvitationHooks from './invitation';
 import configureItemHooks from './item';
+import configureItemFavoriteHooks from './itemFavorite';
 import configureItemFlagHooks from './itemFlag';
 import configureItemLikeHooks from './itemLike';
 import configureItemLoginHooks from './itemLogin';
 import configureItemPublishedHooks from './itemPublish';
 import configureItemTagHooks from './itemTag';
 import configureItemValidationHooks from './itemValidation';
-import configureItemFavoriteHooks from './itemFavorite'
 import configureMemberHooks from './member';
 import configureMembershipHooks from './membership';
 import configureMentionsHooks from './mention';
diff --git a/src/hooks/itemFavorite.test.ts b/src/hooks/itemFavorite.test.ts
index 30e01a5e6..6a0aa0692 100644
--- a/src/hooks/itemFavorite.test.ts
+++ b/src/hooks/itemFavorite.test.ts
@@ -1,48 +1,44 @@
 import { StatusCodes } from 'http-status-codes';
-import Cookies from 'js-cookie';
 
 import { FAVORITE_ITEM, UNAUTHORIZED_RESPONSE } from '../../test/constants';
 import { mockHook, setUpTest } from '../../test/utils';
-import {
-  FAVORITE_ITEMS_KEY,
-} from '../config/keys';
 import { GET_FAVORITE_ITEMS_ROUTE } from '../api/routes';
+import { FAVORITE_ITEMS_KEY } from '../config/keys';
 
 const { hooks, wrapper, queryClient } = setUpTest();
-jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });
 
-  describe('useLikesForItem', () => {
-    const route = `/${GET_FAVORITE_ITEMS_ROUTE}`;
-    const key = FAVORITE_ITEMS_KEY;
-
-    const hook = () => hooks.useFavoriteItems();
-
-    it(`Retrieve favorite items`, async () => {
-      const response = FAVORITE_ITEM;
-      const endpoints = [{ route, response: response.toJS() }];
-      await mockHook({ endpoints, hook, wrapper });
-
-      // verify cache keys
-      expect(queryClient.getQueryData(key)).toEqualImmutable(response);
+describe('useFavoriteItems', () => {
+  const route = `/${GET_FAVORITE_ITEMS_ROUTE}`;
+  const key = FAVORITE_ITEMS_KEY;
+
+  const hook = () => hooks.useFavoriteItems();
+
+  it(`Retrieve favorite items`, async () => {
+    const response = FAVORITE_ITEM;
+    const endpoints = [{ route, response: response.toJS() }];
+    await mockHook({ endpoints, hook, wrapper });
+
+    // verify cache keys
+    expect(queryClient.getQueryData(key)).toEqualImmutable(response);
+  });
+
+  it(`Unauthorized`, async () => {
+    const endpoints = [
+      {
+        route,
+        response: UNAUTHORIZED_RESPONSE,
+        statusCode: StatusCodes.UNAUTHORIZED,
+      },
+    ];
+    const { data, isError } = await mockHook({
+      hook,
+      wrapper,
+      endpoints,
     });
 
-    it(`Unauthorized`, async () => {
-      const endpoints = [
-        {
-          route,
-          response: UNAUTHORIZED_RESPONSE,
-          statusCode: StatusCodes.UNAUTHORIZED,
-        },
-      ];
-      const { data, isError } = await mockHook({
-        hook,
-        wrapper,
-        endpoints,
-      });
-
-      expect(data).toBeFalsy();
-      expect(isError).toBeTruthy();
-      // verify cache keys
-      expect(queryClient.getQueryData(key)).toBeFalsy();
-    });
+    expect(data).toBeFalsy();
+    expect(isError).toBeTruthy();
+    // verify cache keys
+    expect(queryClient.getQueryData(key)).toBeFalsy();
+  });
 });
diff --git a/src/hooks/itemFavorite.ts b/src/hooks/itemFavorite.ts
index 2f11077a5..164b107f7 100644
--- a/src/hooks/itemFavorite.ts
+++ b/src/hooks/itemFavorite.ts
@@ -5,9 +5,7 @@ import { convertJs } from '@graasp/sdk';
 import { ItemFavoriteRecord } from '@graasp/sdk/frontend';
 
 import * as Api from '../api';
-import {
-  FAVORITE_ITEMS_KEY,
-} from '../config/keys';
+import { FAVORITE_ITEMS_KEY } from '../config/keys';
 import { QueryClientConfig } from '../types';
 
 export default (queryConfig: QueryClientConfig) => {
@@ -22,6 +20,6 @@ export default (queryConfig: QueryClientConfig) => {
     });
 
   return {
-    useFavoriteItems
+    useFavoriteItems,
   };
 };
diff --git a/src/mutations/index.ts b/src/mutations/index.ts
index ef1140ed0..449fa8d59 100644
--- a/src/mutations/index.ts
+++ b/src/mutations/index.ts
@@ -7,8 +7,8 @@ import chatMutations from './chat';
 import invitationMutations from './invitation';
 import itemMutations from './item';
 import itemCategoryMutations from './itemCategory';
-import itemFavoriteMutations from './itemFavorite';
 import itemExportMutations from './itemExport';
+import itemFavoriteMutations from './itemFavorite';
 import flagsMutations from './itemFlag';
 import itemLikeMutations from './itemLike';
 import itemLoginMutations from './itemLogin';
diff --git a/src/mutations/itemFavorite.test.ts b/src/mutations/itemFavorite.test.ts
index 63579dd91..72f83b5d8 100644
--- a/src/mutations/itemFavorite.test.ts
+++ b/src/mutations/itemFavorite.test.ts
@@ -1,20 +1,14 @@
 import { act } from '@testing-library/react-hooks';
-import Cookies from 'js-cookie';
+import { StatusCodes } from 'http-status-codes';
 import nock from 'nock';
 
 import { HttpMethod } from '@graasp/sdk';
 
-
+import { UNAUTHORIZED_RESPONSE } from '../../test/constants';
 import { mockMutation, setUpTest, waitForMutation } from '../../test/utils';
-import {
-  buildFavoriteItemRoute,
-} from '../api/routes';
+import { buildFavoriteItemRoute } from '../api/routes';
 import { FAVORITE_ITEMS_KEY } from '../config/keys';
 import { addFavoriteItemRoutine, deleteFavoriteItemRoutine } from '../routines';
-import { UNAUTHORIZED_RESPONSE } from '../../test/constants';
-import { StatusCodes } from 'http-status-codes';
-
-jest.spyOn(Cookies, 'get').mockReturnValue({ session: 'somesession' });
 
 const mockedNotifier = jest.fn();
 const { wrapper, queryClient, mutations } = setUpTest({
@@ -26,14 +20,14 @@ describe('Favorite Mutations', () => {
     nock.cleanAll();
   });
 
-  describe('useFavoriteItem', () => {
+  describe('useAddFavoriteItem', () => {
     const itemId = 'item-id';
     const route = `/${buildFavoriteItemRoute(itemId)}`;
-    const mutation = mutations.useFavoriteItem;
+    const mutation = mutations.useAddFavoriteItem;
 
     it(`Successfully add favorite item`, async () => {
       // set random data in cache
-      queryClient.setQueryData(FAVORITE_ITEMS_KEY, "mock");
+      queryClient.setQueryData(FAVORITE_ITEMS_KEY, 'mock');
       const endpoints = [
         {
           response: itemId,
@@ -61,96 +55,95 @@ describe('Favorite Mutations', () => {
     });
 
     it(`Unauthorized`, async () => {
-        const endpoints = [
-            {
-              response: UNAUTHORIZED_RESPONSE,
-              statusCode: StatusCodes.UNAUTHORIZED,
-              method: HttpMethod.POST,
-              route,
-            },
-          ];
-    
-          const mockedMutation = await mockMutation({
-            endpoints,
-            mutation,
-            wrapper,
-          });
-    
-          await act(async () => {
-            await mockedMutation.mutate(itemId);
-            await waitForMutation();
-          });
-    
-          expect(mockedNotifier).toHaveBeenCalledWith(
-            expect.objectContaining({
-              type: addFavoriteItemRoutine.FAILURE,
-            }),
-          );
-        });
+      const endpoints = [
+        {
+          response: UNAUTHORIZED_RESPONSE,
+          statusCode: StatusCodes.UNAUTHORIZED,
+          method: HttpMethod.POST,
+          route,
+        },
+      ];
+
+      const mockedMutation = await mockMutation({
+        endpoints,
+        mutation,
+        wrapper,
+      });
+
+      await act(async () => {
+        await mockedMutation.mutate(itemId);
+        await waitForMutation();
+      });
+
+      expect(mockedNotifier).toHaveBeenCalledWith(
+        expect.objectContaining({
+          type: addFavoriteItemRoutine.FAILURE,
+        }),
+      );
+    });
+  });
+
+  describe('useRemoveFavoriteItem', () => {
+    const itemId = 'item-id';
+    const route = `/${buildFavoriteItemRoute(itemId)}`;
+    const mutation = mutations.useRemoveFavoriteItem;
+
+    it('Delete item like', async () => {
+      queryClient.setQueryData(FAVORITE_ITEMS_KEY, itemId);
+
+      const endpoints = [
+        {
+          response: itemId,
+          method: HttpMethod.DELETE,
+          route,
+        },
+      ];
+
+      const mockedMutation = await mockMutation({
+        endpoints,
+        mutation,
+        wrapper,
+      });
+
+      await act(async () => {
+        await mockedMutation.mutate(itemId);
+        await waitForMutation();
+      });
+
+      expect(
+        queryClient.getQueryState(FAVORITE_ITEMS_KEY)?.isInvalidated,
+      ).toBeTruthy();
+      expect(mockedNotifier).toHaveBeenCalledWith({
+        type: deleteFavoriteItemRoutine.SUCCESS,
+      });
     });
 
-    describe('useDeleteItemLike', () => {
-        const itemId = 'item-id';
-        const route = `/${buildFavoriteItemRoute(itemId)}`;
-        const mutation = mutations.useUnfavoriteItem;
-    
-        it('Delete item like', async () => {
-          queryClient.setQueryData(FAVORITE_ITEMS_KEY, itemId);
-    
-    
-          const endpoints = [
-            {
-              response: itemId,
-              method: HttpMethod.DELETE,
-              route,
-            },
-          ];
-    
-          const mockedMutation = await mockMutation({
-            endpoints,
-            mutation,
-            wrapper,
-          });
-    
-          await act(async () => {
-            await mockedMutation.mutate(itemId);
-            await waitForMutation();
-          });
-    
-          expect(
-            queryClient.getQueryState(FAVORITE_ITEMS_KEY)?.isInvalidated,
-          ).toBeTruthy();
-          expect(mockedNotifier).toHaveBeenCalledWith({
-            type: deleteFavoriteItemRoutine.SUCCESS,
-          });
-        });
-    
-        it('Unauthorized to delete item like', async () => {
-          const endpoints = [
-            {
-              response: UNAUTHORIZED_RESPONSE,
-              statusCode: StatusCodes.UNAUTHORIZED,
-              method: HttpMethod.DELETE,
-              route,
-            },
-          ];
-    
-          const mockedMutation = await mockMutation({
-            endpoints,
-            mutation,
-            wrapper,
-          });
-    
-          await act(async () => {
-            await mockedMutation.mutate(itemId);
-            await waitForMutation();
-          });
-    
-          expect(mockedNotifier).toHaveBeenCalledWith(
-            expect.objectContaining({
-              type: deleteFavoriteItemRoutine.FAILURE,
-            }),
-          );
-        });
+    it('Unauthorized to delete item like', async () => {
+      const endpoints = [
+        {
+          response: UNAUTHORIZED_RESPONSE,
+          statusCode: StatusCodes.UNAUTHORIZED,
+          method: HttpMethod.DELETE,
+          route,
+        },
+      ];
+
+      const mockedMutation = await mockMutation({
+        endpoints,
+        mutation,
+        wrapper,
+      });
+
+      await act(async () => {
+        await mockedMutation.mutate(itemId);
+        await waitForMutation();
+      });
+
+      expect(mockedNotifier).toHaveBeenCalledWith(
+        expect.objectContaining({
+          type: deleteFavoriteItemRoutine.FAILURE,
+        }),
+      );
     });
+  });
 });
diff --git a/src/mutations/itemFavorite.ts b/src/mutations/itemFavorite.ts
index 7090539c1..119485737 100644
--- a/src/mutations/itemFavorite.ts
+++ b/src/mutations/itemFavorite.ts
@@ -4,17 +4,14 @@ import { UUID } from '@graasp/sdk';
 
 import * as Api from '../api';
 import { FAVORITE_ITEMS_KEY, MUTATION_KEYS } from '../config/keys';
-import {
-  addFavoriteItemRoutine,
-  deleteFavoriteItemRoutine
-} from '../routines';
+import { addFavoriteItemRoutine, deleteFavoriteItemRoutine } from '../routines';
 import { QueryClientConfig } from '../types';
 
 export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
   const { notifier } = queryConfig;
 
-  queryClient.setMutationDefaults(MUTATION_KEYS.FAVORITE_ITEM, {
-    mutationFn: (itemId) => Api.favoriteItem(itemId, queryConfig),
+  queryClient.setMutationDefaults(MUTATION_KEYS.ADD_FAVORITE_ITEM, {
+    mutationFn: (itemId) => Api.addFavoriteItem(itemId, queryConfig),
     onSuccess: () => {
       notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
     },
@@ -25,26 +22,29 @@ export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
       queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
     },
   });
-  const useFavoriteItem = () =>
-    useMutation<void, unknown, UUID>(MUTATION_KEYS.FAVORITE_ITEM);
+  const useAddFavoriteItem = () =>
+    useMutation<void, unknown, UUID>(MUTATION_KEYS.ADD_FAVORITE_ITEM);
 
-  queryClient.setMutationDefaults(MUTATION_KEYS.UNFAVORITE_ITEM, {
-    mutationFn: (itemId) => Api.unfavoriteItem(itemId, queryConfig),
+  queryClient.setMutationDefaults(MUTATION_KEYS.REMOVE_FAVORITE_ITEM, {
+    mutationFn: (itemId) => Api.removeFavoriteItem(itemId, queryConfig),
     onSuccess: () => {
       notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
     },
     onError: (error) => {
-      notifier?.({ type: deleteFavoriteItemRoutine.FAILURE, payload: { error } });
+      notifier?.({
+        type: deleteFavoriteItemRoutine.FAILURE,
+        payload: { error },
+      });
     },
     onSettled: () => {
       queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
     },
   });
-  const useUnfavoriteItem = () =>
-    useMutation<void, unknown, UUID>(MUTATION_KEYS.UNFAVORITE_ITEM);
+  const useRemoveFavoriteItem = () =>
+    useMutation<void, unknown, UUID>(MUTATION_KEYS.REMOVE_FAVORITE_ITEM);
 
   return {
-    useFavoriteItem,
-    useUnfavoriteItem
+    useAddFavoriteItem,
+    useRemoveFavoriteItem,
   };
 };
diff --git a/src/mutations/member.ts b/src/mutations/member.ts
index 60f6c657b..ce589a19e 100644
--- a/src/mutations/member.ts
+++ b/src/mutations/member.ts
@@ -18,11 +18,7 @@ import {
 } from '../routines';
 import { QueryClientConfig } from '../types';
 
-const {
-  DELETE_MEMBER,
-  EDIT_MEMBER,
-  UPLOAD_AVATAR,
-} = MUTATION_KEYS;
+const { DELETE_MEMBER, EDIT_MEMBER, UPLOAD_AVATAR } = MUTATION_KEYS;
 
 export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
   const { notifier } = queryConfig;
diff --git a/src/routines/index.ts b/src/routines/index.ts
index 6ac8f607b..9beb9f10c 100644
--- a/src/routines/index.ts
+++ b/src/routines/index.ts
@@ -7,7 +7,7 @@ export * from './itemFlag';
 export * from './chat';
 export * from './mentions';
 export * from './itemCategory';
-export * from './itemFavorite'
+export * from './itemFavorite';
 export * from './itemExport';
 export * from './itemLike';
 export * from './itemValidation';
diff --git a/src/routines/itemFavorite.ts b/src/routines/itemFavorite.ts
index 3f6e8104a..199aea9fb 100644
--- a/src/routines/itemFavorite.ts
+++ b/src/routines/itemFavorite.ts
@@ -1,4 +1,4 @@
-import createRoutine from "./utils";
+import createRoutine from './utils';
 
 export const addFavoriteItemRoutine = createRoutine('ADD_FAVORITE_ITEM');
-export const deleteFavoriteItemRoutine = createRoutine('DELETE_FAVORITE_ITEM');
\ No newline at end of file
+export const deleteFavoriteItemRoutine = createRoutine('DELETE_FAVORITE_ITEM');

From b4ce832a2690cc41f50222cb07d23bd2292e884f Mon Sep 17 00:00:00 2001
From: Morgan Ridel <mail@morganridel.fr>
Date: Tue, 20 Jun 2023 11:47:19 +0000
Subject: [PATCH 4/5] chore: refactor mutation to new formats

---
 src/api/itemFavorite.ts       |  6 +--
 src/config/keys.ts            |  2 -
 src/mutations/index.ts        |  2 +-
 src/mutations/itemFavorite.ts | 76 +++++++++++++++++++----------------
 4 files changed, 46 insertions(+), 40 deletions(-)

diff --git a/src/api/itemFavorite.ts b/src/api/itemFavorite.ts
index 4d3a8abba..509b02fcd 100644
--- a/src/api/itemFavorite.ts
+++ b/src/api/itemFavorite.ts
@@ -1,4 +1,4 @@
-import { Item, UUID } from '@graasp/sdk';
+import { Item, ItemFavorite, UUID } from '@graasp/sdk';
 
 import { QueryClientConfig } from '../types';
 import configureAxios, { verifyAuthentication } from './axios';
@@ -18,7 +18,7 @@ export const getFavoriteItems = async ({
 export const addFavoriteItem = async (
   id: UUID,
   { API_HOST }: QueryClientConfig,
-) =>
+): Promise<ItemFavorite> =>
   verifyAuthentication(() =>
     axios
       .post(`${API_HOST}/${buildFavoriteItemRoute(id)}`)
@@ -28,7 +28,7 @@ export const addFavoriteItem = async (
 export const removeFavoriteItem = async (
   id: UUID,
   { API_HOST }: QueryClientConfig,
-) =>
+): Promise<UUID> =>
   verifyAuthentication(() =>
     axios
       .delete(`${API_HOST}/${buildFavoriteItemRoute(id)}`)
diff --git a/src/config/keys.ts b/src/config/keys.ts
index b9c4aefdf..7883738c5 100644
--- a/src/config/keys.ts
+++ b/src/config/keys.ts
@@ -306,8 +306,6 @@ export const MUTATION_KEYS = {
   RECYCLE_ITEM: 'recycleItem',
   RECYCLE_ITEMS: 'recycleItems',
   RESTORE_ITEMS: 'restoreItems',
-  ADD_FAVORITE_ITEM: 'addFavoriteItem',
-  REMOVE_FAVORITE_ITEM: 'removeFavoriteItem',
   POST_ITEM_CATEGORY: 'postItemCategory',
   DELETE_ITEM_CATEGORY: 'deleteItemCategory',
   UPLOAD_ITEM_THUMBNAIL: 'uploadItemThumbnail',
diff --git a/src/mutations/index.ts b/src/mutations/index.ts
index 449fa8d59..d2497cfc0 100644
--- a/src/mutations/index.ts
+++ b/src/mutations/index.ts
@@ -33,7 +33,7 @@ const configureMutations = (
   ...chatMutations(queryClient, queryConfig),
   ...mentionMutations(queryClient, queryConfig),
   ...itemCategoryMutations(queryClient, queryConfig),
-  ...itemFavoriteMutations(queryClient, queryConfig),
+  ...itemFavoriteMutations(queryConfig),
   ...itemExportMutations(queryClient, queryConfig),
   ...itemLikeMutations(queryClient, queryConfig),
   ...itemValidationMutations(queryClient, queryConfig),
diff --git a/src/mutations/itemFavorite.ts b/src/mutations/itemFavorite.ts
index 119485737..154476c21 100644
--- a/src/mutations/itemFavorite.ts
+++ b/src/mutations/itemFavorite.ts
@@ -1,47 +1,55 @@
-import { QueryClient, useMutation } from 'react-query';
+import { useMutation, useQueryClient } from 'react-query';
 
 import { UUID } from '@graasp/sdk';
 
 import * as Api from '../api';
-import { FAVORITE_ITEMS_KEY, MUTATION_KEYS } from '../config/keys';
+import { FAVORITE_ITEMS_KEY } from '../config/keys';
 import { addFavoriteItemRoutine, deleteFavoriteItemRoutine } from '../routines';
 import { QueryClientConfig } from '../types';
 
-export default (queryClient: QueryClient, queryConfig: QueryClientConfig) => {
+export default (queryConfig: QueryClientConfig) => {
   const { notifier } = queryConfig;
 
-  queryClient.setMutationDefaults(MUTATION_KEYS.ADD_FAVORITE_ITEM, {
-    mutationFn: (itemId) => Api.addFavoriteItem(itemId, queryConfig),
-    onSuccess: () => {
-      notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
-    },
-    onError: (error) => {
-      notifier?.({ type: addFavoriteItemRoutine.FAILURE, payload: { error } });
-    },
-    onSettled: () => {
-      queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
-    },
-  });
-  const useAddFavoriteItem = () =>
-    useMutation<void, unknown, UUID>(MUTATION_KEYS.ADD_FAVORITE_ITEM);
+  const useAddFavoriteItem = () => {
+    const queryClient = useQueryClient();
+    return useMutation(
+      (itemId: UUID) =>
+        Api.addFavoriteItem(itemId, queryConfig),
+      {
+        onSuccess: () => {
+          notifier?.({ type: addFavoriteItemRoutine.SUCCESS });
+        },
+        onError: (error: Error) => {
+          notifier?.({ type: addFavoriteItemRoutine.FAILURE, payload: { error } });
+        },
+        onSettled: () => {
+          queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
+        },
+      },
+    );
+  };
 
-  queryClient.setMutationDefaults(MUTATION_KEYS.REMOVE_FAVORITE_ITEM, {
-    mutationFn: (itemId) => Api.removeFavoriteItem(itemId, queryConfig),
-    onSuccess: () => {
-      notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
-    },
-    onError: (error) => {
-      notifier?.({
-        type: deleteFavoriteItemRoutine.FAILURE,
-        payload: { error },
-      });
-    },
-    onSettled: () => {
-      queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
-    },
-  });
-  const useRemoveFavoriteItem = () =>
-    useMutation<void, unknown, UUID>(MUTATION_KEYS.REMOVE_FAVORITE_ITEM);
+  const useRemoveFavoriteItem = () => {
+    const queryClient = useQueryClient();
+    return useMutation(
+      (itemId: UUID) =>
+        Api.removeFavoriteItem(itemId, queryConfig),
+      {
+        onSuccess: () => {
+          notifier?.({ type: deleteFavoriteItemRoutine.SUCCESS });
+        },
+        onError: (error: Error) => {
+          notifier?.({
+            type: deleteFavoriteItemRoutine.FAILURE,
+            payload: { error },
+          });
+        },
+        onSettled: () => {
+          queryClient.invalidateQueries(FAVORITE_ITEMS_KEY);
+        },
+      },
+    );
+  };
 
   return {
     useAddFavoriteItem,

From a66037bb0fe5b4db898a734de0c35bcd179d31de Mon Sep 17 00:00:00 2001
From: Morgan Ridel <mail@morganridel.fr>
Date: Tue, 20 Jun 2023 13:34:26 +0000
Subject: [PATCH 5/5] chore: update sdk version

---
 package.json |    2 +-
 yarn.lock    | 1461 +++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 1277 insertions(+), 186 deletions(-)

diff --git a/package.json b/package.json
index c38292e8d..b75abac8e 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "Alexandre Chau"
   ],
   "dependencies": {
-    "@graasp/sdk": "github:graasp/graasp-sdk#refactor-favorite",
+    "@graasp/sdk": "1.0.0",
     "@graasp/translations": "1.13.0",
     "axios": "0.27.2",
     "crypto-js": "4.1.1",
diff --git a/yarn.lock b/yarn.lock
index 33870ddb3..ed00e7582 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -35,6 +35,1136 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-crypto/crc32@npm:3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/crc32@npm:3.0.0"
+  dependencies:
+    "@aws-crypto/util": ^3.0.0
+    "@aws-sdk/types": ^3.222.0
+    tslib: ^1.11.1
+  checksum: 9fdb3e837fc54119b017ea34fd0a6d71d2c88075d99e1e818a5158e0ad30ced67ddbcc423a11ceeef6cc465ab5ffd91830acab516470b48237ca7abd51be9642
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/crc32c@npm:3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/crc32c@npm:3.0.0"
+  dependencies:
+    "@aws-crypto/util": ^3.0.0
+    "@aws-sdk/types": ^3.222.0
+    tslib: ^1.11.1
+  checksum: 0a116b5d1c5b09a3dde65aab04a07b32f543e87b68f2d175081e3f4a1a17502343f223d691dd883ace1ddce65cd40093673e7c7415dcd99062202ba87ffb4038
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/ie11-detection@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/ie11-detection@npm:3.0.0"
+  dependencies:
+    tslib: ^1.11.1
+  checksum: 299b2ddd46eddac1f2d54d91386ceb37af81aef8a800669281c73d634ed17fd855dcfb8b3157f2879344b93a2666a6d602550eb84b71e4d7868100ad6da8f803
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/sha1-browser@npm:3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/sha1-browser@npm:3.0.0"
+  dependencies:
+    "@aws-crypto/ie11-detection": ^3.0.0
+    "@aws-crypto/supports-web-crypto": ^3.0.0
+    "@aws-crypto/util": ^3.0.0
+    "@aws-sdk/types": ^3.222.0
+    "@aws-sdk/util-locate-window": ^3.0.0
+    "@aws-sdk/util-utf8-browser": ^3.0.0
+    tslib: ^1.11.1
+  checksum: 78c379e105a0c4e7b2ed745dffd8f55054d7dde8b350b61de682bbc3cd081a50e2f87861954fa9cd53c7ea711ebca1ca0137b14cb36483efc971f60f573cf129
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/sha256-browser@npm:3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/sha256-browser@npm:3.0.0"
+  dependencies:
+    "@aws-crypto/ie11-detection": ^3.0.0
+    "@aws-crypto/sha256-js": ^3.0.0
+    "@aws-crypto/supports-web-crypto": ^3.0.0
+    "@aws-crypto/util": ^3.0.0
+    "@aws-sdk/types": ^3.222.0
+    "@aws-sdk/util-locate-window": ^3.0.0
+    "@aws-sdk/util-utf8-browser": ^3.0.0
+    tslib: ^1.11.1
+  checksum: ca89456bf508db2e08060a7f656460db97ac9a15b11e39d6fa7665e2b156508a1758695bff8e82d0a00178d6ac5c36f35eb4bcfac2e48621265224ca14a19bd2
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/sha256-js@npm:3.0.0, @aws-crypto/sha256-js@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/sha256-js@npm:3.0.0"
+  dependencies:
+    "@aws-crypto/util": ^3.0.0
+    "@aws-sdk/types": ^3.222.0
+    tslib: ^1.11.1
+  checksum: 644ded32ea310237811afae873d3c7320739cb6f6cc39dced9c94801379e68e5ee2cca0c34f0384793fa9e750a7e0a5e2468f95754bd08e6fd72ab833c8fe23c
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/supports-web-crypto@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0"
+  dependencies:
+    tslib: ^1.11.1
+  checksum: 35479a1558db9e9a521df6877a99f95670e972c602f2a0349303477e5d638a5baf569fb037c853710e382086e6fd77e8ed58d3fb9b49f6e1186a9d26ce7be006
+  languageName: node
+  linkType: hard
+
+"@aws-crypto/util@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "@aws-crypto/util@npm:3.0.0"
+  dependencies:
+    "@aws-sdk/types": ^3.222.0
+    "@aws-sdk/util-utf8-browser": ^3.0.0
+    tslib: ^1.11.1
+  checksum: d29d5545048721aae3d60b236708535059733019a105f8a64b4e4a8eab7cf8dde1546dc56bff7de20d36140a4d1f0f4693e639c5732a7059273a7b1e56354776
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/abort-controller@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/abort-controller@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: fb7d8205987c6edd7825035e3599ab5587b1b79a5e8df1e10fc66c8c64f33f9d9354fed6dd029073c262c750d661fc64e68ced40955101b930cf7636597ff690
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/chunked-blob-reader@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/chunked-blob-reader@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 4969fe05c6cea38d0a8dc3ec8e37cbd82a0a5b6f8c32ad6c7d02f0800bc3641e96356f47981c88b645b4dc2bdcb73d03d7ec67ac38d277dde8337b61688f815b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/client-s3@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/client-s3@npm:3.353.0"
+  dependencies:
+    "@aws-crypto/sha1-browser": 3.0.0
+    "@aws-crypto/sha256-browser": 3.0.0
+    "@aws-crypto/sha256-js": 3.0.0
+    "@aws-sdk/client-sts": 3.353.0
+    "@aws-sdk/config-resolver": 3.353.0
+    "@aws-sdk/credential-provider-node": 3.353.0
+    "@aws-sdk/eventstream-serde-browser": 3.347.0
+    "@aws-sdk/eventstream-serde-config-resolver": 3.347.0
+    "@aws-sdk/eventstream-serde-node": 3.347.0
+    "@aws-sdk/fetch-http-handler": 3.353.0
+    "@aws-sdk/hash-blob-browser": 3.353.0
+    "@aws-sdk/hash-node": 3.347.0
+    "@aws-sdk/hash-stream-node": 3.347.0
+    "@aws-sdk/invalid-dependency": 3.347.0
+    "@aws-sdk/md5-js": 3.347.0
+    "@aws-sdk/middleware-bucket-endpoint": 3.353.0
+    "@aws-sdk/middleware-content-length": 3.347.0
+    "@aws-sdk/middleware-endpoint": 3.347.0
+    "@aws-sdk/middleware-expect-continue": 3.347.0
+    "@aws-sdk/middleware-flexible-checksums": 3.347.0
+    "@aws-sdk/middleware-host-header": 3.347.0
+    "@aws-sdk/middleware-location-constraint": 3.347.0
+    "@aws-sdk/middleware-logger": 3.347.0
+    "@aws-sdk/middleware-recursion-detection": 3.347.0
+    "@aws-sdk/middleware-retry": 3.353.0
+    "@aws-sdk/middleware-sdk-s3": 3.347.0
+    "@aws-sdk/middleware-serde": 3.347.0
+    "@aws-sdk/middleware-signing": 3.353.0
+    "@aws-sdk/middleware-ssec": 3.347.0
+    "@aws-sdk/middleware-stack": 3.347.0
+    "@aws-sdk/middleware-user-agent": 3.352.0
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/node-http-handler": 3.350.0
+    "@aws-sdk/signature-v4-multi-region": 3.347.0
+    "@aws-sdk/smithy-client": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    "@aws-sdk/util-body-length-browser": 3.310.0
+    "@aws-sdk/util-body-length-node": 3.310.0
+    "@aws-sdk/util-defaults-mode-browser": 3.353.0
+    "@aws-sdk/util-defaults-mode-node": 3.353.0
+    "@aws-sdk/util-endpoints": 3.352.0
+    "@aws-sdk/util-retry": 3.347.0
+    "@aws-sdk/util-stream-browser": 3.353.0
+    "@aws-sdk/util-stream-node": 3.350.0
+    "@aws-sdk/util-user-agent-browser": 3.347.0
+    "@aws-sdk/util-user-agent-node": 3.353.0
+    "@aws-sdk/util-utf8": 3.310.0
+    "@aws-sdk/util-waiter": 3.347.0
+    "@aws-sdk/xml-builder": 3.310.0
+    "@smithy/protocol-http": ^1.0.1
+    "@smithy/types": ^1.0.0
+    fast-xml-parser: 4.2.4
+    tslib: ^2.5.0
+  checksum: f84ffcd9a3b2512b5dfb4d22ced8ce2b86a78b65afff43aa53d3ef196372376afbb479799db8472e6af41ee48d895ec9b874db0e85dfaf162c337138b0137b9a
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/client-sso-oidc@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/client-sso-oidc@npm:3.353.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": 3.0.0
+    "@aws-crypto/sha256-js": 3.0.0
+    "@aws-sdk/config-resolver": 3.353.0
+    "@aws-sdk/fetch-http-handler": 3.353.0
+    "@aws-sdk/hash-node": 3.347.0
+    "@aws-sdk/invalid-dependency": 3.347.0
+    "@aws-sdk/middleware-content-length": 3.347.0
+    "@aws-sdk/middleware-endpoint": 3.347.0
+    "@aws-sdk/middleware-host-header": 3.347.0
+    "@aws-sdk/middleware-logger": 3.347.0
+    "@aws-sdk/middleware-recursion-detection": 3.347.0
+    "@aws-sdk/middleware-retry": 3.353.0
+    "@aws-sdk/middleware-serde": 3.347.0
+    "@aws-sdk/middleware-stack": 3.347.0
+    "@aws-sdk/middleware-user-agent": 3.352.0
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/node-http-handler": 3.350.0
+    "@aws-sdk/smithy-client": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    "@aws-sdk/util-body-length-browser": 3.310.0
+    "@aws-sdk/util-body-length-node": 3.310.0
+    "@aws-sdk/util-defaults-mode-browser": 3.353.0
+    "@aws-sdk/util-defaults-mode-node": 3.353.0
+    "@aws-sdk/util-endpoints": 3.352.0
+    "@aws-sdk/util-retry": 3.347.0
+    "@aws-sdk/util-user-agent-browser": 3.347.0
+    "@aws-sdk/util-user-agent-node": 3.353.0
+    "@aws-sdk/util-utf8": 3.310.0
+    "@smithy/protocol-http": ^1.0.1
+    "@smithy/types": ^1.0.0
+    tslib: ^2.5.0
+  checksum: abc97b48cbeb1800fa97499221ca7bbf14f1f651479d5a6d4ecbc748b63ce6042f91043347b0c3639f02bf9d1dfd2949034ec91984fb4a6d9bbad3cbb51ccdee
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/client-sso@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/client-sso@npm:3.353.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": 3.0.0
+    "@aws-crypto/sha256-js": 3.0.0
+    "@aws-sdk/config-resolver": 3.353.0
+    "@aws-sdk/fetch-http-handler": 3.353.0
+    "@aws-sdk/hash-node": 3.347.0
+    "@aws-sdk/invalid-dependency": 3.347.0
+    "@aws-sdk/middleware-content-length": 3.347.0
+    "@aws-sdk/middleware-endpoint": 3.347.0
+    "@aws-sdk/middleware-host-header": 3.347.0
+    "@aws-sdk/middleware-logger": 3.347.0
+    "@aws-sdk/middleware-recursion-detection": 3.347.0
+    "@aws-sdk/middleware-retry": 3.353.0
+    "@aws-sdk/middleware-serde": 3.347.0
+    "@aws-sdk/middleware-stack": 3.347.0
+    "@aws-sdk/middleware-user-agent": 3.352.0
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/node-http-handler": 3.350.0
+    "@aws-sdk/smithy-client": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    "@aws-sdk/util-body-length-browser": 3.310.0
+    "@aws-sdk/util-body-length-node": 3.310.0
+    "@aws-sdk/util-defaults-mode-browser": 3.353.0
+    "@aws-sdk/util-defaults-mode-node": 3.353.0
+    "@aws-sdk/util-endpoints": 3.352.0
+    "@aws-sdk/util-retry": 3.347.0
+    "@aws-sdk/util-user-agent-browser": 3.347.0
+    "@aws-sdk/util-user-agent-node": 3.353.0
+    "@aws-sdk/util-utf8": 3.310.0
+    "@smithy/protocol-http": ^1.0.1
+    "@smithy/types": ^1.0.0
+    tslib: ^2.5.0
+  checksum: 9ea9dab44ea8ccfda02bd4a38e61cb49bccee7b111fee48316a71808120c213f05d09cd96550fe7ce92c07787618c47949404368b9753da3634b86917c82b271
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/client-sts@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/client-sts@npm:3.353.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": 3.0.0
+    "@aws-crypto/sha256-js": 3.0.0
+    "@aws-sdk/config-resolver": 3.353.0
+    "@aws-sdk/credential-provider-node": 3.353.0
+    "@aws-sdk/fetch-http-handler": 3.353.0
+    "@aws-sdk/hash-node": 3.347.0
+    "@aws-sdk/invalid-dependency": 3.347.0
+    "@aws-sdk/middleware-content-length": 3.347.0
+    "@aws-sdk/middleware-endpoint": 3.347.0
+    "@aws-sdk/middleware-host-header": 3.347.0
+    "@aws-sdk/middleware-logger": 3.347.0
+    "@aws-sdk/middleware-recursion-detection": 3.347.0
+    "@aws-sdk/middleware-retry": 3.353.0
+    "@aws-sdk/middleware-sdk-sts": 3.353.0
+    "@aws-sdk/middleware-serde": 3.347.0
+    "@aws-sdk/middleware-signing": 3.353.0
+    "@aws-sdk/middleware-stack": 3.347.0
+    "@aws-sdk/middleware-user-agent": 3.352.0
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/node-http-handler": 3.350.0
+    "@aws-sdk/smithy-client": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    "@aws-sdk/util-body-length-browser": 3.310.0
+    "@aws-sdk/util-body-length-node": 3.310.0
+    "@aws-sdk/util-defaults-mode-browser": 3.353.0
+    "@aws-sdk/util-defaults-mode-node": 3.353.0
+    "@aws-sdk/util-endpoints": 3.352.0
+    "@aws-sdk/util-retry": 3.347.0
+    "@aws-sdk/util-user-agent-browser": 3.347.0
+    "@aws-sdk/util-user-agent-node": 3.353.0
+    "@aws-sdk/util-utf8": 3.310.0
+    "@smithy/protocol-http": ^1.0.1
+    "@smithy/types": ^1.0.0
+    fast-xml-parser: 4.2.4
+    tslib: ^2.5.0
+  checksum: 118dd8ad669dc69edf9b1992245273c09d77e05f91cf0b5591092365037567c197d045fca831a29114a63945b00b8658108631da3d34f2f6b3b6337058c0e1c3
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/config-resolver@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/config-resolver@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-config-provider": 3.310.0
+    "@aws-sdk/util-middleware": 3.347.0
+    tslib: ^2.5.0
+  checksum: 5f3bd83c60cc09595c4651b778e9c70ba757ef52d226067a7082364422f962b1f3e1f0e7c709ca94102078a0e506826991fcc5a70ddb679d870f6920d090b916
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-env@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-env@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 546a325727b45245e3c33e0b0e8bd435fd3c33bcc245fa98d77229f6a46ab8155808622c522fdd08aadea4cf03b10742f8d3c4c811f51f5bc18b5aed1517e41b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-imds@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-imds@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    tslib: ^2.5.0
+  checksum: 9dfd0533306d2f6ba6a47c8353c059f9e5b06e68da822c0e472ac1b77ac13e80f568ae6434b2ad44daed8430e90a1165981cc78a92aa54072432c11a5db8026a
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-ini@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-ini@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/credential-provider-env": 3.353.0
+    "@aws-sdk/credential-provider-imds": 3.353.0
+    "@aws-sdk/credential-provider-process": 3.353.0
+    "@aws-sdk/credential-provider-sso": 3.353.0
+    "@aws-sdk/credential-provider-web-identity": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: e67600611f4117f82e0e5cb98aafe808746f069d69ea563facdc870346b2662a374af5bcaa8b93b6601934deb4c6d82c3c82a915a883846b4e885651bede3250
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-node@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-node@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/credential-provider-env": 3.353.0
+    "@aws-sdk/credential-provider-imds": 3.353.0
+    "@aws-sdk/credential-provider-ini": 3.353.0
+    "@aws-sdk/credential-provider-process": 3.353.0
+    "@aws-sdk/credential-provider-sso": 3.353.0
+    "@aws-sdk/credential-provider-web-identity": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: b0812045c4e3c8e5bbbc966dd89a3d64f9d346e2d44ae46c04b0aac280485c1b15d19cdaaab5a5913ea9058dcff5319ca26239b3a6a080cae16255fc4c564c1b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-process@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-process@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 0d9f8d33c9ee677efad9912ee31043d8b0870c22348a5d862c101a1064d596750ef77241e97ac224f56588e8b22579c0cf987a20a8ab9a592df9a8a22b826d01
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-sso@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-sso@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/client-sso": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/token-providers": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 12b5b7144eff12e2cb27f011bcdcb9edfcebca373677c2e6c72ffe696e7f6a1eef2949868033365e6e0ea9f01617422f7264c384ff6af1a304a6bd911b9aeab0
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/credential-provider-web-identity@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/credential-provider-web-identity@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 6a5fc1f4da9782b2be5eed02fa442fe3bcc7fe35bb67d93b26d3d4210fb88e85530cae14092b438181f063954d1e3a6c37ee5c6beab9c631c60bd3d10ba3c9ab
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/eventstream-codec@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/eventstream-codec@npm:3.347.0"
+  dependencies:
+    "@aws-crypto/crc32": 3.0.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-hex-encoding": 3.310.0
+    tslib: ^2.5.0
+  checksum: 5279095f6c3c90ff301ae378e77ef3a74c9bec660f59be9aa1f6f2917f37529170d8a84d7d68763a8046507d43818a25a757f166744a4fbde398af398ebf7d14
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/eventstream-serde-browser@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/eventstream-serde-browser@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/eventstream-serde-universal": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: e1a24dfcd18ea0ea8f04b6d2ecb5589d586131e0b73c6f2f896d73d5829266ee00401c3c7d04c662645ebbe654028c9ba62ff267ab0f0cca124283f179f0471f
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/eventstream-serde-config-resolver@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/eventstream-serde-config-resolver@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: d40574c294ae23983c5957b64508625985ddfb1198835e9fe295a67947f81c26227d930189043751e04b0d3201976009e9209e8a23c7de38c63231955eec7342
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/eventstream-serde-node@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/eventstream-serde-node@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/eventstream-serde-universal": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 223445d11155c88762fd5ede06845b8b68045a36ca644bceee76dcf48e66e3788c79edf6e68cf35233a3a81cc32ca13342da62163fe2fad0ce0dee05b7822102
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/eventstream-serde-universal@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/eventstream-serde-universal@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/eventstream-codec": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 67df71b0dc77c5944b797e9df4277a236ee77634cd3b58b6afc5d17b327dfbcefbdfdb9e8a8df45a7a34a696eb36e0943c195116f3d78b1fa362f3279c42956b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/fetch-http-handler@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/fetch-http-handler@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/querystring-builder": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    tslib: ^2.5.0
+  checksum: edcbe84e5d5802f4620c0b31b8559a4134c290349da1237619a700b1651df923ec1bcc6d0487a81d4fb19d1f47cdd6311f2c3e0ce5a8ecc89872eea100970d71
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/hash-blob-browser@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/hash-blob-browser@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/chunked-blob-reader": 3.310.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 9621e8410e0bfff793f69147f655436d078bb121a33baf073814e090760e51be751d196a88d2e0e047bc5623f67e334457725270fada5e1173eea24c0eede50e
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/hash-node@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/hash-node@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-buffer-from": 3.310.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: 5e33eb7b3adb291d661a105306f737eca932330b1b0395d6825f216c97a3eca47a19bb708a4dc10d68c8d80d78073886ba2714cd8cdeb27c6099da8760b37ead
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/hash-stream-node@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/hash-stream-node@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: c379b66cbbaadb4cc3807aa06941c987952fa09af50c3d59b9cc44670a64d049c991d6b15208e9f4928438e6ccb7145ef2cad4b9d5e1a916a4a0fc8b18be836d
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/invalid-dependency@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/invalid-dependency@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 78abc0831478c0bfa83df7bbbdf346d42a9b0f379f2392bba11576198e1b6a3a3b24e95aea46eabe7eb6233ab20ab1e5dc2c6f7c48d2b296d1590bd9370afcae
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/is-array-buffer@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/is-array-buffer@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: ddd1536ad16e29186fb5055bc279cfe9790b7c32552e1ee21e31d4e410e1df297b06c94c6117f854ec368d29e60a231dd8cc77e5b604a6260e7602876fd047f8
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/md5-js@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/md5-js@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: ee1d07546d1d6a6f5dbb30567bfdac658523569a75bd46cf7d2a0f4ab2360a6877f8377e081484733e7a38979f84531f2a8d3a2c5edd8acff6f50390bcf6efa6
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-bucket-endpoint@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-arn-parser": 3.310.0
+    "@aws-sdk/util-config-provider": 3.310.0
+    tslib: ^2.5.0
+  checksum: 61751b38230159b59e3e400570a346c3e2da9c339ba8769174ca2a5f3185bcd6970703bee1e07babb5e359910c1648e442081f1c0883e293b993a7b874dd33b8
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-content-length@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-content-length@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: b22e25918d41e8bf382dda43f1b9558a0b06a5db9c953e0ea492d56be2f83a444e69b96c9a23fb6505e6955ff03727501f933ed7d936a5d9cf7655ab7d42d092
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-endpoint@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-endpoint@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/middleware-serde": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/url-parser": 3.347.0
+    "@aws-sdk/util-middleware": 3.347.0
+    tslib: ^2.5.0
+  checksum: 0c6b0c5adb1bf3035f39fd3df3aa93fcd7e5d67c0f049a178ddaaa548896c23e0b7571686579b49f69fd82e3a74bd939d8386986a66b53e900fde9a24f0c35f7
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-expect-continue@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-expect-continue@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 6da6dfe3ae754317f053860cdcc3c0e730800c9cde97248d6b42be0376397a6f61440739dbb576569c65d31a3cdda0a1667d64982686cc855187207458af5ade
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-flexible-checksums@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.347.0"
+  dependencies:
+    "@aws-crypto/crc32": 3.0.0
+    "@aws-crypto/crc32c": 3.0.0
+    "@aws-sdk/is-array-buffer": 3.310.0
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: e4d7522c1445b569d5eb3226874377bb109bcf64df16592123eb5051fc63345f0491d4b0f58678a607c8c5e5e02bd87b5b39b46a3f9c17d623fa7b53d42d118b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-host-header@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-host-header@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 7aaf9ca270fa90bf6c99f24dbbc2a9ad36c663a605df548630e46ac50783402fcc0b49784d60cb9367697be78835ebcf0592218973d6a176e6b15f12a1f7ae70
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-location-constraint@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-location-constraint@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 0c362bdf6e7f6e3f041ed78ab8e4afdaca1b3cbc093b611e83374814512b3a224eb9c677ab75ca1f46e8be8bd9eee72f152ed8d2ae88638250125b7b4be383df
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-logger@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-logger@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: dd88e3b525ee46cef3324925003d9690e48900397e27fa6c6bbdfcaae1e56bfb9bd4115d0aea30a09c60ae47757eb089358e3049bd4090726e52415a4d945c62
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-recursion-detection@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-recursion-detection@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 380f79b18c8fa70f7d8e7f31fbe4c3d43336043d7abb6b577945349d69ddda487d50f61a9de2b7305e0149ad896f5c9605d27da9cd8011d1da0cd82225659eaf
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-retry@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/middleware-retry@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/service-error-classification": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-middleware": 3.347.0
+    "@aws-sdk/util-retry": 3.347.0
+    tslib: ^2.5.0
+    uuid: ^8.3.2
+  checksum: 25e9ec67ab1f35c8811a4d1c7f42e87f1ee9e804dffa49e8a35ed00a5f7883fae1d70c62b6b2bc7410977a9a68c894a1e02546c048c2cc81ab85535a376e5615
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-sdk-s3@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-sdk-s3@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-arn-parser": 3.310.0
+    tslib: ^2.5.0
+  checksum: b1110352d5f794537931f820a429d86f5d6891be9461cd16cbbcfdea7f8666d9f953ef3e0a939fda7961b8d5554fa819159cfe88f36307ffd4fdd396b341f420
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-sdk-sts@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/middleware-sdk-sts@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/middleware-signing": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 78a8c52a30b634c5965293fd7d0bdee36a519d84d3ffbe7dc43e70925ebf622495f95ad7947aa0871d3c6b19b744bbf2f40b737dd83b502be6d32ca85bbdc40d
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-serde@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-serde@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: c7b903f7656e7eefcac4772a475b669d46bdc42cbcb21d471fcbf6bf125b0cc701fe634a4f80876aa4264eb3febb5a73a9745b1fd747a0fac5455214b55264dd
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-signing@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/middleware-signing@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/signature-v4": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-middleware": 3.347.0
+    tslib: ^2.5.0
+  checksum: 9c5a3a844e8f7be7188079239a30208193939bd73a2dcb1eec10f78fa331290ea649f05468057fec208a8449338da1a26ca3015c2f3392033a4d5e4c9b0c1692
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-ssec@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-ssec@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 638daeb4569e40f2e3279289aa78909683c648f085b0de27bd58d7a461ef4723dab1794150eaad6ef787dad15f977a8871e89a0e5c4581a7ae01dda9e36a4abd
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-stack@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/middleware-stack@npm:3.347.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: e2321ed82fb71ea5a0cdc18e88be3c5ae58e36ddfd0786258f791bed2db72716fd36d00c33417046a52f1bc0f3f611178cf790b52fc9a3e7278027601c49bad2
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/middleware-user-agent@npm:3.352.0":
+  version: 3.352.0
+  resolution: "@aws-sdk/middleware-user-agent@npm:3.352.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-endpoints": 3.352.0
+    tslib: ^2.5.0
+  checksum: 52fc810a7529681cbcbebf8ef0ecaa0c37964a426c5f1b9e8f75dfabdc235466d8a2783fde6068be4300772029c1cea86562eb9e00ed18630d15c896ad4a5b56
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/node-config-provider@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/node-config-provider@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 3864160125dd23f98a8b2cd971e8df2f931a8adbff43a9ecccf1e5b27a941d3f30efde8269660ddec57eafe2df2d1221a8eef5e31145e588996d07eaefe3b17f
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/node-http-handler@npm:3.350.0":
+  version: 3.350.0
+  resolution: "@aws-sdk/node-http-handler@npm:3.350.0"
+  dependencies:
+    "@aws-sdk/abort-controller": 3.347.0
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/querystring-builder": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 142299a3162b76ccd96d1e7e7dd4e0b5d0de6ff0f03af226042a0df0ca8e6a9752fa619bb75eff3508512b1e582b1bea257a985e9046444ec3a7a913def4479b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/property-provider@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/property-provider@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 18e25fdf606bdbb980f16ba385b61e9073b821a66c094c66d071c2f8c05d86f8d6ac3da264bb85cdaedb1e246780988d2abd6122891999786ed8226283f386cd
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/protocol-http@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/protocol-http@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: b783ec09ed684e747628fb4689df980e1d6e98ed65e25769c7102af8625b6e085498d7a6572dea3f47a1477ea7eb062e9f5508bd923c56a63a41916ad030a909
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/querystring-builder@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/querystring-builder@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-uri-escape": 3.310.0
+    tslib: ^2.5.0
+  checksum: a4f6f9c9a340107de37cd3e206d31c57f40f91de14aece87daac229746e57077a8440f126389d200f6f6f4af7507e5663a3cc7d67443e750b947d6645ba644ac
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/querystring-parser@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/querystring-parser@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: a27075fc174ca7d0487851107b590f651bf4396f872a2015af8d4967d11610000919eb99cebd679989219ef59127b70093d03fc07cf4ce3e5295d6848ed213dd
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/service-error-classification@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/service-error-classification@npm:3.347.0"
+  checksum: 5404b520a41ddddf54f48f391b27c551232358ed059ba8f02c818b57d7cc5b96156e3a4466f08435fca181ba645e97ad487d771b30dd13024ca0c2e3fd06cfaa
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/shared-ini-file-loader@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/shared-ini-file-loader@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 5dd8e322733f31b284215e187b54c20640689c2c4b459854436f393b82e485bd8273bcaea527f77bc3ae9c7ff3956913a4b5a355e30d00dfa21f4b9b177c3a89
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/signature-v4-multi-region@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/signature-v4-multi-region@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/protocol-http": 3.347.0
+    "@aws-sdk/signature-v4": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  peerDependencies:
+    "@aws-sdk/signature-v4-crt": ^3.118.0
+  peerDependenciesMeta:
+    "@aws-sdk/signature-v4-crt":
+      optional: true
+  checksum: e1dfca095f7b69f25cc7311476d49b7c3c0531710a8f34568624ee143fb88ba7d62c4542fd4bd450e1a4540b10ec008634ddda0f36595f00f96f67be2932551f
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/signature-v4@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/signature-v4@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/eventstream-codec": 3.347.0
+    "@aws-sdk/is-array-buffer": 3.310.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-hex-encoding": 3.310.0
+    "@aws-sdk/util-middleware": 3.347.0
+    "@aws-sdk/util-uri-escape": 3.310.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: ec372f09661a876b2dcec3031c5b2c7ddd64cf39780afe66979e1c2c116c044e31e1c174ba99d5b94c7e62c0c27696f859fa592b01ff9f7f3400ec44ef91e0d3
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/smithy-client@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/smithy-client@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/middleware-stack": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 90dcc3bd689de075bc718e6e5ea2169bf3a0705525dca62dfe8169ab48919000db1703ebd1decd12fc6439428a9240e252ef3c4ecd361ef0a7cfa5a26902fb24
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/token-providers@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/token-providers@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/client-sso-oidc": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/shared-ini-file-loader": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 8051329e1685ba8a14093734ebf341901667e81733b2d822e879cadc1db9ee5e4f9ab68282c341bc489adf125e67cedec4bd34e6f383c5e04d0a2e20602483ee
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/types@npm:3.347.0, @aws-sdk/types@npm:^3.222.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/types@npm:3.347.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 799b053d3651f1754e2925b671fe890047d0ff1af69d22b6826d8e74edefcd558c7c7a911d48eaf5930032bcf291dbdbb6dd2d2f0c596bbe52100941aa349221
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/url-parser@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/url-parser@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/querystring-parser": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: d1dc99173f00cfce7709a259afc54c4e5792e757f4515bdf71dd8adc22dbb1d9b9e4be1b1056a80055e42272d3658f5ac511fb4d436826ba7d425a208f287e08
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-arn-parser@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-arn-parser@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: faac1e10f8bb6c2fe5fee82bcb7ce56c2b37ae9ffdb2b78b0746a7a06005eaa5ea747a0a10eaf490c1c4907ecc327e1c94a600e26a069e023e54b8d63c031e96
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-base64@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-base64@npm:3.310.0"
+  dependencies:
+    "@aws-sdk/util-buffer-from": 3.310.0
+    tslib: ^2.5.0
+  checksum: 3c9f7c818401fe8332d2ce438c0660cc9be7db9a5eef68d7fafa30ddcc44b0af3ba9ea58092f0e2b2537a18ec0942ce3c8f12090d3e3b9568b6a94a0713e9de7
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-body-length-browser@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-body-length-browser@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: c26136521ccbb59ba83ff29d6e52cb0e4b443b68e830c9dab578556539973573e6892093e5dea39101b1517c28b5d53c80ee38b9a01f9fa9fcd75f3aa5689857
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-body-length-node@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-body-length-node@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 202417ece7078f09f63c4119cb3ab5f321688ea893125f7d97985e8bf7fc61419d8d990f870d9ead3281dc51334975196ef98c50592eca1f9785472bd39b870d
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-buffer-from@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-buffer-from@npm:3.310.0"
+  dependencies:
+    "@aws-sdk/is-array-buffer": 3.310.0
+    tslib: ^2.5.0
+  checksum: 9c3bd9c0664a0cbb5270eb285a662274bb9c46ae0d79e0275a85e74659a4b1f094bab900994780fd70dd0152dc6d2d33a8bc681d87f3911fa48eae9f6c3558d6
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-config-provider@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-config-provider@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 958efc58ee492111ad746fe6224b25286da415f8aca1197c742bca063672b858d437d2d6b4df5f90ba770e1af9339b3fb1ffa9cc87f2fa993a7177057eb22caf
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-defaults-mode-browser@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/util-defaults-mode-browser@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    bowser: ^2.11.0
+    tslib: ^2.5.0
+  checksum: 998e7337bf153bf91637d4b4dbba2d7581b973fd095b097a317befa31e52224ece7a58c9de36f77e95bb24c9a509e60a39aa6d9d76d1ab085e81f01c1547cb05
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-defaults-mode-node@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/util-defaults-mode-node@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/config-resolver": 3.353.0
+    "@aws-sdk/credential-provider-imds": 3.353.0
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/property-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: db44aac5be4e35cd64c0fd68c75fcb3ea78120aeccbcaccb1bf104221b730154ac500c82674b58584f2874d3f29ed3af57ad5e1297f52cbd591f10f1d8621fe9
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-endpoints@npm:3.352.0":
+  version: 3.352.0
+  resolution: "@aws-sdk/util-endpoints@npm:3.352.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 105dd20260bb40e5b0b83398397d5fef9d8990fe8c98dc91bbb5c8431f748faffba4ee0e7eb418fd684c05b2383f161b2db6a854905704af3f98111e32fbe57c
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-hex-encoding@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-hex-encoding@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 97b8d7e0e406189cdbd4fccb0a497dd247a22d54b18caf5a64a63d19d2535b95a64ee79ecf81b13f741bda1d565eb11448d4fd39617e4b86fc8626b05485d98c
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-locate-window@npm:^3.0.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-locate-window@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: d552ce5f0f836ecb13d7920ae650552c56706f26a5e8abf894ba471e18775a3791869bda95269153735bac9d211efc3ba78ea01c34428c3fed4318ac693a08bc
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-middleware@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/util-middleware@npm:3.347.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 96b9233a190c0575caac2b44f17a64078423221c300b48744ae8b5954856a0caaeb2c6ab3fa2ce280cb766f64532cb87dcf3051720cb2a2107e57910d57d083c
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-retry@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/util-retry@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/service-error-classification": 3.347.0
+    tslib: ^2.5.0
+  checksum: d8d016e00f2519e1282f696322f8b3be6f8266f51dda9f7666f25fdac4cd28e569b88a855499f766aaed47551d323decd2e385940912d6b3b861cc11549babb8
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-stream-browser@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/util-stream-browser@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/fetch-http-handler": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-base64": 3.310.0
+    "@aws-sdk/util-hex-encoding": 3.310.0
+    "@aws-sdk/util-utf8": 3.310.0
+    tslib: ^2.5.0
+  checksum: 6a1ea9246d11c3b47cf1064a3c88fae9ccdbb92eea43f041cfd029af7b5214518e7e0af9d15bb050a052809793d077cbcf07ae265b7c2fac634cec5a9740530c
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-stream-node@npm:3.350.0":
+  version: 3.350.0
+  resolution: "@aws-sdk/util-stream-node@npm:3.350.0"
+  dependencies:
+    "@aws-sdk/node-http-handler": 3.350.0
+    "@aws-sdk/types": 3.347.0
+    "@aws-sdk/util-buffer-from": 3.310.0
+    tslib: ^2.5.0
+  checksum: ac437783b140d9cc6772dae422f616f87989604314f5fad35817f0cc7c22c29f02229c3c9b7c87a2c62509d49be0dd42bacf30b36e6e060d786ba36fc8aa3e27
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-uri-escape@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-uri-escape@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 614c0a43b238b7371b6655a5961e21c57b708de3e1ce3138bd56284bedc48888e5c7d2a6965544108c3334fcdc45e9ddba86b2470c8e6901559ad7be8e21d418
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-user-agent-browser@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/util-user-agent-browser@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/types": 3.347.0
+    bowser: ^2.11.0
+    tslib: ^2.5.0
+  checksum: 64382e5b728152c004e127321ba88a43acf7a33d5a8ae16510e93bf21d0adbf1c53dd8ce96ad2c8276451ffdf895c990e4982f8f3cb96af0d2f16e0fa97c6646
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-user-agent-node@npm:3.353.0":
+  version: 3.353.0
+  resolution: "@aws-sdk/util-user-agent-node@npm:3.353.0"
+  dependencies:
+    "@aws-sdk/node-config-provider": 3.353.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  peerDependencies:
+    aws-crt: ">=1.0.0"
+  peerDependenciesMeta:
+    aws-crt:
+      optional: true
+  checksum: b3b1334e6b9aaecd99f69a05d434551f5e5559ff25535c8d606d6f6d9f83993f7aa1a59e96df114ba4fd142ca7f27feb9960f7b5bf6495dbc3b9cccddb5a2b6b
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-utf8-browser@npm:^3.0.0":
+  version: 3.259.0
+  resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0"
+  dependencies:
+    tslib: ^2.3.1
+  checksum: b6a1e580da1c9b62c749814182a7649a748ca4253edb4063aa521df97d25b76eae3359eb1680b86f71aac668e05cc05c514379bca39ebf4ba998ae4348412da8
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-utf8@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/util-utf8@npm:3.310.0"
+  dependencies:
+    "@aws-sdk/util-buffer-from": 3.310.0
+    tslib: ^2.5.0
+  checksum: 4045e79b8e3593e12233b359ba77d1b4c162fd9fcb4ab3b58b711c41b725552306dd91402b8d57ce5be080c76309f046a7a0c4ff704d12f9ba71e3b25b810086
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/util-waiter@npm:3.347.0":
+  version: 3.347.0
+  resolution: "@aws-sdk/util-waiter@npm:3.347.0"
+  dependencies:
+    "@aws-sdk/abort-controller": 3.347.0
+    "@aws-sdk/types": 3.347.0
+    tslib: ^2.5.0
+  checksum: 44d7553e0b82a596233d707218b55d2e4f911b0983b0c5fd751c7390c3945ad1cfc9009bd9db8d8a5107b2c28386ff4d9a72e1688e91324c99165777e8515480
+  languageName: node
+  linkType: hard
+
+"@aws-sdk/xml-builder@npm:3.310.0":
+  version: 3.310.0
+  resolution: "@aws-sdk/xml-builder@npm:3.310.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: fc17fd8f68470702d947948ada46097bdddecafdc68fa57bf584320e92748e8ef0372a51999d3ab7902ba4f62c2dbfbdec2dba1180fca19bb5127bad1ef0e48b
+  languageName: node
+  linkType: hard
+
 "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.5.5, @babel/code-frame@npm:^7.8.3":
   version: 7.18.6
   resolution: "@babel/code-frame@npm:7.18.6"
@@ -1837,6 +2967,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@babel/runtime@npm:^7.21.0":
+  version: 7.22.5
+  resolution: "@babel/runtime@npm:7.22.5"
+  dependencies:
+    regenerator-runtime: ^0.13.11
+  checksum: 12a50b7de2531beef38840d17af50c55a094253697600cee255311222390c68eed704829308d4fd305e1b3dfbce113272e428e9d9d45b1730e0fede997eaceb1
+  languageName: node
+  linkType: hard
+
 "@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3":
   version: 7.20.7
   resolution: "@babel/template@npm:7.20.7"
@@ -2415,10 +3554,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@fastify/error@npm:^3.0.0":
-  version: 3.2.0
-  resolution: "@fastify/error@npm:3.2.0"
-  checksum: e538ef76fd2dedd0584691e0c891997321a2050092b11089a70090f5a0edab0dc8ab069747aa6025782280824e2348548e051c8e77558baec699bd44e581e187
+"@fastify/error@npm:^3.2.0":
+  version: 3.2.1
+  resolution: "@fastify/error@npm:3.2.1"
+  checksum: b36a9195c817aad2296c194bab26b4a41ee8a34d8c2d14722004e0e8ec2cec20d183bafe2c619a62c3cdec8007f7b4f67f1730d0e07454bef38c5a81624e23f1
   languageName: node
   linkType: hard
 
@@ -2431,16 +3570,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@fastify/secure-session@npm:5.3.0":
-  version: 5.3.0
-  resolution: "@fastify/secure-session@npm:5.3.0"
+"@fastify/secure-session@npm:6.1.0":
+  version: 6.1.0
+  resolution: "@fastify/secure-session@npm:6.1.0"
   dependencies:
     "@fastify/cookie": ^8.0.0
     fastify-plugin: ^4.0.0
-    sodium-native: ^3.0.0
+    sodium-native: ^4.0.0
   bin:
     secure-session: genkey.js
-  checksum: 584d9321f26ade63b8913758f65693b14400c9babfe23e26ded715e4b97ee26e01bf49df2562630b35f588beccdaff38f016a2c66c0e1357708588aa93a058d0
+  checksum: 05547a9d38f7f4dca4ce3551bbd8623a106c406872876363d3b3bc5eaa405e09ce8ec815155cbe9d455c4a7f19b115f143251594a35d529b7e45da8b78a8e7ba
   languageName: node
   linkType: hard
 
@@ -2479,7 +3618,7 @@ __metadata:
     "@babel/preset-typescript": 7.21.5
     "@commitlint/cli": 17.6.1
     "@commitlint/config-conventional": 17.6.1
-    "@graasp/sdk": "github:graasp/graasp-sdk#refactor-favorite"
+    "@graasp/sdk": 1.0.0
     "@graasp/translations": 1.13.0
     "@testing-library/jest-dom": 5.16.5
     "@testing-library/react": 14.0.0
@@ -2535,22 +3674,22 @@ __metadata:
   languageName: unknown
   linkType: soft
 
-"@graasp/sdk@github:graasp/graasp-sdk#refactor-favorite":
-  version: 1.0.0-rc1
-  resolution: "@graasp/sdk@https://github.com/graasp/graasp-sdk.git#commit=d98d8976d9301d3732c993b76ac417e30cfeccf0"
+"@graasp/sdk@npm:1.0.0":
+  version: 1.0.0
+  resolution: "@graasp/sdk@npm:1.0.0"
   dependencies:
-    "@fastify/secure-session": 5.3.0
+    "@aws-sdk/client-s3": 3.353.0
+    "@fastify/secure-session": 6.1.0
     "@graasp/etherpad-api": 2.1.1
-    aws-sdk: 2.1370.0
-    fastify: 4.17.0
+    fastify: 4.18.0
     fluent-json-schema: 4.1.0
     immutable: 4.3.0
     js-cookie: 3.0.5
-    qs: 6.11.1
-    typeorm: 0.3.15
+    qs: 6.11.2
+    typeorm: 0.3.16
     uuid: 9.0.0
     validator: 13.9.0
-  checksum: 7286ca0f9e10a785bb756ce1bc5dc75d74fac3b0b0285da2e2a5b220a041e61944788bfc17e5387c26247826fadcfced5497ee152edeb9d379778d759f7a43d2
+  checksum: b0df0352ea73445d3c98024e457dbaec1160cb2281865b017b82eba6595cc2a6d8d1e68aac8ee273c1cf453418329324865c4a4aa8be2c5ab859edb6259291e9
   languageName: node
   linkType: hard
 
@@ -3546,6 +4685,25 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@smithy/protocol-http@npm:^1.0.1":
+  version: 1.1.0
+  resolution: "@smithy/protocol-http@npm:1.1.0"
+  dependencies:
+    "@smithy/types": ^1.1.0
+    tslib: ^2.5.0
+  checksum: f912e085a477664abf38ff4cd0c2ac064ef068afc6cc0a09ce9c2849f07bac8be622edf60f19a91e2701184b63daa85cb2898e243d7a2c6fd1613de705b3152c
+  languageName: node
+  linkType: hard
+
+"@smithy/types@npm:^1.0.0, @smithy/types@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "@smithy/types@npm:1.1.0"
+  dependencies:
+    tslib: ^2.5.0
+  checksum: 8c0589fa973e5c71cf776c28c43aba04ee07139578fd0174aac0d74c3688e3ffa7075cecd65b223b2a155ad711808b1e4ad58a084ba9f24fcb49679272018387
+  languageName: node
+  linkType: hard
+
 "@sqltools/formatter@npm:^1.2.5":
   version: 1.2.5
   resolution: "@sqltools/formatter@npm:1.2.5"
@@ -5457,7 +6615,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"avvio@npm:^8.2.0":
+"avvio@npm:^8.2.1":
   version: 8.2.1
   resolution: "avvio@npm:8.2.1"
   dependencies:
@@ -5468,24 +6626,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"aws-sdk@npm:2.1370.0":
-  version: 2.1370.0
-  resolution: "aws-sdk@npm:2.1370.0"
-  dependencies:
-    buffer: 4.9.2
-    events: 1.1.1
-    ieee754: 1.1.13
-    jmespath: 0.16.0
-    querystring: 0.2.0
-    sax: 1.2.1
-    url: 0.10.3
-    util: ^0.12.4
-    uuid: 8.0.0
-    xml2js: 0.5.0
-  checksum: 6f4170efb47c1e9731afbf48928d365231f348f0245a6c36a4284fa2aed07571c20f64f805c309bc73b288c1d345aaf90a5394520b4ca15b9259807f766f719f
-  languageName: node
-  linkType: hard
-
 "axe-core@npm:^4.6.2":
   version: 4.6.3
   resolution: "axe-core@npm:4.6.3"
@@ -5779,7 +6919,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1":
+"base64-js@npm:^1.3.1":
   version: 1.5.1
   resolution: "base64-js@npm:1.5.1"
   checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005
@@ -5879,6 +7019,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"bowser@npm:^2.11.0":
+  version: 2.11.0
+  resolution: "bowser@npm:2.11.0"
+  checksum: 29c3f01f22e703fa6644fc3b684307442df4240b6e10f6cfe1b61c6ca5721073189ca97cdeedb376081148c8518e33b1d818a57f781d70b0b70e1f31fb48814f
+  languageName: node
+  linkType: hard
+
 "brace-expansion@npm:^1.1.7":
   version: 1.1.11
   resolution: "brace-expansion@npm:1.1.11"
@@ -5978,17 +7125,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"buffer@npm:4.9.2":
-  version: 4.9.2
-  resolution: "buffer@npm:4.9.2"
-  dependencies:
-    base64-js: ^1.0.2
-    ieee754: ^1.1.4
-    isarray: ^1.0.0
-  checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3
-  languageName: node
-  linkType: hard
-
 "buffer@npm:^6.0.3":
   version: 6.0.3
   resolution: "buffer@npm:6.0.3"
@@ -7394,6 +8530,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"date-fns@npm:^2.29.3":
+  version: 2.30.0
+  resolution: "date-fns@npm:2.30.0"
+  dependencies:
+    "@babel/runtime": ^7.21.0
+  checksum: f7be01523282e9bb06c0cd2693d34f245247a29098527d4420628966a2d9aad154bd0e90a6b1cf66d37adcb769cd108cf8a7bd49d76db0fb119af5cdd13644f4
+  languageName: node
+  linkType: hard
+
 "dateformat@npm:^3.0.0":
   version: 3.0.3
   resolution: "dateformat@npm:3.0.3"
@@ -8730,13 +9875,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"events@npm:1.1.1":
-  version: 1.1.1
-  resolution: "events@npm:1.1.1"
-  checksum: 40431eb005cc4c57861b93d44c2981a49e7feb99df84cf551baed299ceea4444edf7744733f6a6667e942af687359b1f4a87ec1ec4f21d5127dac48a782039b9
-  languageName: node
-  linkType: hard
-
 "events@npm:^3.2.0, events@npm:^3.3.0":
   version: 3.3.0
   resolution: "events@npm:3.3.0"
@@ -8917,6 +10055,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"fast-xml-parser@npm:4.2.4":
+  version: 4.2.4
+  resolution: "fast-xml-parser@npm:4.2.4"
+  dependencies:
+    strnum: ^1.0.5
+  bin:
+    fxparser: src/cli/cli.js
+  checksum: d3b4d0c0152c09f98def792769fca6bb3fa1d597f9745d9564451c239089bd86bdf573c9263b4944860028cb7edb81752d64399c1aff8b87c9225ecef96905f7
+  languageName: node
+  linkType: hard
+
 "fastify-plugin@npm:^4.0.0":
   version: 4.5.0
   resolution: "fastify-plugin@npm:4.5.0"
@@ -8924,27 +10073,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"fastify@npm:4.17.0":
-  version: 4.17.0
-  resolution: "fastify@npm:4.17.0"
+"fastify@npm:4.18.0":
+  version: 4.18.0
+  resolution: "fastify@npm:4.18.0"
   dependencies:
     "@fastify/ajv-compiler": ^3.5.0
-    "@fastify/error": ^3.0.0
+    "@fastify/error": ^3.2.0
     "@fastify/fast-json-stringify-compiler": ^4.3.0
     abstract-logging: ^2.0.1
-    avvio: ^8.2.0
+    avvio: ^8.2.1
     fast-content-type-parse: ^1.0.0
     fast-json-stringify: ^5.7.0
     find-my-way: ^7.6.0
-    light-my-request: ^5.6.1
-    pino: ^8.5.0
-    process-warning: ^2.0.0
+    light-my-request: ^5.9.1
+    pino: ^8.12.0
+    process-warning: ^2.2.0
     proxy-addr: ^2.0.7
     rfdc: ^1.3.0
     secure-json-parse: ^2.5.0
-    semver: ^7.3.7
+    semver: ^7.5.0
     tiny-lru: ^11.0.1
-  checksum: 919018b384485452ad2100b172080a266327bdb89c68dbebb8443cc2352efe96fb3d1225d2924b1ca8f06175d5478fee5c18c8857fe6981b8c39328182ff48a1
+  checksum: 14dc83e3cc46edf4c0c8874eec2d9e95c96980acd3dfa0114d40cf0b27e09f1c01e4956d5bdb412be955c59e4e1e4a9c28a31659dfb26e8c1d0e2d77af4d926e
   languageName: node
   linkType: hard
 
@@ -10205,14 +11354,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ieee754@npm:1.1.13":
-  version: 1.1.13
-  resolution: "ieee754@npm:1.1.13"
-  checksum: 102df1ba662e316e6160f7ce29c7c7fa3e04f2014c288336c5a9ff40bbcc2a27d209fa2a81ebfb33f28b1941021343d30e9ad8ee85a2d61f79f5936c35edc33d
-  languageName: node
-  linkType: hard
-
-"ieee754@npm:^1.1.4, ieee754@npm:^1.2.1":
+"ieee754@npm:^1.2.1":
   version: 1.2.1
   resolution: "ieee754@npm:1.2.1"
   checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e
@@ -10406,7 +11548,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1":
+"is-arguments@npm:^1.1.1":
   version: 1.1.1
   resolution: "is-arguments@npm:1.1.1"
   dependencies:
@@ -10545,15 +11687,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-generator-function@npm:^1.0.7":
-  version: 1.0.10
-  resolution: "is-generator-function@npm:1.0.10"
-  dependencies:
-    has-tostringtag: ^1.0.0
-  checksum: d54644e7dbaccef15ceb1e5d91d680eb5068c9ee9f9eb0a9e04173eb5542c9b51b5ab52c5537f5703e48d5fddfd376817c1ca07a84a407b7115b769d4bdde72b
-  languageName: node
-  linkType: hard
-
 "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
   version: 4.0.3
   resolution: "is-glob@npm:4.0.3"
@@ -10746,7 +11879,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9":
+"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.9":
   version: 1.1.10
   resolution: "is-typed-array@npm:1.1.10"
   dependencies:
@@ -10801,13 +11934,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"isarray@npm:^1.0.0, isarray@npm:~1.0.0":
-  version: 1.0.0
-  resolution: "isarray@npm:1.0.0"
-  checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
-  languageName: node
-  linkType: hard
-
 "isarray@npm:^2.0.5":
   version: 2.0.5
   resolution: "isarray@npm:2.0.5"
@@ -10815,6 +11941,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"isarray@npm:~1.0.0":
+  version: 1.0.0
+  resolution: "isarray@npm:1.0.0"
+  checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab
+  languageName: node
+  linkType: hard
+
 "isexe@npm:^2.0.0":
   version: 2.0.0
   resolution: "isexe@npm:2.0.0"
@@ -11953,13 +13086,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"jmespath@npm:0.16.0":
-  version: 0.16.0
-  resolution: "jmespath@npm:0.16.0"
-  checksum: 2d602493a1e4addfd1350ac8c9d54b1b03ed09e305fd863bab84a4ee1f52868cf939dd1a08c5cdea29ce9ba8f86875ebb458b6ed45dab3e1c3f2694503fb2fd9
-  languageName: node
-  linkType: hard
-
 "joi@npm:^17.7.0":
   version: 17.8.4
   resolution: "joi@npm:17.8.4"
@@ -12309,14 +13435,14 @@ __metadata:
   languageName: node
   linkType: hard
 
-"light-my-request@npm:^5.6.1":
-  version: 5.9.1
-  resolution: "light-my-request@npm:5.9.1"
+"light-my-request@npm:^5.9.1":
+  version: 5.10.0
+  resolution: "light-my-request@npm:5.10.0"
   dependencies:
     cookie: ^0.5.0
     process-warning: ^2.0.0
     set-cookie-parser: ^2.4.1
-  checksum: d501924ed3b4e04819145d6577c778a04573305762d6e6e8606307e6253a77a5c71e0ca60d79f37b76232d7cb9ccda11d47ca9969ee5934ca69179c7ee97c07f
+  checksum: 50450afd155c8a8800435d568b118ea24b0367d36defaf0467fc49f5e7591c3f8c90b97641f67792d3f25190d53c988c6bc03dd389dfec9dcdfd1907f01ff36f
   languageName: node
   linkType: hard
 
@@ -13173,7 +14299,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"node-gyp-build@npm:^4.3.0":
+"node-gyp-build@npm:^4.6.0":
   version: 4.6.0
   resolution: "node-gyp-build@npm:4.6.0"
   bin:
@@ -13872,7 +14998,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"pino@npm:^8.5.0":
+"pino@npm:^8.12.0":
   version: 8.14.1
   resolution: "pino@npm:8.14.1"
   dependencies:
@@ -15219,7 +16345,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"process-warning@npm:^2.0.0":
+"process-warning@npm:^2.0.0, process-warning@npm:^2.2.0":
   version: 2.2.0
   resolution: "process-warning@npm:2.2.0"
   checksum: 394ae451c2622ee7d014a7196d36658fc1a5d5cc9f3bfeb54aadd5b77fcfecc89a30a25db259ae76ff49fde3f3f3dd7031dcdfb4da2e5445dac795549352e5d0
@@ -15318,13 +16444,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"punycode@npm:1.3.2":
-  version: 1.3.2
-  resolution: "punycode@npm:1.3.2"
-  checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6
-  languageName: node
-  linkType: hard
-
 "punycode@npm:^2.1.0, punycode@npm:^2.1.1":
   version: 2.3.0
   resolution: "punycode@npm:2.3.0"
@@ -15364,10 +16483,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"querystring@npm:0.2.0":
-  version: 0.2.0
-  resolution: "querystring@npm:0.2.0"
-  checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69
+"qs@npm:6.11.2":
+  version: 6.11.2
+  resolution: "qs@npm:6.11.2"
+  dependencies:
+    side-channel: ^1.0.4
+  checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b
   languageName: node
   linkType: hard
 
@@ -16389,14 +17510,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"sax@npm:1.2.1":
-  version: 1.2.1
-  resolution: "sax@npm:1.2.1"
-  checksum: 8dca7d5e1cd7d612f98ac50bdf0b9f63fbc964b85f0c4e2eb271f8b9b47fd3bf344c4d6a592e69ecf726d1485ca62cd8a52e603bbc332d18a66af25a9a1045ad
-  languageName: node
-  linkType: hard
-
-"sax@npm:>=0.6.0, sax@npm:~1.2.4":
+"sax@npm:~1.2.4":
   version: 1.2.4
   resolution: "sax@npm:1.2.4"
   checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe
@@ -16519,6 +17633,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"semver@npm:^7.5.0":
+  version: 7.5.2
+  resolution: "semver@npm:7.5.2"
+  dependencies:
+    lru-cache: ^6.0.0
+  bin:
+    semver: bin/semver.js
+  checksum: 3fdf5d1e6f170fe8bcc41669e31787649af91af7f54f05c71d0865bb7aa27e8b92f68b3e6b582483e2c1c648008bc84249d2cd86301771fe5cbf7621d1fe5375
+  languageName: node
+  linkType: hard
+
 "send@npm:0.18.0":
   version: 0.18.0
   resolution: "send@npm:0.18.0"
@@ -16735,13 +17860,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"sodium-native@npm:^3.0.0":
-  version: 3.4.1
-  resolution: "sodium-native@npm:3.4.1"
+"sodium-native@npm:^4.0.0":
+  version: 4.0.4
+  resolution: "sodium-native@npm:4.0.4"
   dependencies:
     node-gyp: latest
-    node-gyp-build: ^4.3.0
-  checksum: 88f2f8c9ecb3c7952098b667ee3803f24253d72a3b3874b126e0e36b2ac20432e12ad44bde3664024e6d0ae1bc6d24fdebc81273af161e735f2eec22f10d26dd
+    node-gyp-build: ^4.6.0
+  checksum: 5697e7d8fa49b0ccde9b15beda2664c09ccc55450304b78287a6522e3e5db02c7fc3de415df5b2273bbc45e3f5e17966bf487c8a8bff7a84c8cbbee8fcd5e7b2
   languageName: node
   linkType: hard
 
@@ -17211,6 +18336,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"strnum@npm:^1.0.5":
+  version: 1.0.5
+  resolution: "strnum@npm:1.0.5"
+  checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2
+  languageName: node
+  linkType: hard
+
 "style-inject@npm:^0.3.0":
   version: 0.3.0
   resolution: "style-inject@npm:0.3.0"
@@ -17812,6 +18944,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"tslib@npm:^2.3.1":
+  version: 2.5.3
+  resolution: "tslib@npm:2.5.3"
+  checksum: 88902b309afaf83259131c1e13da1dceb0ad1682a213143a1346a649143924d78cf3760c448b84d796938fd76127183894f8d85cbb3bf9c4fddbfcc140c0003c
+  languageName: node
+  linkType: hard
+
 "tsutils@npm:^3.21.0":
   version: 3.21.0
   resolution: "tsutils@npm:3.21.0"
@@ -17927,15 +19066,16 @@ __metadata:
   languageName: node
   linkType: hard
 
-"typeorm@npm:0.3.15":
-  version: 0.3.15
-  resolution: "typeorm@npm:0.3.15"
+"typeorm@npm:0.3.16":
+  version: 0.3.16
+  resolution: "typeorm@npm:0.3.16"
   dependencies:
     "@sqltools/formatter": ^1.2.5
     app-root-path: ^3.1.0
     buffer: ^6.0.3
     chalk: ^4.1.2
     cli-highlight: ^2.1.11
+    date-fns: ^2.29.3
     debug: ^4.3.4
     dotenv: ^16.0.3
     glob: ^8.1.0
@@ -18002,7 +19142,7 @@ __metadata:
     typeorm: cli.js
     typeorm-ts-node-commonjs: cli-ts-node-commonjs.js
     typeorm-ts-node-esm: cli-ts-node-esm.js
-  checksum: 91038765ac3472ede5a979454c6e5c9b9d93527d5e61426c6c434e5f37c3561230fe9517fad93b8309fcf5cdd57648521b86791ecf9f6cbbb7872bfba74cf4f8
+  checksum: d889f6b4392367c38d9748fabaab1a75d21d78e7aa62088d53847958f2308b672acf8ab3e0bcf26c880fa52fac54be9a78ca43c99f19c4fe604fab458dc04c17
   languageName: node
   linkType: hard
 
@@ -18197,16 +19337,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"url@npm:0.10.3":
-  version: 0.10.3
-  resolution: "url@npm:0.10.3"
-  dependencies:
-    punycode: 1.3.2
-    querystring: 0.2.0
-  checksum: 7b83ddb106c27bf9bde8629ccbe8d26e9db789c8cda5aa7db72ca2c6f9b8a88a5adf206f3e10db78e6e2d042b327c45db34c7010c1bf0d9908936a17a2b57d05
-  languageName: node
-  linkType: hard
-
 "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1":
   version: 1.0.2
   resolution: "util-deprecate@npm:1.0.2"
@@ -18226,19 +19356,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"util@npm:^0.12.4":
-  version: 0.12.5
-  resolution: "util@npm:0.12.5"
-  dependencies:
-    inherits: ^2.0.3
-    is-arguments: ^1.0.4
-    is-generator-function: ^1.0.7
-    is-typed-array: ^1.1.3
-    which-typed-array: ^1.1.2
-  checksum: 705e51f0de5b446f4edec10739752ac25856541e0254ea1e7e45e5b9f9b0cb105bc4bd415736a6210edc68245a7f903bf085ffb08dd7deb8a0e847f60538a38a
-  languageName: node
-  linkType: hard
-
 "utila@npm:~0.4":
   version: 0.4.0
   resolution: "utila@npm:0.4.0"
@@ -18253,15 +19370,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"uuid@npm:8.0.0":
-  version: 8.0.0
-  resolution: "uuid@npm:8.0.0"
-  bin:
-    uuid: dist/bin/uuid
-  checksum: 56d4e23aa7ac26fa2db6bd1778db34cb8c9f5a10df1770a27167874bf6705fc8f14a4ac414af58a0d96c7653b2bd4848510b29d1c2ef8c91ccb17429c1872b5e
-  languageName: node
-  linkType: hard
-
 "uuid@npm:9.0.0, uuid@npm:^9.0.0":
   version: 9.0.0
   resolution: "uuid@npm:9.0.0"
@@ -18705,7 +19813,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9":
+"which-typed-array@npm:^1.1.9":
   version: 1.1.9
   resolution: "which-typed-array@npm:1.1.9"
   dependencies:
@@ -19053,23 +20161,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"xml2js@npm:0.5.0":
-  version: 0.5.0
-  resolution: "xml2js@npm:0.5.0"
-  dependencies:
-    sax: ">=0.6.0"
-    xmlbuilder: ~11.0.0
-  checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea
-  languageName: node
-  linkType: hard
-
-"xmlbuilder@npm:~11.0.0":
-  version: 11.0.1
-  resolution: "xmlbuilder@npm:11.0.1"
-  checksum: 7152695e16f1a9976658215abab27e55d08b1b97bca901d58b048d2b6e106b5af31efccbdecf9b07af37c8377d8e7e821b494af10b3a68b0ff4ae60331b415b0
-  languageName: node
-  linkType: hard
-
 "xmlchars@npm:^2.2.0":
   version: 2.2.0
   resolution: "xmlchars@npm:2.2.0"