From 929769eb482727d837f46166ac242cd0bf810d3f Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Fri, 2 Feb 2024 13:59:04 -0600 Subject: [PATCH 01/13] Create initial UI for choosing tags --- src/app/admin/photos/[photoId]/edit/page.tsx | 6 +- src/app/admin/uploads/[uploadPath]/page.tsx | 10 ++- src/components/Badge.tsx | 2 +- src/components/CommaSeparatedInput.tsx | 86 ++++++++++++++++++++ src/components/FieldSetWithStatus.tsx | 60 +++++++++----- src/photo/PhotoEditPageClient.tsx | 5 +- src/photo/{ => form}/PhotoForm.tsx | 15 +++- src/photo/{form.ts => form/index.ts} | 14 +++- src/site/globals.css | 11 ++- src/tag/index.ts | 3 + 10 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 src/components/CommaSeparatedInput.tsx rename src/photo/{ => form}/PhotoForm.tsx (93%) rename src/photo/{form.ts => form/index.ts} (96%) diff --git a/src/app/admin/photos/[photoId]/edit/page.tsx b/src/app/admin/photos/[photoId]/edit/page.tsx index 5c51f491..27762339 100644 --- a/src/app/admin/photos/[photoId]/edit/page.tsx +++ b/src/app/admin/photos/[photoId]/edit/page.tsx @@ -1,5 +1,5 @@ import { redirect } from 'next/navigation'; -import { getPhotoNoStore } from '@/cache'; +import { getPhotoNoStore, getUniqueTagsCached } from '@/cache'; import { PATH_ADMIN } from '@/site/paths'; import PhotoEditPageClient from '@/photo/PhotoEditPageClient'; @@ -12,7 +12,9 @@ export default async function PhotoEditPage({ if (!photo) { redirect(PATH_ADMIN); } + const uniqueTags = (await getUniqueTagsCached()).map(tag => tag.tag); + return ( - + ); }; diff --git a/src/app/admin/uploads/[uploadPath]/page.tsx b/src/app/admin/uploads/[uploadPath]/page.tsx index 7c031239..f2354591 100644 --- a/src/app/admin/uploads/[uploadPath]/page.tsx +++ b/src/app/admin/uploads/[uploadPath]/page.tsx @@ -1,8 +1,9 @@ -import PhotoForm from '@/photo/PhotoForm'; +import PhotoForm from '@/photo/form/PhotoForm'; import AdminChildPage from '@/components/AdminChildPage'; import { PATH_ADMIN, PATH_ADMIN_UPLOADS } from '@/site/paths'; import { extractExifDataFromBlobPath } from '@/photo/server'; import { redirect } from 'next/navigation'; +import { getUniqueTagsCached } from '@/cache'; interface Params { params: { uploadPath: string } @@ -14,6 +15,8 @@ export default async function UploadPage({ params: { uploadPath } }: Params) { photoFormExif, } = await extractExifDataFromBlobPath(uploadPath); + const uniqueTags = (await getUniqueTagsCached()).map(tag => tag.tag); + if (!photoFormExif) { redirect(PATH_ADMIN); } return ( @@ -22,7 +25,10 @@ export default async function UploadPage({ params: { uploadPath } }: Params) { backLabel="Uploads" breadcrumb={blobId} > - + ); }; diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index d1c3c369..67461974 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -26,7 +26,7 @@ export default function Badge({ 'px-[0.3rem] py-1 rounded-[0.25rem]', 'text-[0.7rem] font-medium', highContrast - ? 'text-invert bg-primary' + ? 'text-invert bg-invert' : 'text-medium bg-gray-300/30 dark:bg-gray-700/50', interactive && highContrast ? 'hover:opacity-70' diff --git a/src/components/CommaSeparatedInput.tsx b/src/components/CommaSeparatedInput.tsx new file mode 100644 index 00000000..808cc186 --- /dev/null +++ b/src/components/CommaSeparatedInput.tsx @@ -0,0 +1,86 @@ +import { convertStringToArray } from '@/utility/string'; +import { Combobox } from '@headlessui/react'; +import { clsx } from 'clsx/lite'; +import { BiExpandVertical } from 'react-icons/bi'; +import { FaCheck } from 'react-icons/fa'; + +export default function CommaSeparatedInput({ + onChange, + id, + name, + value, + type, + autoCapitalize, + readOnly, + options: optionsRaw = [], +}: { + value?: string + onChange?: (value: string) => void + options?: string[] +} & Omit, 'onChange'>) { + const items = (convertStringToArray(value) ?? []) + .map(tag => tag.trim()) + .filter(Boolean); + + const options = items + .filter(item => !optionsRaw.includes(item)) + .concat(optionsRaw); + + return ( +
+ onChange?.(e.join(','))} + multiple + > +
+ onChange?.(e.target.value)} + displayValue={(tags: string[]) => tags.join(', ')} + {...{ + id, + name, + type, + autoCapitalize, + readOnly, + }} + /> + {options && + + + } +
+ {options && + + {options.map((tag) => ( + clsx( + 'p-1 rounded-[0.2rem] !hover:cursor', + focus && 'text-invert bg-invert', + )} + > + {({ selected }) =>
+ + {tag} + + {selected && + } +
} +
+ ))} +
} +
+
+ ); +} diff --git a/src/components/FieldSetWithStatus.tsx b/src/components/FieldSetWithStatus.tsx index c984720c..71df72b3 100644 --- a/src/components/FieldSetWithStatus.tsx +++ b/src/components/FieldSetWithStatus.tsx @@ -4,6 +4,8 @@ import { LegacyRef } from 'react'; import { useFormStatus } from 'react-dom'; import Spinner from './Spinner'; import { clsx } from 'clsx/lite'; +import { FieldSetType } from '@/photo/form'; +import CommaSeparatedInput from '@/components/CommaSeparatedInput'; export default function FieldSetWithStatus({ id, @@ -14,6 +16,7 @@ export default function FieldSetWithStatus({ onChange, selectOptions, selectOptionsDefaultLabel, + commaSeparatedOptions, placeholder, loading, required, @@ -30,12 +33,13 @@ export default function FieldSetWithStatus({ onChange?: (value: string) => void selectOptions?: { value: string, label: string }[] selectOptionsDefaultLabel?: string + commaSeparatedOptions?: string[] placeholder?: string loading?: boolean required?: boolean readOnly?: boolean capitalize?: boolean - type?: 'text' | 'email' | 'password' | 'checkbox' + type?: FieldSetType inputRef?: LegacyRef }) { const { pending } = useFormStatus(); @@ -86,25 +90,41 @@ export default function FieldSetWithStatus({ {optionLabel} )} - : onChange?.(type === 'checkbox' - ? e.target.value === 'true' ? 'false' : 'true' - : e.target.value)} - type={type} - autoComplete="off" - readOnly={readOnly || pending} - className={clsx( - type === 'text' && 'w-full', - error && 'error', - )} - autoCapitalize={!capitalize ? 'off' : undefined} - />} + : commaSeparatedOptions + ? + : onChange?.(type === 'checkbox' + ? e.target.value === 'true' ? 'false' : 'true' + : e.target.value)} + type={type} + autoComplete="off" + autoCapitalize={!capitalize ? 'off' : undefined} + readOnly={readOnly || pending} + className={clsx( + type === 'text' && 'w-full', + error && 'error', + )} + />} ); }; diff --git a/src/photo/PhotoEditPageClient.tsx b/src/photo/PhotoEditPageClient.tsx index 65d4d471..0bf00b99 100644 --- a/src/photo/PhotoEditPageClient.tsx +++ b/src/photo/PhotoEditPageClient.tsx @@ -5,7 +5,7 @@ import { Photo } from '.'; import { PATH_ADMIN_PHOTOS } from '@/site/paths'; import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; import { PhotoFormData, convertPhotoToFormData } from './form'; -import PhotoForm from './PhotoForm'; +import PhotoForm from './form/PhotoForm'; import { useFormState } from 'react-dom'; import { areSimpleObjectsEqual } from '@/utility/object'; import IconGrSync from '@/site/IconGrSync'; @@ -13,8 +13,10 @@ import { getExifDataAction } from './actions'; export default function PhotoEditPageClient({ photo, + uniqueTags, }: { photo: Photo + uniqueTags?: string[] }) { const seedExifData = { url: photo.url }; @@ -51,6 +53,7 @@ export default function PhotoEditPageClient({ updatedExifData={hasExifDataBeenFound ? updatedExifData : undefined} + uniqueTags={uniqueTags} /> ); diff --git a/src/photo/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx similarity index 93% rename from src/photo/PhotoForm.tsx rename to src/photo/form/PhotoForm.tsx index a25fdc0b..9101f210 100644 --- a/src/photo/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -7,9 +7,9 @@ import { convertFormKeysToLabels, getInitialErrors, isFormValid, -} from './form'; +} from '.'; import FieldSetWithStatus from '@/components/FieldSetWithStatus'; -import { createPhotoAction, updatePhotoAction } from './actions'; +import { createPhotoAction, updatePhotoAction } from '../actions'; import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; import Link from 'next/link'; import { clsx } from 'clsx/lite'; @@ -23,6 +23,7 @@ import { toastSuccess, toastWarning } from '@/toast'; import { getDimensionsFromSize } from '@/utility/size'; import ImageBlurFallback from '@/components/ImageBlurFallback'; import { BLUR_ENABLED } from '@/site/config'; +import { sortTagsWithoutFavs } from '@/tag'; const THUMBNAIL_SIZE = 300; @@ -30,11 +31,13 @@ export default function PhotoForm({ initialPhotoForm, updatedExifData, type = 'create', + uniqueTags, debugBlur, }: { initialPhotoForm: Partial updatedExifData?: Partial type?: 'create' | 'edit' + uniqueTags?: string[] debugBlur?: boolean }) { const [formData, setFormData] = @@ -140,6 +143,7 @@ export default function PhotoForm({
blur()} className="space-y-6" > {FORM_METADATA_ENTRIES.map(([key, { @@ -154,7 +158,7 @@ export default function PhotoForm({ hideIfEmpty, hideBasedOnCamera, loadingMessage, - checkbox, + type, }]) => ( (!hideIfEmpty || formData[key]) && @@ -175,6 +179,9 @@ export default function PhotoForm({ }} selectOptions={options} selectOptionsDefaultLabel={optionsDefaultLabel} + commaSeparatedOptions={key === 'tags' + ? sortTagsWithoutFavs(uniqueTags ?? []) + : undefined} required={required} readOnly={readOnly} capitalize={capitalize} @@ -182,7 +189,7 @@ export default function PhotoForm({ ? loadingMessage : undefined} loading={loadingMessage && !formData[key] ? true : false} - type={checkbox ? 'checkbox' : undefined} + type={type} />)}
; +export type FieldSetType = + 'text' | + 'email' | + 'password' | + 'checkbox'; + type FormMeta = { label: string note?: string @@ -32,7 +38,7 @@ type FormMeta = { hideIfEmpty?: boolean hideBasedOnCamera?: (make?: string, mode?: string) => boolean loadingMessage?: string - checkbox?: boolean + type?: FieldSetType options?: { value: string, label: string }[] optionsDefaultLabel?: string }; @@ -77,8 +83,8 @@ const FORM_METADATA: Record = { takenAt: { label: 'taken at' }, takenAtNaive: { label: 'taken at (naive)' }, priorityOrder: { label: 'priority order' }, - favorite: { label: 'favorite', checkbox: true, virtual: true }, - hidden: { label: 'hidden', checkbox: true }, + favorite: { label: 'favorite', type: 'checkbox', virtual: true }, + hidden: { label: 'hidden', type: 'checkbox' }, }; export const FORM_METADATA_ENTRIES = diff --git a/src/site/globals.css b/src/site/globals.css index 528d5fe8..1b8325c6 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -7,8 +7,8 @@ body { @apply text-main + bg-main font-mono text-sm md:text-base - bg-white dark:bg-black } /* Forms */ label { @@ -17,12 +17,13 @@ text-medium tracking-wider } + .control, button, .button, input[type=text], input[type=email], input[type=password], select { @apply px-2.5 py-2 border rounded-md - bg-white dark:bg-black + bg-main border-gray-200 dark:border-gray-700 font-mono text-base leading-tight min-h-[2.4rem] @@ -142,12 +143,16 @@ text-red-500 dark:text-red-400 } /* Common Utilities: Background */ + .bg-main { + @apply + bg-white dark:bg-black + } .bg-content { @apply bg-white border-gray-200 dark:bg-black dark:border-gray-800 } - .bg-primary { + .bg-invert { @apply bg-gray-900 dark:bg-gray-100 } diff --git a/src/tag/index.ts b/src/tag/index.ts index ceb71026..03cf7699 100644 --- a/src/tag/index.ts +++ b/src/tag/index.ts @@ -43,6 +43,9 @@ export const sortTagsObject = ( .filter(({ tag }) => tag!== tagToHide) .sort(({ tag: a }, { tag: b }) => isTagFavs(a) ? -1 : a.localeCompare(b)); +export const sortTagsWithoutFavs = (tags: string[]) => + sortTags(tags, TAG_FAVS); + export const descriptionForTaggedPhotos = ( photos: Photo[], dateBased?: boolean, From e571161aca4b34f9b4957ff1c0c76464d3659782 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 3 Feb 2024 13:26:43 -0600 Subject: [PATCH 02/13] Add explicit "create new tag" menu option --- src/components/CommaSeparatedInput.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/CommaSeparatedInput.tsx b/src/components/CommaSeparatedInput.tsx index 808cc186..752430bf 100644 --- a/src/components/CommaSeparatedInput.tsx +++ b/src/components/CommaSeparatedInput.tsx @@ -18,6 +18,9 @@ export default function CommaSeparatedInput({ onChange?: (value: string) => void options?: string[] } & Omit, 'onChange'>) { + const lastTerm = value?.split(',').slice(-1)?.[0].trim(); + const hasLastTerm = lastTerm && !optionsRaw.includes(lastTerm); + const items = (convertStringToArray(value) ?? []) .map(tag => tag.trim()) .filter(Boolean); @@ -57,10 +60,16 @@ export default function CommaSeparatedInput({ /> }
- {options && + {(options || hasLastTerm) && + {hasLastTerm && + + Create {`"${lastTerm}"`} + } {options.map((tag) => ( Date: Sat, 3 Feb 2024 14:03:07 -0600 Subject: [PATCH 03/13] Remove last term, tweak check mark --- src/components/CommaSeparatedInput.tsx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/CommaSeparatedInput.tsx b/src/components/CommaSeparatedInput.tsx index 752430bf..bab7ee57 100644 --- a/src/components/CommaSeparatedInput.tsx +++ b/src/components/CommaSeparatedInput.tsx @@ -18,9 +18,6 @@ export default function CommaSeparatedInput({ onChange?: (value: string) => void options?: string[] } & Omit, 'onChange'>) { - const lastTerm = value?.split(',').slice(-1)?.[0].trim(); - const hasLastTerm = lastTerm && !optionsRaw.includes(lastTerm); - const items = (convertStringToArray(value) ?? []) .map(tag => tag.trim()) .filter(Boolean); @@ -60,31 +57,29 @@ export default function CommaSeparatedInput({ /> } - {(options || hasLastTerm) && + {options && - {hasLastTerm && - - Create {`"${lastTerm}"`} - } {options.map((tag) => ( clsx( 'p-1 rounded-[0.2rem] !hover:cursor', - focus && 'text-invert bg-invert', + focus && 'bg-gray-100 dark:bg-gray-900', )} > {({ selected }) =>
+ + {selected && + } + {tag} - {selected && - } +
}
))} From f4913db81e0bac25b8813fa71a6965a3e6ead25b Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 3 Feb 2024 23:49:08 -0600 Subject: [PATCH 04/13] Switch to new tag component --- src/components/CommaSeparatedInput.tsx | 90 ----------- src/components/FieldSetWithStatus.tsx | 22 ++- src/components/TagInput.tsx | 210 +++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 103 deletions(-) delete mode 100644 src/components/CommaSeparatedInput.tsx create mode 100644 src/components/TagInput.tsx diff --git a/src/components/CommaSeparatedInput.tsx b/src/components/CommaSeparatedInput.tsx deleted file mode 100644 index bab7ee57..00000000 --- a/src/components/CommaSeparatedInput.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { convertStringToArray } from '@/utility/string'; -import { Combobox } from '@headlessui/react'; -import { clsx } from 'clsx/lite'; -import { BiExpandVertical } from 'react-icons/bi'; -import { FaCheck } from 'react-icons/fa'; - -export default function CommaSeparatedInput({ - onChange, - id, - name, - value, - type, - autoCapitalize, - readOnly, - options: optionsRaw = [], -}: { - value?: string - onChange?: (value: string) => void - options?: string[] -} & Omit, 'onChange'>) { - const items = (convertStringToArray(value) ?? []) - .map(tag => tag.trim()) - .filter(Boolean); - - const options = items - .filter(item => !optionsRaw.includes(item)) - .concat(optionsRaw); - - return ( -
- onChange?.(e.join(','))} - multiple - > -
- onChange?.(e.target.value)} - displayValue={(tags: string[]) => tags.join(', ')} - {...{ - id, - name, - type, - autoCapitalize, - readOnly, - }} - /> - {options && - - - } -
- {options && - - {options.map((tag) => ( - clsx( - 'p-1 rounded-[0.2rem] !hover:cursor', - focus && 'bg-gray-100 dark:bg-gray-900', - )} - > - {({ selected }) =>
- - {selected && - } - - - {tag} - - -
} -
- ))} -
} -
-
- ); -} diff --git a/src/components/FieldSetWithStatus.tsx b/src/components/FieldSetWithStatus.tsx index 71df72b3..cc5cf811 100644 --- a/src/components/FieldSetWithStatus.tsx +++ b/src/components/FieldSetWithStatus.tsx @@ -5,7 +5,8 @@ import { useFormStatus } from 'react-dom'; import Spinner from './Spinner'; import { clsx } from 'clsx/lite'; import { FieldSetType } from '@/photo/form'; -import CommaSeparatedInput from '@/components/CommaSeparatedInput'; +import TagInput from './TagInput'; +import { convertStringToArray } from '@/utility/string'; export default function FieldSetWithStatus({ id, @@ -91,20 +92,15 @@ export default function FieldSetWithStatus({ )} : commaSeparatedOptions - ? { + onChange?.(value.join(', ')); + console.log(value.join(', ')); + }} readOnly={readOnly || pending} - className={clsx( - type === 'text' && 'w-full', - error && 'error', - )} /> : void + readOnly?: boolean +}) { + const containerRef = useRef(null); + const inputRef = useRef(null); + const optionsRef = useRef(null); + + const [hasFocus, setHasFocus] = useState(false); + const [inputText, setInputText] = useState(''); + const [selectedOptionIndex, setSelectedOptionIndex] = useState(); + + const inputTextFormatted = inputText.toLocaleLowerCase().trim(); + const isInputTextNew = + inputTextFormatted && + !selectedOptions.includes(inputTextFormatted); + + let optionsFiltered = options + .filter(option => + !selectedOptions.includes(option) && + ( + !inputTextFormatted || + option.includes(inputTextFormatted) + )); + + if (isInputTextNew) { + optionsFiltered = [ + `${CREATE_LABEL}"${inputTextFormatted}"`, + ...optionsFiltered, + ]; + } + + const addOption = useCallback((option: string) => { + onChange?.([ + ...selectedOptions, + option.startsWith(CREATE_LABEL) + ? option.slice(CREATE_LABEL.length + 1, -1) + : option, + ] + .filter(Boolean) + .map(option => option.toLocaleLowerCase().trim())); + setSelectedOptionIndex(undefined); + }, [onChange, selectedOptions]); + + useEffect(() => { + if (!hasFocus) { setSelectedOptionIndex(undefined); } + }, [hasFocus]); + + useEffect(() => { + if (selectedOptionIndex !== undefined) { + const ref = optionsRef.current; + const options = ref?.querySelectorAll('div'); + const option = options?.[selectedOptionIndex] as HTMLElement | undefined; + console.log({options, option: option?.innerHTML}); + option?.focus(); + } + }, [selectedOptionIndex]); + + useEffect(() => { + const ref = containerRef.current; + const listener = (e: KeyboardEvent) => { + switch (e.key) { + case 'Enter': + case 'ArrowDown': + case 'ArrowUp': + case 'Escape': + e.stopImmediatePropagation(); + e.preventDefault(); + } + switch (e.key) { + case 'Enter': + addOption(optionsFiltered[selectedOptionIndex ?? 0]); + inputRef.current?.focus(); + setInputText(''); + break; + case 'ArrowDown': + setSelectedOptionIndex(i => { + if (i === undefined || i >= optionsFiltered.length - 1) { + return 0; + } else { + return i + 1; + } + }); + break; + case 'ArrowUp': + setSelectedOptionIndex(i => { + if (i === undefined || i === 0) { + return optionsFiltered.length - 1; + } else { + return i - 1; + } + }); + break; + case 'Backspace': + if (inputText === '') { + onChange?.(selectedOptions.slice(0, -1)); + // setHasFocus(false); + } + break; + case 'Escape': + setHasFocus(false); + break; + } + }; + ref?.addEventListener(KEYDOWN_KEY, listener); + return () => ref?.removeEventListener(KEYDOWN_KEY, listener); + }, [ + inputText, + onChange, + hasFocus, + selectedOptions, + selectedOptionIndex, + optionsFiltered, + addOption, + ]); + + return ( +
setHasFocus(true)} + onBlur={e => { + if (!e.currentTarget.contains(e.relatedTarget)) { + setHasFocus(false); + setSelectedOptionIndex(undefined); + } + }} + > +
+ {selectedOptions + .filter(Boolean) + .map(option => + + onChange?.(selectedOptions.filter(o => o !== option))} + > + {option} + )} + setInputText(e.target.value)} + autoComplete="off" + autoCapitalize="off" + readOnly={readOnly} + /> +
+
+ {hasFocus && optionsFiltered.length > 0 && +
+ {optionsFiltered.map((option, index) => +
{ + addOption(option); + inputRef.current?.focus(); + setInputText(''); + // setHasFocus(false); + }} + > + {option} +
)} +
} +
+
+ ); +} From 84481ea6cfdb1efd07a8aca5f037293c696b3549 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 3 Feb 2024 23:51:57 -0600 Subject: [PATCH 05/13] Remove logging --- src/components/TagInput.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 66173c68..7b9c091f 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -65,7 +65,6 @@ export default function TagInput({ const ref = optionsRef.current; const options = ref?.querySelectorAll('div'); const option = options?.[selectedOptionIndex] as HTMLElement | undefined; - console.log({options, option: option?.innerHTML}); option?.focus(); } }, [selectedOptionIndex]); @@ -198,7 +197,6 @@ export default function TagInput({ addOption(option); inputRef.current?.focus(); setInputText(''); - // setHasFocus(false); }} > {option} From b77c186ae9585ad95677b18aeff7348ab7c8eb30 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 14:34:17 -0600 Subject: [PATCH 06/13] Refine TagInput behavior --- src/components/FieldSetWithStatus.tsx | 16 ++-- src/components/ImageBlurFallback.tsx | 2 +- src/components/MoreMenu.tsx | 2 +- src/components/TagInput.tsx | 115 ++++++++++++++++---------- src/photo/form/PhotoForm.tsx | 4 +- src/photo/form/index.ts | 1 - src/site/globals.css | 2 +- src/tag/FavsTag.tsx | 2 +- 8 files changed, 84 insertions(+), 60 deletions(-) diff --git a/src/components/FieldSetWithStatus.tsx b/src/components/FieldSetWithStatus.tsx index cc5cf811..a680704c 100644 --- a/src/components/FieldSetWithStatus.tsx +++ b/src/components/FieldSetWithStatus.tsx @@ -6,7 +6,6 @@ import Spinner from './Spinner'; import { clsx } from 'clsx/lite'; import { FieldSetType } from '@/photo/form'; import TagInput from './TagInput'; -import { convertStringToArray } from '@/utility/string'; export default function FieldSetWithStatus({ id, @@ -77,6 +76,7 @@ export default function FieldSetWithStatus({ onChange={e => onChange?.(e.target.value)} className={clsx( 'w-full', + clsx(Boolean(error) && 'error'), // Use special class because `select` can't be readonly readOnly || pending && 'disabled-select', )} @@ -92,14 +92,12 @@ export default function FieldSetWithStatus({ )} : commaSeparatedOptions - ? - { - onChange?.(value.join(', ')); - console.log(value.join(', ')); - }} + onChange={onChange} + className={clsx(Boolean(error) && 'error')} readOnly={readOnly || pending} /> : } diff --git a/src/components/ImageBlurFallback.tsx b/src/components/ImageBlurFallback.tsx index d34c1fc1..1b1162e8 100644 --- a/src/components/ImageBlurFallback.tsx +++ b/src/components/ImageBlurFallback.tsx @@ -1,5 +1,5 @@ import { BLUR_ENABLED } from '@/site/config'; -import clsx from 'clsx/lite'; +import { clsx} from 'clsx/lite'; import Image, { ImageProps } from 'next/image'; export default function ImageBlurFallback(props: ImageProps) { diff --git a/src/components/MoreMenu.tsx b/src/components/MoreMenu.tsx index 903cf688..d553d059 100644 --- a/src/components/MoreMenu.tsx +++ b/src/components/MoreMenu.tsx @@ -1,4 +1,4 @@ -import clsx from 'clsx/lite'; +import { clsx} from 'clsx/lite'; import Link from 'next/link'; import { Menu } from '@headlessui/react'; import { FiMoreHorizontal } from 'react-icons/fi'; diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 7b9c091f..c2291547 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -1,19 +1,23 @@ -import clsx from 'clsx'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { convertStringToArray, parameterize } from '@/utility/string'; +import { clsx } from 'clsx/lite'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; const KEYDOWN_KEY = 'keydown'; - const CREATE_LABEL = 'Create '; export default function TagInput({ + name, + value = '', options = [], - selectedOptions = [], onChange, + className, readOnly, }: { + name: string + value?: string options?: string[] - selectedOptions?: string[] - onChange?: (options: string[]) => void + onChange?: (value: string) => void + className?: string readOnly?: boolean }) { const containerRef = useRef(null); @@ -24,56 +28,64 @@ export default function TagInput({ const [inputText, setInputText] = useState(''); const [selectedOptionIndex, setSelectedOptionIndex] = useState(); - const inputTextFormatted = inputText.toLocaleLowerCase().trim(); - const isInputTextNew = + const selectedOptions = useMemo(() => + convertStringToArray(value) ?? [] + , [value]); + + const inputTextFormatted = parameterize(inputText); + const isInputTextUnique = inputTextFormatted && !selectedOptions.includes(inputTextFormatted); - let optionsFiltered = options + const optionsFiltered = (isInputTextUnique + ? [`${CREATE_LABEL}"${inputTextFormatted}"`] + : []).concat(options .filter(option => !selectedOptions.includes(option) && ( !inputTextFormatted || option.includes(inputTextFormatted) - )); + ))); - if (isInputTextNew) { - optionsFiltered = [ - `${CREATE_LABEL}"${inputTextFormatted}"`, - ...optionsFiltered, - ]; - } - - const addOption = useCallback((option: string) => { - onChange?.([ - ...selectedOptions, - option.startsWith(CREATE_LABEL) - ? option.slice(CREATE_LABEL.length + 1, -1) - : option, - ] - .filter(Boolean) - .map(option => option.toLocaleLowerCase().trim())); - setSelectedOptionIndex(undefined); + const addOption = useCallback((option?: string) => { + if (option && !selectedOptions.includes(option)) { + onChange?.([ + ...selectedOptions, + option.startsWith(CREATE_LABEL) + ? option.slice(CREATE_LABEL.length + 1, -1) + : option, + ] + .filter(Boolean) + .map(option => option.toLocaleLowerCase().trim()).join(',')); + setSelectedOptionIndex(undefined); + } }, [onChange, selectedOptions]); + const removeOption = useCallback((option: string) => { + onChange?.(selectedOptions.filter(o => o !== option).join(',')); + }, [onChange, selectedOptions]); + + // Reset selected option index when focus is lost useEffect(() => { if (!hasFocus) { setSelectedOptionIndex(undefined); } }, [hasFocus]); + // Focus option in the DOM when selected index changes useEffect(() => { if (selectedOptionIndex !== undefined) { - const ref = optionsRef.current; - const options = ref?.querySelectorAll('div'); + const options = optionsRef.current?.querySelectorAll('div'); const option = options?.[selectedOptionIndex] as HTMLElement | undefined; option?.focus(); } }, [selectedOptionIndex]); + // Setup keyboard listener useEffect(() => { const ref = containerRef.current; const listener = (e: KeyboardEvent) => { + // Keys which always trap focus switch (e.key) { - case 'Enter': + case ',': case 'ArrowDown': case 'ArrowUp': case 'Escape': @@ -82,10 +94,20 @@ export default function TagInput({ } switch (e.key) { case 'Enter': + // Only trap focus if there are options to select + // otherwise allow form to submit + if (optionsFiltered.length > 0) { + e.stopImmediatePropagation(); + e.preventDefault(); + } addOption(optionsFiltered[selectedOptionIndex ?? 0]); inputRef.current?.focus(); setInputText(''); break; + case ',': + addOption(inputText); + setInputText(''); + break; case 'ArrowDown': setSelectedOptionIndex(i => { if (i === undefined || i >= optionsFiltered.length - 1) { @@ -105,9 +127,8 @@ export default function TagInput({ }); break; case 'Backspace': - if (inputText === '') { - onChange?.(selectedOptions.slice(0, -1)); - // setHasFocus(false); + if (inputText === '' && selectedOptions.length > 0) { + removeOption(selectedOptions[selectedOptions.length - 1]); } break; case 'Escape': @@ -119,7 +140,7 @@ export default function TagInput({ return () => ref?.removeEventListener(KEYDOWN_KEY, listener); }, [ inputText, - onChange, + removeOption, hasFocus, selectedOptions, selectedOptionIndex, @@ -139,7 +160,12 @@ export default function TagInput({ } }} > -
+
{selectedOptions .filter(Boolean) .map(option => @@ -150,10 +176,10 @@ export default function TagInput({ 'whitespace-nowrap', 'px-1.5 py-0.5', 'bg-gray-100 dark:bg-gray-800', + 'active:bg-gray-50 dark:active:bg-gray-900', 'rounded-sm', )} - onClick={() => - onChange?.(selectedOptions.filter(o => o !== option))} + onClick={() => removeOption(option)} > {option} )} @@ -161,7 +187,7 @@ export default function TagInput({ ref={inputRef} type="text" className={clsx( - 'grow !min-w-0 !p-0', + 'grow !min-w-0 !p-0 text-lg', '!border-none !ring-transparent', )} value={inputText} @@ -170,29 +196,32 @@ export default function TagInput({ autoCapitalize="off" readOnly={readOnly} /> +
{hasFocus && optionsFiltered.length > 0 &&
{optionsFiltered.map((option, index) =>
{ addOption(option); inputRef.current?.focus(); diff --git a/src/photo/form/PhotoForm.tsx b/src/photo/form/PhotoForm.tsx index 9101f210..61b78a2d 100644 --- a/src/photo/form/PhotoForm.tsx +++ b/src/photo/form/PhotoForm.tsx @@ -198,9 +198,7 @@ export default function PhotoForm({ > Cancel - + {type === 'create' ? 'Create' : 'Update'}
diff --git a/src/photo/form/index.ts b/src/photo/form/index.ts index 33adf52e..d9c2a762 100644 --- a/src/photo/form/index.ts +++ b/src/photo/form/index.ts @@ -47,7 +47,6 @@ const FORM_METADATA: Record = { title: { label: 'title', capitalize: true }, tags: { label: 'tags', - note: 'comma-separated values', validate: tags => doesTagsStringIncludeFavs(tags) ? `'${TAG_FAVS}' is a reserved tag` : undefined, diff --git a/src/site/globals.css b/src/site/globals.css index 1b8325c6..bf2ff669 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -65,7 +65,7 @@ @apply rounded-md } - input.error, select.error { + .error { @apply border-red-500 dark:border-red-400 } diff --git a/src/tag/FavsTag.tsx b/src/tag/FavsTag.tsx index 3590f015..5496a276 100644 --- a/src/tag/FavsTag.tsx +++ b/src/tag/FavsTag.tsx @@ -2,7 +2,7 @@ import { FaStar } from 'react-icons/fa'; import EntityLink, { EntityLinkExternalProps } from '@/components/EntityLink'; import { TAG_FAVS } from '.'; import { pathForTag } from '@/site/paths'; -import clsx from 'clsx'; +import { clsx } from 'clsx/lite'; export default function FavsTag({ type, From e40b91f0288a9cbb8b1b8daa0b5805ec23c86003 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 17:58:27 -0600 Subject: [PATCH 07/13] Refine custom tag input behavior --- src/components/TagInput.tsx | 83 ++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index c2291547..417ebdd9 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -56,7 +56,8 @@ export default function TagInput({ : option, ] .filter(Boolean) - .map(option => option.toLocaleLowerCase().trim()).join(',')); + .map(parameterize) + .join(',')); setSelectedOptionIndex(undefined); } }, [onChange, selectedOptions]); @@ -110,7 +111,9 @@ export default function TagInput({ break; case 'ArrowDown': setSelectedOptionIndex(i => { - if (i === undefined || i >= optionsFiltered.length - 1) { + if (i === undefined) { + return 1; + } else if (i >= optionsFiltered.length - 1) { return 0; } else { return i + 1; @@ -120,7 +123,8 @@ export default function TagInput({ case 'ArrowUp': setSelectedOptionIndex(i => { if (i === undefined || i === 0) { - return optionsFiltered.length - 1; + inputRef.current?.focus(); + return undefined; } else { return i - 1; } @@ -162,7 +166,8 @@ export default function TagInput({ >
@@ -187,9 +192,10 @@ export default function TagInput({ ref={inputRef} type="text" className={clsx( - 'grow !min-w-0 !p-0 text-lg', + 'grow !min-w-0 !p-0 -my-2 text-xl', '!border-none !ring-transparent', )} + size={12} value={inputText} onChange={e => setInputText(e.target.value)} autoComplete="off" @@ -198,39 +204,40 @@ export default function TagInput({ />
-
- {hasFocus && optionsFiltered.length > 0 && -
- {optionsFiltered.map((option, index) => -
{ - addOption(option); - inputRef.current?.focus(); - setInputText(''); - }} - > - {option} -
)} -
} +
+
0) && 'hidden', + 'control absolute top-0 mt-4 w-full z-10 !px-1.5 !py-1.5', + 'max-h-[7.5rem] overflow-y-auto', + 'flex flex-col gap-y-1', + 'text-xl shadow-lg dark:shadow-xl', + )} + > + {optionsFiltered.map((option, index) => +
{ + addOption(option); + inputRef.current?.focus(); + setInputText(''); + }} + > + {option} +
)} +
); From ef13d52506a6da423ee1c16f5a8a0cdba107f9a5 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 18:07:12 -0600 Subject: [PATCH 08/13] Don't offer to create tag when it exists --- src/components/TagInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 417ebdd9..7ce12036 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -35,6 +35,7 @@ export default function TagInput({ const inputTextFormatted = parameterize(inputText); const isInputTextUnique = inputTextFormatted && + !options.includes(inputTextFormatted) && !selectedOptions.includes(inputTextFormatted); const optionsFiltered = (isInputTextUnique From 559a5c71825af0191505725927f15c489fa6ff72 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 18:09:52 -0600 Subject: [PATCH 09/13] Refine behavior on mobile --- src/components/TagInput.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index 7ce12036..c84002b6 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -59,12 +59,13 @@ export default function TagInput({ .filter(Boolean) .map(parameterize) .join(',')); - setSelectedOptionIndex(undefined); } + setSelectedOptionIndex(undefined); }, [onChange, selectedOptions]); const removeOption = useCallback((option: string) => { onChange?.(selectedOptions.filter(o => o !== option).join(',')); + setSelectedOptionIndex(undefined); }, [onChange, selectedOptions]); // Reset selected option index when focus is lost @@ -178,7 +179,7 @@ export default function TagInput({ setInputText(e.target.value)} autoComplete="off" @@ -221,7 +222,7 @@ export default function TagInput({ key={option} tabIndex={0} className={clsx( - 'cursor-pointer', + 'cursor-pointer select-none', 'px-1 py-1 rounded-sm', index === 0 && selectedOptionIndex === undefined && 'bg-gray-100 dark:bg-gray-800', From 499cf6b4e5613455cfc1db8a86042b1dcab95454 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 18:25:57 -0600 Subject: [PATCH 10/13] Prevent stale tag menu option highlights on mobile --- src/components/TagInput.tsx | 9 ++++++--- tailwind.config.js | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index c84002b6..ce0b4201 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -61,11 +61,13 @@ export default function TagInput({ .join(',')); } setSelectedOptionIndex(undefined); + inputRef.current?.focus(); }, [onChange, selectedOptions]); const removeOption = useCallback((option: string) => { onChange?.(selectedOptions.filter(o => o !== option).join(',')); setSelectedOptionIndex(undefined); + inputRef.current?.focus(); }, [onChange, selectedOptions]); // Reset selected option index when focus is lost @@ -75,10 +77,12 @@ export default function TagInput({ // Focus option in the DOM when selected index changes useEffect(() => { + const options = optionsRef.current?.querySelectorAll('div'); if (selectedOptionIndex !== undefined) { - const options = optionsRef.current?.querySelectorAll('div'); const option = options?.[selectedOptionIndex] as HTMLElement | undefined; option?.focus(); + } else { + inputRef.current?.focus(); } }, [selectedOptionIndex]); @@ -104,7 +108,6 @@ export default function TagInput({ e.preventDefault(); } addOption(optionsFiltered[selectedOptionIndex ?? 0]); - inputRef.current?.focus(); setInputText(''); break; case ',': @@ -138,6 +141,7 @@ export default function TagInput({ } break; case 'Escape': + inputRef.current?.blur(); setHasFocus(false); break; } @@ -233,7 +237,6 @@ export default function TagInput({ )} onClick={() => { addOption(option); - inputRef.current?.focus(); setInputText(''); }} > diff --git a/tailwind.config.js b/tailwind.config.js index f701b311..61b59613 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -37,6 +37,9 @@ module.exports = { }, }, }, + future: { + hoverOnlyWhenSupported: true, + }, plugins: [ require('@tailwindcss/forms'), ], From b45355c951c17749b02c1508874501a4af1d63b8 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 18:38:39 -0600 Subject: [PATCH 11/13] Fix input tag auto-focus behavior --- src/components/TagInput.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index ce0b4201..f3f34039 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -77,12 +77,10 @@ export default function TagInput({ // Focus option in the DOM when selected index changes useEffect(() => { - const options = optionsRef.current?.querySelectorAll('div'); if (selectedOptionIndex !== undefined) { + const options = optionsRef.current?.querySelectorAll('div'); const option = options?.[selectedOptionIndex] as HTMLElement | undefined; option?.focus(); - } else { - inputRef.current?.focus(); } }, [selectedOptionIndex]); @@ -216,7 +214,7 @@ export default function TagInput({ className={clsx( !(hasFocus && optionsFiltered.length > 0) && 'hidden', 'control absolute top-0 mt-4 w-full z-10 !px-1.5 !py-1.5', - 'max-h-[7.5rem] overflow-y-auto', + 'max-h-[8rem] overflow-y-auto', 'flex flex-col gap-y-1', 'text-xl shadow-lg dark:shadow-xl', )} From 376c72ff30e97a0d73f4223867317cc07cc220a1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 18:51:01 -0600 Subject: [PATCH 12/13] Fix safari layout shift --- src/components/TagInput.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/TagInput.tsx b/src/components/TagInput.tsx index f3f34039..b4a69eaa 100644 --- a/src/components/TagInput.tsx +++ b/src/components/TagInput.tsx @@ -65,7 +65,8 @@ export default function TagInput({ }, [onChange, selectedOptions]); const removeOption = useCallback((option: string) => { - onChange?.(selectedOptions.filter(o => o !== option).join(',')); + onChange?.(selectedOptions.filter(o => + o !== parameterize(option)).join(',')); setSelectedOptionIndex(undefined); inputRef.current?.focus(); }, [onChange, selectedOptions]); @@ -159,7 +160,7 @@ export default function TagInput({ return (
setHasFocus(true)} onBlur={e => { if (!e.currentTarget.contains(e.relatedTarget)) { @@ -170,7 +171,7 @@ export default function TagInput({ >
-
+
0) && 'hidden', - 'control absolute top-0 mt-4 w-full z-10 !px-1.5 !py-1.5', + 'control absolute top-0 mt-3 w-full z-10 !px-1.5 !py-1.5', 'max-h-[8rem] overflow-y-auto', 'flex flex-col gap-y-1', 'text-xl shadow-lg dark:shadow-xl', From 654a28c2032bc4abca4e339cbf68f6a24f40bd5b Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 4 Feb 2024 23:43:20 -0600 Subject: [PATCH 13/13] Bump dependencies --- package.json | 26 +- pnpm-lock.yaml | 675 ++++++++++++++++++++++++++----------------------- 2 files changed, 373 insertions(+), 328 deletions(-) diff --git a/package.json b/package.json index 4c92fb0b..e094b6a3 100644 --- a/package.json +++ b/package.json @@ -9,23 +9,23 @@ "analyze": "ANALYZE=true next build" }, "dependencies": { - "@aws-sdk/client-s3": "3.501.0", - "@aws-sdk/s3-request-presigner": "3.501.0", + "@aws-sdk/client-s3": "3.504.0", + "@aws-sdk/s3-request-presigner": "3.504.0", "@headlessui/react": "2.0.0-alpha.4", "@next/bundle-analyzer": "14.1.0", "@tailwindcss/forms": "^0.5.7", - "@testing-library/jest-dom": "^6.3.0", - "@testing-library/react": "^14.1.2", - "@types/jest": "^29.5.11", - "@types/node": "^20.11.8", - "@types/react": "18.2.48", + "@testing-library/jest-dom": "^6.4.1", + "@testing-library/react": "^14.2.1", + "@types/jest": "^29.5.12", + "@types/node": "^20.11.16", + "@types/react": "18.2.53", "@types/react-dom": "18.2.18", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", "@vercel/analytics": "^1.1.2", - "@vercel/blob": "^0.19.0", + "@vercel/blob": "^0.20.0", "@vercel/postgres": "0.7.2", - "@vercel/speed-insights": "^1.0.8", + "@vercel/speed-insights": "^1.0.9", "autoprefixer": "10.4.17", "camelcase-keys": "^9.1.3", "clsx": "^2.1.0", @@ -36,7 +36,7 @@ "framer-motion": "^11.0.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "nanoid": "^5.0.4", + "nanoid": "^5.0.5", "next": "14.1.0", "next-auth": "5.0.0-beta.5", "next-themes": "^0.2.1", @@ -44,7 +44,7 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-icons": "^5.0.1", - "sonner": "^1.3.1", + "sonner": "^1.4.0", "tailwindcss": "3.4.1", "ts-exif-parser": "^0.2.2", "typescript": "5.3.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 416ceb6e..c2db06a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: '@aws-sdk/client-s3': - specifier: 3.501.0 - version: 3.501.0 + specifier: 3.504.0 + version: 3.504.0 '@aws-sdk/s3-request-presigner': - specifier: 3.501.0 - version: 3.501.0 + specifier: 3.504.0 + version: 3.504.0 '@headlessui/react': specifier: 2.0.0-alpha.4 version: 2.0.0-alpha.4(react-dom@18.2.0)(react@18.2.0) @@ -21,41 +21,41 @@ dependencies: specifier: ^0.5.7 version: 0.5.7(tailwindcss@3.4.1) '@testing-library/jest-dom': - specifier: ^6.3.0 - version: 6.3.0(@types/jest@29.5.11)(jest@29.7.0) + specifier: ^6.4.1 + version: 6.4.1(@types/jest@29.5.12)(jest@29.7.0) '@testing-library/react': - specifier: ^14.1.2 - version: 14.1.2(react-dom@18.2.0)(react@18.2.0) + specifier: ^14.2.1 + version: 14.2.1(react-dom@18.2.0)(react@18.2.0) '@types/jest': - specifier: ^29.5.11 - version: 29.5.11 + specifier: ^29.5.12 + version: 29.5.12 '@types/node': - specifier: ^20.11.8 - version: 20.11.8 + specifier: ^20.11.16 + version: 20.11.16 '@types/react': - specifier: 18.2.48 - version: 18.2.48 + specifier: 18.2.53 + version: 18.2.53 '@types/react-dom': specifier: 18.2.18 version: 18.2.18 '@typescript-eslint/eslint-plugin': - specifier: ^6.19.1 - version: 6.19.1(@typescript-eslint/parser@6.19.1)(eslint@8.56.0)(typescript@5.3.3) + specifier: ^6.20.0 + version: 6.20.0(@typescript-eslint/parser@6.20.0)(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/parser': - specifier: ^6.19.1 - version: 6.19.1(eslint@8.56.0)(typescript@5.3.3) + specifier: ^6.20.0 + version: 6.20.0(eslint@8.56.0)(typescript@5.3.3) '@vercel/analytics': specifier: ^1.1.2 version: 1.1.2 '@vercel/blob': - specifier: ^0.19.0 - version: 0.19.0 + specifier: ^0.20.0 + version: 0.20.0 '@vercel/postgres': specifier: 0.7.2 version: 0.7.2 '@vercel/speed-insights': - specifier: ^1.0.8 - version: 1.0.8(next@14.1.0)(react@18.2.0) + specifier: ^1.0.9 + version: 1.0.9(next@14.1.0)(react@18.2.0) autoprefixer: specifier: 10.4.17 version: 10.4.17(postcss@8.4.33) @@ -82,13 +82,13 @@ dependencies: version: 11.0.3(react-dom@18.2.0)(react@18.2.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.11.8) + version: 29.7.0(@types/node@20.11.16) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 nanoid: - specifier: ^5.0.4 - version: 5.0.4 + specifier: ^5.0.5 + version: 5.0.5 next: specifier: 14.1.0 version: 14.1.0(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) @@ -111,8 +111,8 @@ dependencies: specifier: ^5.0.1 version: 5.0.1(react@18.2.0) sonner: - specifier: ^1.3.1 - version: 1.3.1(react-dom@18.2.0)(react@18.2.0) + specifier: ^1.4.0 + version: 1.4.0(react-dom@18.2.0)(react@18.2.0) tailwindcss: specifier: 3.4.1 version: 3.4.1 @@ -168,7 +168,7 @@ packages: resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 tslib: 1.14.1 dev: false @@ -176,7 +176,7 @@ packages: resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==} dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 tslib: 1.14.1 dev: false @@ -192,7 +192,7 @@ packages: '@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.496.0 + '@aws-sdk/types': 3.502.0 '@aws-sdk/util-locate-window': 3.465.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -205,7 +205,7 @@ packages: '@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.496.0 + '@aws-sdk/types': 3.502.0 '@aws-sdk/util-locate-window': 3.465.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -215,7 +215,7 @@ packages: resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 tslib: 1.14.1 dev: false @@ -228,38 +228,38 @@ packages: /@aws-crypto/util@3.0.0: resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 dev: false - /@aws-sdk/client-s3@3.501.0: - resolution: {integrity: sha512-ovxYSGdnEdr4UrNiT+9e3ov2XULFr0bcyoXJkYxnkXPDg9Y65nuZgZAIZQMS6wnJVmNrUprhqTSQB3KHXvaEuQ==} + /@aws-sdk/client-s3@3.504.0: + resolution: {integrity: sha512-J8xPsnk7EDwalFSaDxPFNT2+x99nG2uQTpsLXAV3bWbT1nD/JZ+fase9GqxM11v6WngzqRvTQg26ljMn5hQSKA==} engines: {node: '>=14.0.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.501.0 + '@aws-sdk/client-sts': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) '@aws-sdk/core': 3.496.0 - '@aws-sdk/credential-provider-node': 3.501.0 - '@aws-sdk/middleware-bucket-endpoint': 3.496.0 - '@aws-sdk/middleware-expect-continue': 3.496.0 - '@aws-sdk/middleware-flexible-checksums': 3.496.0 - '@aws-sdk/middleware-host-header': 3.496.0 - '@aws-sdk/middleware-location-constraint': 3.496.0 - '@aws-sdk/middleware-logger': 3.496.0 - '@aws-sdk/middleware-recursion-detection': 3.496.0 - '@aws-sdk/middleware-sdk-s3': 3.499.0 - '@aws-sdk/middleware-signing': 3.496.0 - '@aws-sdk/middleware-ssec': 3.498.0 - '@aws-sdk/middleware-user-agent': 3.496.0 - '@aws-sdk/region-config-resolver': 3.496.0 - '@aws-sdk/signature-v4-multi-region': 3.499.0 - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-endpoints': 3.496.0 - '@aws-sdk/util-user-agent-browser': 3.496.0 - '@aws-sdk/util-user-agent-node': 3.496.0 + '@aws-sdk/credential-provider-node': 3.504.0 + '@aws-sdk/middleware-bucket-endpoint': 3.502.0 + '@aws-sdk/middleware-expect-continue': 3.502.0 + '@aws-sdk/middleware-flexible-checksums': 3.502.0 + '@aws-sdk/middleware-host-header': 3.502.0 + '@aws-sdk/middleware-location-constraint': 3.502.0 + '@aws-sdk/middleware-logger': 3.502.0 + '@aws-sdk/middleware-recursion-detection': 3.502.0 + '@aws-sdk/middleware-sdk-s3': 3.502.0 + '@aws-sdk/middleware-signing': 3.502.0 + '@aws-sdk/middleware-ssec': 3.502.0 + '@aws-sdk/middleware-user-agent': 3.502.0 + '@aws-sdk/region-config-resolver': 3.502.0 + '@aws-sdk/signature-v4-multi-region': 3.502.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-endpoints': 3.502.0 + '@aws-sdk/util-user-agent-browser': 3.502.0 + '@aws-sdk/util-user-agent-node': 3.502.0 '@aws-sdk/xml-builder': 3.496.0 '@smithy/config-resolver': 2.1.1 '@smithy/core': 1.3.1 @@ -299,22 +299,27 @@ packages: - aws-crt dev: false - /@aws-sdk/client-sso@3.496.0: - resolution: {integrity: sha512-fuaMuxKg7CMUsP9l3kxYWCOxFsBjdA0xj5nlikaDm1661/gB4KkAiGqRY8LsQkpNXvXU8Nj+f7oCFADFyGYzyw==} + /@aws-sdk/client-sso-oidc@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-ODA33/nm2srhV08EW0KZAP577UgV0qjyr7Xp2yEo8MXWL4ZqQZprk1c+QKBhjr4Djesrm0VPmSD/np0mtYP68A==} engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/credential-provider-node': ^3.504.0 dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/client-sts': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) '@aws-sdk/core': 3.496.0 - '@aws-sdk/middleware-host-header': 3.496.0 - '@aws-sdk/middleware-logger': 3.496.0 - '@aws-sdk/middleware-recursion-detection': 3.496.0 - '@aws-sdk/middleware-user-agent': 3.496.0 - '@aws-sdk/region-config-resolver': 3.496.0 - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-endpoints': 3.496.0 - '@aws-sdk/util-user-agent-browser': 3.496.0 - '@aws-sdk/util-user-agent-node': 3.496.0 + '@aws-sdk/credential-provider-node': 3.504.0 + '@aws-sdk/middleware-host-header': 3.502.0 + '@aws-sdk/middleware-logger': 3.502.0 + '@aws-sdk/middleware-recursion-detection': 3.502.0 + '@aws-sdk/middleware-signing': 3.502.0 + '@aws-sdk/middleware-user-agent': 3.502.0 + '@aws-sdk/region-config-resolver': 3.502.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-endpoints': 3.502.0 + '@aws-sdk/util-user-agent-browser': 3.502.0 + '@aws-sdk/util-user-agent-node': 3.502.0 '@smithy/config-resolver': 2.1.1 '@smithy/core': 1.3.1 '@smithy/fetch-http-handler': 2.4.1 @@ -344,23 +349,70 @@ packages: - aws-crt dev: false - /@aws-sdk/client-sts@3.501.0: - resolution: {integrity: sha512-Uwc/xuxsA46dZS5s+4U703LBNDrGpWF7RB4XYEEMD21BLfGuqntxLLQux8xxKt3Pcur0CsXNja5jXt3uLnE5MA==} + /@aws-sdk/client-sso@3.502.0: + resolution: {integrity: sha512-OZAYal1+PQgUUtWiHhRayDtX0OD+XpXHKAhjYgEIPbyhQaCMp3/Bq1xDX151piWXvXqXLJHFKb8DUEqzwGO9QA==} engines: {node: '>=14.0.0'} dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/core': 3.496.0 - '@aws-sdk/credential-provider-node': 3.501.0 - '@aws-sdk/middleware-host-header': 3.496.0 - '@aws-sdk/middleware-logger': 3.496.0 - '@aws-sdk/middleware-recursion-detection': 3.496.0 - '@aws-sdk/middleware-user-agent': 3.496.0 - '@aws-sdk/region-config-resolver': 3.496.0 - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-endpoints': 3.496.0 - '@aws-sdk/util-user-agent-browser': 3.496.0 - '@aws-sdk/util-user-agent-node': 3.496.0 + '@aws-sdk/middleware-host-header': 3.502.0 + '@aws-sdk/middleware-logger': 3.502.0 + '@aws-sdk/middleware-recursion-detection': 3.502.0 + '@aws-sdk/middleware-user-agent': 3.502.0 + '@aws-sdk/region-config-resolver': 3.502.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-endpoints': 3.502.0 + '@aws-sdk/util-user-agent-browser': 3.502.0 + '@aws-sdk/util-user-agent-node': 3.502.0 + '@smithy/config-resolver': 2.1.1 + '@smithy/core': 1.3.1 + '@smithy/fetch-http-handler': 2.4.1 + '@smithy/hash-node': 2.1.1 + '@smithy/invalid-dependency': 2.1.1 + '@smithy/middleware-content-length': 2.1.1 + '@smithy/middleware-endpoint': 2.4.1 + '@smithy/middleware-retry': 2.1.1 + '@smithy/middleware-serde': 2.1.1 + '@smithy/middleware-stack': 2.1.1 + '@smithy/node-config-provider': 2.2.1 + '@smithy/node-http-handler': 2.3.1 + '@smithy/protocol-http': 3.1.1 + '@smithy/smithy-client': 2.3.1 + '@smithy/types': 2.9.1 + '@smithy/url-parser': 2.1.1 + '@smithy/util-base64': 2.1.1 + '@smithy/util-body-length-browser': 2.1.1 + '@smithy/util-body-length-node': 2.2.1 + '@smithy/util-defaults-mode-browser': 2.1.1 + '@smithy/util-defaults-mode-node': 2.1.1 + '@smithy/util-endpoints': 1.1.1 + '@smithy/util-retry': 2.1.1 + '@smithy/util-utf8': 2.1.1 + tslib: 2.6.2 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/client-sts@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-IESs8FkL7B/uY+ml4wgoRkrr6xYo4PizcNw6JX17eveq1gRBCPKeGMjE6HTDOcIYZZ8rqz/UeuH3JD4UhrMOnA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/credential-provider-node': ^3.504.0 + dependencies: + '@aws-crypto/sha256-browser': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-sdk/core': 3.496.0 + '@aws-sdk/credential-provider-node': 3.504.0 + '@aws-sdk/middleware-host-header': 3.502.0 + '@aws-sdk/middleware-logger': 3.502.0 + '@aws-sdk/middleware-recursion-detection': 3.502.0 + '@aws-sdk/middleware-user-agent': 3.502.0 + '@aws-sdk/region-config-resolver': 3.502.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-endpoints': 3.502.0 + '@aws-sdk/util-user-agent-browser': 3.502.0 + '@aws-sdk/util-user-agent-node': 3.502.0 '@smithy/config-resolver': 2.1.1 '@smithy/core': 1.3.1 '@smithy/fetch-http-handler': 2.4.1 @@ -404,25 +456,62 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/credential-provider-env@3.496.0: - resolution: {integrity: sha512-lukQMJ8SWWP5RqkRNOHi/H+WMhRvSWa3Fc5Jf/VP6xHiPLfF1XafcvthtV91e0VwPCiseI+HqChrcGq8pvnxHw==} + /@aws-sdk/credential-provider-env@3.502.0: + resolution: {integrity: sha512-KIB8Ae1Z7domMU/jU4KiIgK4tmYgvuXlhR54ehwlVHxnEoFPoPuGHFZU7oFn79jhhSLUFQ1lRYMxP0cEwb7XeQ==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/property-provider': 2.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/credential-provider-ini@3.501.0: - resolution: {integrity: sha512-6UXnwLtYIr298ljveumCVXsH+x7csGscK5ylY+veRFy514NqyloRdJt8JY26hhh5SF9MYnkW+JyWSJ2Ls3tOjQ==} + /@aws-sdk/credential-provider-http@3.503.1: + resolution: {integrity: sha512-rTdlFFGoPPFMF2YjtlfRuSgKI+XsF49u7d98255hySwhsbwd3Xp+utTTPquxP+CwDxMHbDlI7NxDzFiFdsoZug==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/credential-provider-env': 3.496.0 - '@aws-sdk/credential-provider-process': 3.496.0 - '@aws-sdk/credential-provider-sso': 3.501.0 - '@aws-sdk/credential-provider-web-identity': 3.496.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 + '@smithy/fetch-http-handler': 2.4.1 + '@smithy/node-http-handler': 2.3.1 + '@smithy/property-provider': 2.1.1 + '@smithy/protocol-http': 3.1.1 + '@smithy/smithy-client': 2.3.1 + '@smithy/types': 2.9.1 + '@smithy/util-stream': 2.1.1 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-ini@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-ODICLXfr8xTUd3wweprH32Ge41yuBa+u3j0JUcLdTUO1N9ldczSMdo8zOPlP0z4doqD3xbnqMkjNQWgN/Q+5oQ==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sts': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/credential-provider-env': 3.502.0 + '@aws-sdk/credential-provider-process': 3.502.0 + '@aws-sdk/credential-provider-sso': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/credential-provider-web-identity': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/types': 3.502.0 + '@smithy/credential-provider-imds': 2.2.1 + '@smithy/property-provider': 2.1.1 + '@smithy/shared-ini-file-loader': 2.3.1 + '@smithy/types': 2.9.1 + tslib: 2.6.2 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' + - aws-crt + dev: false + + /@aws-sdk/credential-provider-node@3.504.0: + resolution: {integrity: sha512-6+V5hIh+tILmUjf2ZQWQINR3atxQVgH/bFrGdSR/sHSp/tEgw3m0xWL3IRslWU1e4/GtXrfg1iYnMknXy68Ikw==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/credential-provider-env': 3.502.0 + '@aws-sdk/credential-provider-http': 3.503.1 + '@aws-sdk/credential-provider-ini': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/credential-provider-process': 3.502.0 + '@aws-sdk/credential-provider-sso': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/credential-provider-web-identity': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/types': 3.502.0 '@smithy/credential-provider-imds': 2.2.1 '@smithy/property-provider': 2.1.1 '@smithy/shared-ini-file-loader': 2.3.1 @@ -432,66 +521,52 @@ packages: - aws-crt dev: false - /@aws-sdk/credential-provider-node@3.501.0: - resolution: {integrity: sha512-NM62D8gYrQ1nyLYwW4k48B2/lMHDzHDcQccS1wJakr6bg5sdtG06CumwlVcY+LAa0o1xRnhHmh/yiwj/nN4avw==} + /@aws-sdk/credential-provider-process@3.502.0: + resolution: {integrity: sha512-fJJowOjQ4infYQX0E1J3xFVlmuwEYJAFk0Mo1qwafWmEthsBJs+6BR2RiWDELHKrSK35u4Pf3fu3RkYuCtmQFw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/credential-provider-env': 3.496.0 - '@aws-sdk/credential-provider-ini': 3.501.0 - '@aws-sdk/credential-provider-process': 3.496.0 - '@aws-sdk/credential-provider-sso': 3.501.0 - '@aws-sdk/credential-provider-web-identity': 3.496.0 - '@aws-sdk/types': 3.496.0 - '@smithy/credential-provider-imds': 2.2.1 + '@aws-sdk/types': 3.502.0 + '@smithy/property-provider': 2.1.1 + '@smithy/shared-ini-file-loader': 2.3.1 + '@smithy/types': 2.9.1 + tslib: 2.6.2 + dev: false + + /@aws-sdk/credential-provider-sso@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-4MgH2or2SjPzaxM08DCW+BjaX4DSsEGJlicHKmz6fh+w9JmLh750oXcTnbvgUeVz075jcs6qTKjvUcsdGM/t8Q==} + engines: {node: '>=14.0.0'} + dependencies: + '@aws-sdk/client-sso': 3.502.0 + '@aws-sdk/token-providers': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/types': 3.502.0 '@smithy/property-provider': 2.1.1 '@smithy/shared-ini-file-loader': 2.3.1 '@smithy/types': 2.9.1 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' - aws-crt dev: false - /@aws-sdk/credential-provider-process@3.496.0: - resolution: {integrity: sha512-/YZscCTGOKVmGr916Th4XF8Sz6JDtZ/n2loHG9exok9iy/qIbACsTRNLP9zexPxhPoue/oZqecY5xbVljfY34A==} + /@aws-sdk/credential-provider-web-identity@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-L1ljCvGpIEFdJk087ijf2ohg7HBclOeB1UgBxUBBzf4iPRZTQzd2chGaKj0hm2VVaXz7nglswJeURH5PFcS5oA==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/client-sts': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/types': 3.502.0 '@smithy/property-provider': 2.1.1 - '@smithy/shared-ini-file-loader': 2.3.1 - '@smithy/types': 2.9.1 - tslib: 2.6.2 - dev: false - - /@aws-sdk/credential-provider-sso@3.501.0: - resolution: {integrity: sha512-y90dlvvZ55PwecODFdMx0NiNlJJfm7X6S61PKdLNCMRcu1YK+eWn0CmPHGHobBUQ4SEYhnFLcHSsf+VMim6BtQ==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/client-sso': 3.496.0 - '@aws-sdk/token-providers': 3.501.0 - '@aws-sdk/types': 3.496.0 - '@smithy/property-provider': 2.1.1 - '@smithy/shared-ini-file-loader': 2.3.1 '@smithy/types': 2.9.1 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' - aws-crt dev: false - /@aws-sdk/credential-provider-web-identity@3.496.0: - resolution: {integrity: sha512-IbP+qLlvJSpNPj+zW6TtFuLRTK5Tf0hW+2pom4vFyi5YSH4pn8UOC136UdewX8vhXGS9BJQ5zBDMasIyl5VeGQ==} + /@aws-sdk/middleware-bucket-endpoint@3.502.0: + resolution: {integrity: sha512-mUSP2DUcjhO5zM2b21CvZ9AqwI8DaAeZA6NYHOxWGTV9BUxHcdGWXEjDkcVj9CQ0gvNwTtw6B5L/q52rVAnZbw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 - '@smithy/property-provider': 2.1.1 - '@smithy/types': 2.9.1 - tslib: 2.6.2 - dev: false - - /@aws-sdk/middleware-bucket-endpoint@3.496.0: - resolution: {integrity: sha512-B+ilBMSs3+LJuo2bl2KB8GFdu+8PPVtYEWtwhNkmnaU8iMisgMBp5uuM8sUDvJX7I4iSF0WbgnhguX4cJqfAew==} - engines: {node: '>=14.0.0'} - dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@aws-sdk/util-arn-parser': 3.495.0 '@smithy/node-config-provider': 2.2.1 '@smithy/protocol-http': 3.1.1 @@ -500,23 +575,23 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/middleware-expect-continue@3.496.0: - resolution: {integrity: sha512-+exo5DVc+BeDus2iI6Fz1thefHGDXxUhHZ+4VHQ6HkStMy3Y22HugyEGHSQZmtRL86Hjr7dFbEWFsC47a2ItGA==} + /@aws-sdk/middleware-expect-continue@3.502.0: + resolution: {integrity: sha512-DxfAuBVuPSt8as9xP57o8ks6ySVSjwO2NNNAdpLwk4KhEAPYEpHlf2yWYorYLrS+dDmwfYgOhRNoguuBdCu6ow==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/protocol-http': 3.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-flexible-checksums@3.496.0: - resolution: {integrity: sha512-yQIWfjEMvgsAJ7ku224vXDjXPD+f9zfKZFialJva8VUlEr7hQp4CQ0rxV3YThSaixKEDDs5k6kOjWAd2BPGr2A==} + /@aws-sdk/middleware-flexible-checksums@3.502.0: + resolution: {integrity: sha512-kCt2zQDFumz/LnJJJOSd2GW4dr8oT8YMJKgxC/pph3aRXoSHXRwhrMbFnQ8swEE9vjywxtcED8sym0b0tNhhoA==} engines: {node: '>=14.0.0'} dependencies: '@aws-crypto/crc32': 3.0.0 '@aws-crypto/crc32c': 3.0.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/is-array-buffer': 2.1.1 '@smithy/protocol-http': 3.1.1 '@smithy/types': 2.9.1 @@ -524,49 +599,49 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/middleware-host-header@3.496.0: - resolution: {integrity: sha512-jUdPpSJeqCYXf6hSjfwsfHway7peIV8Vz51w/BN91bF4vB/bYwAC5o9/iJiK/EoByp5asxA8fg9wFOyGjzdbLg==} + /@aws-sdk/middleware-host-header@3.502.0: + resolution: {integrity: sha512-EjnG0GTYXT/wJBmm5/mTjDcAkzU8L7wQjOzd3FTXuTCNNyvAvwrszbOj5FlarEw5XJBbQiZtBs+I5u9+zy560w==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/protocol-http': 3.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-location-constraint@3.496.0: - resolution: {integrity: sha512-i4ocJ2Zs86OtPREbB18InFukhqg2qtBxb5gywv79IHDPVmpOYE4m/3v3yGUrkjfF2GTlUL0k5FskNNqw41yfng==} + /@aws-sdk/middleware-location-constraint@3.502.0: + resolution: {integrity: sha512-fLRwPuTZvEWQkPjys03m3D6tYN4kf7zU6+c8mJxwvEg+yfBuv2RBsbd+Vn2bTisUjXvIg1kyBzONlpHoIyFneg==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-logger@3.496.0: - resolution: {integrity: sha512-EwMVSY6iBMeGbVnvwdaFl/ClMS/YWtxCAo+bcEtgk8ltRuo7qgbJem8Km/fvWC1vdWvIbe4ArdJ8iGzq62ffAw==} + /@aws-sdk/middleware-logger@3.502.0: + resolution: {integrity: sha512-FDyv6K4nCoHxbjLGS2H8ex8I0KDIiu4FJgVRPs140ZJy6gE5Pwxzv6YTzZGLMrnqcIs9gh065Lf6DjwMelZqaw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-recursion-detection@3.496.0: - resolution: {integrity: sha512-+IuOcFsfqg2WAnaEzH6KhVbicqCxtOq9w3DH2jwTpddRlCx2Kqf6wCzg8luhHRGyjBZdsbIS+OXwyMevoppawA==} + /@aws-sdk/middleware-recursion-detection@3.502.0: + resolution: {integrity: sha512-hvbyGJbxeuezxOu8VfFmcV4ql1hKXLxHTe5FNYfEBat2KaZXVhc1Hg+4TvB06/53p+E8J99Afmumkqbxs2esUA==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/protocol-http': 3.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-sdk-s3@3.499.0: - resolution: {integrity: sha512-thTb47U1hYHk5ei+yO0D0aehbgQXeAcgvyyxOID9/HDuRfWuTvKdclWh/goIeDfvSS87VBukEAjnCa5JYBwzug==} + /@aws-sdk/middleware-sdk-s3@3.502.0: + resolution: {integrity: sha512-GbGugrfyL5bNA/zw8iQll92yXBONfWSC8Ns00DtkOU1saPXp4/7WHtyyZGYdvPa73T1IsuZy9egpoYRBmRcd5Q==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@aws-sdk/util-arn-parser': 3.495.0 '@smithy/node-config-provider': 2.2.1 '@smithy/protocol-http': 3.1.1 @@ -577,11 +652,11 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/middleware-signing@3.496.0: - resolution: {integrity: sha512-Oq73Brs4IConvWnRlh8jM1V7LHoTw9SVQklu/QW2FPlNrB3B8fuTdWHHYIWv7ybw1bykXoCY99v865Mmq/Or/g==} + /@aws-sdk/middleware-signing@3.502.0: + resolution: {integrity: sha512-4hF08vSzJ7L6sB+393gOFj3s2N6nLusYS0XrMW6wYNFU10IDdbf8Z3TZ7gysDJJHEGQPmTAesPEDBsasGWcMxg==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/property-provider': 2.1.1 '@smithy/protocol-http': 3.1.1 '@smithy/signature-v4': 2.1.1 @@ -590,31 +665,31 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/middleware-ssec@3.498.0: - resolution: {integrity: sha512-sWujXgzeTqMZzj/pRYEnnEbSzhBosqw9DXHOY1Mg2igI9NEfGlB7lPARp6aKmCaYlP3Bcj2X86vKCqF53mbyig==} + /@aws-sdk/middleware-ssec@3.502.0: + resolution: {integrity: sha512-1nidVTIba6/aVjjzD/WNqWdzSyTrXOHO3Ddz2MGD8S1yGSrYz4iYaq4Bm/uosfdr8B1L0Ws0pjdRXrNfzSw/DQ==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/middleware-user-agent@3.496.0: - resolution: {integrity: sha512-+iMtRxFk0GmFWNUF4ilxylOQd9PZdR4ZC9jkcPIh1PZlvKtpCyFywKlk5RRZKklSoJ/CttcqwhMvOXTNbWm/0w==} + /@aws-sdk/middleware-user-agent@3.502.0: + resolution: {integrity: sha512-TxbBZbRiXPH0AUxegqiNd9aM9zNSbfjtBs5MEfcBsweeT/B2O7K1EjP9+CkB8Xmk/5FLKhAKLr19b1TNoE27rw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-endpoints': 3.496.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-endpoints': 3.502.0 '@smithy/protocol-http': 3.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/region-config-resolver@3.496.0: - resolution: {integrity: sha512-URrNVOPHPgEDm6QFu6lDC2cUFs+Jx23mA3jEwCvoKlXiEY/ZoWjH8wlX3OMUlLrF1qoUTuD03jjrJzF6zoCgug==} + /@aws-sdk/region-config-resolver@3.502.0: + resolution: {integrity: sha512-mxmsX2AGgnSM+Sah7mcQCIneOsJQNiLX0COwEttuf8eO+6cLMAZvVudH3BnWTfea4/A9nuri9DLCqBvEmPrilg==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/node-config-provider': 2.2.1 '@smithy/types': 2.9.1 '@smithy/util-config-provider': 2.2.1 @@ -622,13 +697,13 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/s3-request-presigner@3.501.0: - resolution: {integrity: sha512-n5PF9viJEVVG2C+QIM749oCTWRXe8GzDaooJJPffN8Di1Lsqmm00V4R00AJStkt0SQaJ4tr/YPVC2zHgKfK+qg==} + /@aws-sdk/s3-request-presigner@3.504.0: + resolution: {integrity: sha512-5FxVdRufiFLSUDJ/Qul5JFPHjhFFzo+C6u53bzbi7gaSshA6lLLhJ9KbVk2LmKE1mTR+nh2+JebI6y+3njtkzw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/signature-v4-multi-region': 3.499.0 - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-format-url': 3.496.0 + '@aws-sdk/signature-v4-multi-region': 3.502.0 + '@aws-sdk/types': 3.502.0 + '@aws-sdk/util-format-url': 3.502.0 '@smithy/middleware-endpoint': 2.4.1 '@smithy/protocol-http': 3.1.1 '@smithy/smithy-client': 2.3.1 @@ -636,65 +711,35 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/signature-v4-multi-region@3.499.0: - resolution: {integrity: sha512-8HSFnZErRm7lAfk+Epxrf4QNdQEamg1CnbLybtKQQEjmvxLuXYvj16KlpYEZIwEENOMEvnCqMc7syTPkmjVhJA==} + /@aws-sdk/signature-v4-multi-region@3.502.0: + resolution: {integrity: sha512-NpOXtUXH0ZAgnyI3Y3s2fPrgwbsWoNMwdoXdFZvH0eDzzX80tim7Yuy6dzVA5zrxSzOYs1xjcOhM+4CmM0QZiw==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/middleware-sdk-s3': 3.499.0 - '@aws-sdk/types': 3.496.0 + '@aws-sdk/middleware-sdk-s3': 3.502.0 + '@aws-sdk/types': 3.502.0 '@smithy/protocol-http': 3.1.1 '@smithy/signature-v4': 2.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 dev: false - /@aws-sdk/token-providers@3.501.0: - resolution: {integrity: sha512-MvLPhNxlStmQqVm2crGLUqYWvK/AbMmI9j4FbEfJ15oG/I+730zjSJQEy2MvdiqbJRDPZ/tRCL89bUedOrmi0g==} + /@aws-sdk/token-providers@3.504.0(@aws-sdk/credential-provider-node@3.504.0): + resolution: {integrity: sha512-YIJWWsZi2ClUiILS1uh5L6VjmCUSTI6KKMuL9DkGjYqJ0aI6M8bd8fT9Wm7QmXCyjcArTgr/Atkhia4T7oKvzQ==} engines: {node: '>=14.0.0'} dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/middleware-host-header': 3.496.0 - '@aws-sdk/middleware-logger': 3.496.0 - '@aws-sdk/middleware-recursion-detection': 3.496.0 - '@aws-sdk/middleware-user-agent': 3.496.0 - '@aws-sdk/region-config-resolver': 3.496.0 - '@aws-sdk/types': 3.496.0 - '@aws-sdk/util-endpoints': 3.496.0 - '@aws-sdk/util-user-agent-browser': 3.496.0 - '@aws-sdk/util-user-agent-node': 3.496.0 - '@smithy/config-resolver': 2.1.1 - '@smithy/fetch-http-handler': 2.4.1 - '@smithy/hash-node': 2.1.1 - '@smithy/invalid-dependency': 2.1.1 - '@smithy/middleware-content-length': 2.1.1 - '@smithy/middleware-endpoint': 2.4.1 - '@smithy/middleware-retry': 2.1.1 - '@smithy/middleware-serde': 2.1.1 - '@smithy/middleware-stack': 2.1.1 - '@smithy/node-config-provider': 2.2.1 - '@smithy/node-http-handler': 2.3.1 + '@aws-sdk/client-sso-oidc': 3.504.0(@aws-sdk/credential-provider-node@3.504.0) + '@aws-sdk/types': 3.502.0 '@smithy/property-provider': 2.1.1 - '@smithy/protocol-http': 3.1.1 '@smithy/shared-ini-file-loader': 2.3.1 - '@smithy/smithy-client': 2.3.1 '@smithy/types': 2.9.1 - '@smithy/url-parser': 2.1.1 - '@smithy/util-base64': 2.1.1 - '@smithy/util-body-length-browser': 2.1.1 - '@smithy/util-body-length-node': 2.2.1 - '@smithy/util-defaults-mode-browser': 2.1.1 - '@smithy/util-defaults-mode-node': 2.1.1 - '@smithy/util-endpoints': 1.1.1 - '@smithy/util-retry': 2.1.1 - '@smithy/util-utf8': 2.1.1 tslib: 2.6.2 transitivePeerDependencies: + - '@aws-sdk/credential-provider-node' - aws-crt dev: false - /@aws-sdk/types@3.496.0: - resolution: {integrity: sha512-umkGadK4QuNQaMoDICMm7NKRI/mYSXiyPjcn3d53BhsuArYU/52CebGQKdt4At7SwwsiVJZw9RNBHyN5Mm0HVw==} + /@aws-sdk/types@3.502.0: + resolution: {integrity: sha512-M0DSPYe/gXhwD2QHgoukaZv5oDxhW3FfvYIrJptyqUq3OnPJBcDbihHjrE0PBtfh/9kgMZT60/fQ2NVFANfa2g==} engines: {node: '>=14.0.0'} dependencies: '@smithy/types': 2.9.1 @@ -708,21 +753,21 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/util-endpoints@3.496.0: - resolution: {integrity: sha512-1QzOiWHi383ZwqSi/R2KgKCd7M+6DxkxI5acqLPm8mvDRDP2jRjrnVaC0g9/tlttWousGEemDUWStwrD2mVYSw==} + /@aws-sdk/util-endpoints@3.502.0: + resolution: {integrity: sha512-6LKFlJPp2J24r1Kpfoz5ESQn+1v5fEjDB3mtUKRdpwarhm3syu7HbKlHCF3KbcCOyahobvLvhoedT78rJFEeeg==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/types': 2.9.1 '@smithy/util-endpoints': 1.1.1 tslib: 2.6.2 dev: false - /@aws-sdk/util-format-url@3.496.0: - resolution: {integrity: sha512-GYRqLEUVoIkD8+ULliODFWWRHGyjlanLCnj8faahZXUke6Ey32MG40RgPTu/2eFkUyS6U7sVdt7oLY8MIHShPQ==} + /@aws-sdk/util-format-url@3.502.0: + resolution: {integrity: sha512-4+0zBD0ZIJqtTzSE6VRruRwUx3lG+is8Egv+LN99X5y7i6OdrS9ePYHbCJ9FxkzTThgbkUq6k2W7psEDYvn4VA==} engines: {node: '>=14.0.0'} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/querystring-builder': 2.1.1 '@smithy/types': 2.9.1 tslib: 2.6.2 @@ -735,17 +780,17 @@ packages: tslib: 2.6.2 dev: false - /@aws-sdk/util-user-agent-browser@3.496.0: - resolution: {integrity: sha512-4j2spN+h0I0qfSMsGvJXTfQBu1e18rPdekKvzsGJxhaAE1tNgUfUT4nbvc5uVn0sNjZmirskmJ3kfbzVOrqIFg==} + /@aws-sdk/util-user-agent-browser@3.502.0: + resolution: {integrity: sha512-v8gKyCs2obXoIkLETAeEQ3AM+QmhHhst9xbM1cJtKUGsRlVIak/XyyD+kVE6kmMm1cjfudHpHKABWk9apQcIZQ==} dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/types': 2.9.1 bowser: 2.11.0 tslib: 2.6.2 dev: false - /@aws-sdk/util-user-agent-node@3.496.0: - resolution: {integrity: sha512-h0Ax0jlDc7UIo3KoSI4C4tVLBFoiAdx3+DhTVfgLS7x93d41dMlziPoBX2RgdcFn37qnzw6AQKTVTMwDbRCGpg==} + /@aws-sdk/util-user-agent-node@3.502.0: + resolution: {integrity: sha512-9RjxpkGZKbTdl96tIJvAo+vZoz4P/cQh36SBUt9xfRfW0BtsaLyvSrvlR5wyUYhvRcC12Axqh/8JtnAPq//+Vw==} engines: {node: '>=14.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -753,7 +798,7 @@ packages: aws-crt: optional: true dependencies: - '@aws-sdk/types': 3.496.0 + '@aws-sdk/types': 3.502.0 '@smithy/node-config-provider': 2.2.1 '@smithy/types': 2.9.1 tslib: 2.6.2 @@ -1279,7 +1324,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1300,14 +1345,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.11.8) + jest-config: 29.7.0(@types/node@20.11.16) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1335,7 +1380,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-mock: 29.7.0 dev: false @@ -1362,7 +1407,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -1395,7 +1440,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.20 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -1483,7 +1528,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.11.8 + '@types/node': 20.11.16 '@types/yargs': 17.0.32 chalk: 4.1.2 dev: false @@ -2294,8 +2339,8 @@ packages: pretty-format: 27.5.1 dev: false - /@testing-library/jest-dom@6.3.0(@types/jest@29.5.11)(jest@29.7.0): - resolution: {integrity: sha512-hJVIrkFizEQxoWsGBlycTcQhrpoCH4DhXfrnHFFXgkx3Xdm15zycsq5Ep+vpw4W8S0NJa8cxDHcuJib+1tEbhg==} + /@testing-library/jest-dom@6.4.1(@types/jest@29.5.12)(jest@29.7.0): + resolution: {integrity: sha512-Z7qMM3J2Zw5H/nC2/5CYx5YcuaD56JmDFKNIozZ89VIo6o6Y9FMhssics4e2madEKYDNEpZz3+glPGz0yWMOag==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} peerDependencies: '@jest/globals': '>= 28' @@ -2317,18 +2362,18 @@ packages: dependencies: '@adobe/css-tools': 4.3.2 '@babel/runtime': 7.23.7 - '@types/jest': 29.5.11 + '@types/jest': 29.5.12 aria-query: 5.3.0 chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - jest: 29.7.0(@types/node@20.11.8) + jest: 29.7.0(@types/node@20.11.16) lodash: 4.17.21 redent: 3.0.0 dev: false - /@testing-library/react@14.1.2(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-z4p7DVBTPjKM5qDZ0t5ZjzkpSNb+fZy1u6bzO7kk8oeGagpPCAtgh4cx1syrfp7a+QWkM021jGqjJaxJJnXAZg==} + /@testing-library/react@14.2.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==} engines: {node: '>=14'} peerDependencies: react: ^18.0.0 @@ -2386,7 +2431,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 20.11.8 + '@types/node': 20.11.16 dev: false /@types/istanbul-lib-coverage@2.0.6: @@ -2405,8 +2450,8 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: false - /@types/jest@29.5.11: - resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==} + /@types/jest@29.5.12: + resolution: {integrity: sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==} dependencies: expect: 29.7.0 pretty-format: 29.7.0 @@ -2415,7 +2460,7 @@ packages: /@types/jsdom@20.0.1: resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} dependencies: - '@types/node': 20.11.8 + '@types/node': 20.11.16 '@types/tough-cookie': 4.0.5 parse5: 7.1.2 dev: false @@ -2428,8 +2473,8 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: false - /@types/node@20.11.8: - resolution: {integrity: sha512-i7omyekpPTNdv4Jb/Rgqg0RU8YqLcNsI12quKSDkRXNfx7Wxdm6HhK1awT3xTgEkgxPn3bvnSpiEAc7a7Lpyow==} + /@types/node@20.11.16: + resolution: {integrity: sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==} dependencies: undici-types: 5.26.5 dev: false @@ -2437,7 +2482,7 @@ packages: /@types/pg@8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: - '@types/node': 20.11.8 + '@types/node': 20.11.16 pg-protocol: 1.6.0 pg-types: 2.2.0 dev: false @@ -2449,11 +2494,11 @@ packages: /@types/react-dom@18.2.18: resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} dependencies: - '@types/react': 18.2.48 + '@types/react': 18.2.53 dev: false - /@types/react@18.2.48: - resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==} + /@types/react@18.2.53: + resolution: {integrity: sha512-52IHsMDT8qATp9B9zoOyobW8W3/0QhaJQTw1HwRj0UY2yBpCAQ7+S/CqHYQ8niAm3p4ji+rWUQ9UCib0GxQ60w==} dependencies: '@types/prop-types': 15.7.11 '@types/scheduler': 0.16.8 @@ -2486,8 +2531,8 @@ packages: '@types/yargs-parser': 21.0.3 dev: false - /@typescript-eslint/eslint-plugin@6.19.1(@typescript-eslint/parser@6.19.1)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==} + /@typescript-eslint/eslint-plugin@6.20.0(@typescript-eslint/parser@6.20.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -2498,11 +2543,11 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/type-utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.20.0 + '@typescript-eslint/type-utils': 6.20.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 6.20.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.20.0 debug: 4.3.4 eslint: 8.56.0 graphemer: 1.4.0 @@ -2515,8 +2560,8 @@ packages: - supports-color dev: false - /@typescript-eslint/parser@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==} + /@typescript-eslint/parser@6.20.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2525,10 +2570,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/scope-manager': 6.20.0 + '@typescript-eslint/types': 6.20.0 + '@typescript-eslint/typescript-estree': 6.20.0(typescript@5.3.3) + '@typescript-eslint/visitor-keys': 6.20.0 debug: 4.3.4 eslint: 8.56.0 typescript: 5.3.3 @@ -2536,16 +2581,16 @@ packages: - supports-color dev: false - /@typescript-eslint/scope-manager@6.19.1: - resolution: {integrity: sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==} + /@typescript-eslint/scope-manager@6.20.0: + resolution: {integrity: sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/types': 6.20.0 + '@typescript-eslint/visitor-keys': 6.20.0 dev: false - /@typescript-eslint/type-utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==} + /@typescript-eslint/type-utils@6.20.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2554,8 +2599,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) - '@typescript-eslint/utils': 6.19.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/typescript-estree': 6.20.0(typescript@5.3.3) + '@typescript-eslint/utils': 6.20.0(eslint@8.56.0)(typescript@5.3.3) debug: 4.3.4 eslint: 8.56.0 ts-api-utils: 1.0.3(typescript@5.3.3) @@ -2564,13 +2609,13 @@ packages: - supports-color dev: false - /@typescript-eslint/types@6.19.1: - resolution: {integrity: sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==} + /@typescript-eslint/types@6.20.0: + resolution: {integrity: sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: false - /@typescript-eslint/typescript-estree@6.19.1(typescript@5.3.3): - resolution: {integrity: sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==} + /@typescript-eslint/typescript-estree@6.20.0(typescript@5.3.3): + resolution: {integrity: sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -2578,8 +2623,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/visitor-keys': 6.19.1 + '@typescript-eslint/types': 6.20.0 + '@typescript-eslint/visitor-keys': 6.20.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -2591,8 +2636,8 @@ packages: - supports-color dev: false - /@typescript-eslint/utils@6.19.1(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==} + /@typescript-eslint/utils@6.20.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -2600,9 +2645,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.6 - '@typescript-eslint/scope-manager': 6.19.1 - '@typescript-eslint/types': 6.19.1 - '@typescript-eslint/typescript-estree': 6.19.1(typescript@5.3.3) + '@typescript-eslint/scope-manager': 6.20.0 + '@typescript-eslint/types': 6.20.0 + '@typescript-eslint/typescript-estree': 6.20.0(typescript@5.3.3) eslint: 8.56.0 semver: 7.5.4 transitivePeerDependencies: @@ -2610,11 +2655,11 @@ packages: - typescript dev: false - /@typescript-eslint/visitor-keys@6.19.1: - resolution: {integrity: sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==} + /@typescript-eslint/visitor-keys@6.20.0: + resolution: {integrity: sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.19.1 + '@typescript-eslint/types': 6.20.0 eslint-visitor-keys: 3.4.3 dev: false @@ -2628,8 +2673,8 @@ packages: server-only: 0.0.1 dev: false - /@vercel/blob@0.19.0: - resolution: {integrity: sha512-F3TliTcBeSa8AJ1FfP5A2MPolyfQr27JvQ0ElrhYK0uK8eN0K7V5+1h3Dp+vVPLQold80nRC8VGzwHZsHFDKew==} + /@vercel/blob@0.20.0: + resolution: {integrity: sha512-TpINJc01JutYvNTyF4ZXy31VWXp+gnrK5B3cPnbS0ZGSFR0WLgbEPXCDyhRuK8cmFxOS/XszGZjhBCI2XgdbKQ==} engines: {node: '>=16.14'} dependencies: async-retry: 1.3.3 @@ -2647,8 +2692,8 @@ packages: ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) dev: false - /@vercel/speed-insights@1.0.8(next@14.1.0)(react@18.2.0): - resolution: {integrity: sha512-x1V9MHUMGkaNafmgY0qRbmbr+jfeU+Cvo5KgreeEGo63SFuBYRg9RS52GUJ9p82nEL6NBzc9HNru6fkcvufYcQ==} + /@vercel/speed-insights@1.0.9(next@14.1.0)(react@18.2.0): + resolution: {integrity: sha512-f+XFP0O+aZ4Olj9h+BitkB1b4NJQaxtyCb69wWuDxytJHY6Pa4QtZPdBUftHOcajUCHRVeq062fk3MKXKtjNVQ==} requiresBuild: true peerDependencies: '@sveltejs/kit': ^1 || ^2 @@ -3262,7 +3307,7 @@ packages: engines: {node: '>= 0.6'} dev: false - /create-jest@29.7.0(@types/node@20.11.8): + /create-jest@29.7.0(@types/node@20.11.16): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -3271,7 +3316,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.11.8) + jest-config: 29.7.0(@types/node@20.11.16) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -3681,11 +3726,11 @@ packages: dependencies: '@next/eslint-plugin-next': 14.1.0 '@rushstack/eslint-patch': 1.6.1 - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) eslint-plugin-react: 7.33.2(eslint@8.56.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) @@ -3705,7 +3750,7 @@ packages: - supports-color dev: false - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): + /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0): resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -3715,8 +3760,8 @@ packages: debug: 4.3.4 enhanced-resolve: 5.15.0 eslint: 8.56.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 is-core-module: 2.13.1 @@ -3728,7 +3773,7 @@ packages: - supports-color dev: false - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -3749,16 +3794,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) debug: 3.2.7 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0) transitivePeerDependencies: - supports-color dev: false - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -3768,7 +3813,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.19.1(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -3777,7 +3822,7 @@ packages: doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -4718,7 +4763,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -4739,7 +4784,7 @@ packages: - supports-color dev: false - /jest-cli@29.7.0(@types/node@20.11.8): + /jest-cli@29.7.0(@types/node@20.11.16): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4753,10 +4798,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.11.8) + create-jest: 29.7.0(@types/node@20.11.16) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.11.8) + jest-config: 29.7.0(@types/node@20.11.16) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -4767,7 +4812,7 @@ packages: - ts-node dev: false - /jest-config@29.7.0(@types/node@20.11.8): + /jest-config@29.7.0(@types/node@20.11.16): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -4782,7 +4827,7 @@ packages: '@babel/core': 7.23.7 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 babel-jest: 29.7.0(@babel/core@7.23.7) chalk: 4.1.2 ci-info: 3.9.0 @@ -4848,7 +4893,7 @@ packages: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -4865,7 +4910,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-mock: 29.7.0 jest-util: 29.7.0 dev: false @@ -4881,7 +4926,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.11.8 + '@types/node': 20.11.16 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -4932,7 +4977,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-util: 29.7.0 dev: false @@ -4987,7 +5032,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -5018,7 +5063,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -5070,7 +5115,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -5095,7 +5140,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.11.8 + '@types/node': 20.11.16 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -5107,13 +5152,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 20.11.8 + '@types/node': 20.11.16 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: false - /jest@29.7.0(@types/node@20.11.8): + /jest@29.7.0(@types/node@20.11.16): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -5126,7 +5171,7 @@ packages: '@jest/core': 29.7.0 '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@20.11.8) + jest-cli: 29.7.0(@types/node@20.11.16) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -5460,8 +5505,8 @@ packages: hasBin: true dev: false - /nanoid@5.0.4: - resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==} + /nanoid@5.0.5: + resolution: {integrity: sha512-/Veqm+QKsyMY3kqi4faWplnY1u+VuKO3dD2binyPIybP31DRO29bPF+1mszgLnrR2KqSLceFLBNw0zmvDzN1QQ==} engines: {node: ^18 || >=20} hasBin: true dev: false @@ -6282,8 +6327,8 @@ packages: engines: {node: '>=8'} dev: false - /sonner@1.3.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-+rOAO56b2eI3q5BtgljERSn2umRk63KFIvgb2ohbZ5X+Eb5u+a/7/0ZgswYqgBMg8dyl7n6OXd9KasA8QF9ToA==} + /sonner@1.4.0(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-nvkTsIuOmi9e5Wz5If8ldasJjZNVfwiXYijBi2dbijvTQnQppvMcXTFNxL/NUFWlI2yJ1JX7TREDsg+gYm9WyA==} peerDependencies: react: ^18.0.0 react-dom: ^18.0.0