From 4956164aef383eabc50ceb2491a83ad7075f9288 Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 10 Jun 2026 11:33:09 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20Others=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=B6=94=EA=B0=80=EC=97=90=20=EB=A7=9E=EC=B6=B0=20?= =?UTF-8?q?=EB=B2=94=EB=A1=80=20=EB=B0=95=EC=8A=A4=20=ED=99=95=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Others 카테고리가 새로 생기며 Most Solved Categories 범례가 4개에서 5개로 늘어나 박스 하단이 빡빡해진 문제 수정. 범례 시작점을 위로 올리고 배경 박스를 키워 5개 항목의 상하 여백 균형을 맞춤. --- src/utils/generateCategorySvg.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/generateCategorySvg.ts b/src/utils/generateCategorySvg.ts index de229bb..dfe4ed4 100644 --- a/src/utils/generateCategorySvg.ts +++ b/src/utils/generateCategorySvg.ts @@ -43,8 +43,8 @@ export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light let currentAngle = 0; const legendStartX = 25; - const legendStartY = 75; - const legendItemHeight = 22; + const legendStartY = 64; + const legendItemHeight = 23; const maxLegendItems = 5; // 카테고리가 없는 경우 @@ -140,7 +140,7 @@ export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light Most Solved Categories - + From cf7eb83fc4ac45283222e2a873ca40847ec2d6c8 Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 30 Oct 2026 16:55:05 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EC=B9=B4=EB=93=9C=20=ED=8F=B0?= =?UTF-8?q?=ED=8A=B8=EA=B0=80=20OS=20=EA=B8=B0=EB=B3=B8=20=ED=8F=B0?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=8C=80=EC=B2=B4=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SVG가 로 렌더링되면(GitHub README, 메인페이지 미리보기) 브라우저가 외부 리소스를 불러오지 않아 Google Fonts @import가 무시되고, OS 기본 monospace 폰트(macOS Chrome은 Menlo, Safari는 Courier 등)로 표시되던 문제 수정. JetBrains Mono 가변 폰트를 카드에 쓰는 굵기(400~700)와 ASCII 문자만 남겨 서브셋한 뒤 base64 @font-face로 SVG에 직접 임베드하고, 서브셋 생성 스크립트와 폰트 라이선스(OFL)를 함께 추가. Claude-Session: https://claude.ai/code/session_01AGcght9Lc8cAYdvYCrdSzd --- README.md | 2 + scripts/generate-font-subset.sh | 59 ++++++++++++ .../utils/generateCategorySvg.test.ts | 17 ++++ src/__tests__/utils/generateStatsSvg.test.ts | 9 ++ src/utils/fonts/OFL.txt | 93 +++++++++++++++++++ src/utils/fonts/jetbrainsMono.ts | 5 + src/utils/generateCategorySvg.ts | 3 +- src/utils/generateStatsSvg.ts | 3 +- 8 files changed, 189 insertions(+), 2 deletions(-) create mode 100755 scripts/generate-font-subset.sh create mode 100644 src/utils/fonts/OFL.txt create mode 100644 src/utils/fonts/jetbrainsMono.ts diff --git a/README.md b/README.md index 9d692fe..b4de073 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,5 @@ http://localhost:3000 에서 확인 ## License MIT License + +단, 카드에 임베드되는 JetBrains Mono 폰트(`src/utils/fonts/`)는 [SIL Open Font License 1.1](src/utils/fonts/OFL.txt)을 따릅니다. diff --git a/scripts/generate-font-subset.sh b/scripts/generate-font-subset.sh new file mode 100755 index 0000000..8ccc193 --- /dev/null +++ b/scripts/generate-font-subset.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# SVG 카드에 임베드할 JetBrains Mono 서브셋 폰트(src/utils/fonts/jetbrainsMono.ts)를 생성합니다. +# +# GitHub README처럼 SVG가 로 렌더링되면 브라우저가 외부 리소스(@import 등) 로드를 차단해 +# Google Fonts를 쓸 수 없고 OS 기본 monospace 폰트로 대체됩니다. +# 그래서 카드에 쓰는 글자(ASCII)와 굵기(400~700)만 남긴 가변 폰트를 base64로 SVG에 직접 넣습니다. +# +# 필요: curl, python3 + fonttools, brotli +# pip install fonttools==4.65.0 brotli==1.2.1 (버전이 다르면 생성 결과가 달라질 수 있음) +# 사용: ./scripts/generate-font-subset.sh (다른 python 사용 시 PYTHON=/path/to/python 지정) + +set -euo pipefail + +VERSION="v2.304" +BASE_URL="https://raw.githubusercontent.com/JetBrains/JetBrainsMono/$VERSION" +FONT_SHA256="662a196d58f1183bf2d77428b6d5283fe3f45161ab021bea4036bc98e5cac016" +OFL_SHA256="30f0c136e3c88e422d0791acd97238870f9054a9729bc34cf2ff0d4ed8cac4ad" +UNICODES="U+0020-007E" +WEIGHT_MIN=400 +WEIGHT_MAX=700 +PYTHON="${PYTHON:-python3}" + +OUT_DIR="$(cd "$(dirname "$0")/.." && pwd)/src/utils/fonts" +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +# 실행할 때마다 같은 결과가 나오도록 폰트 타임스탬프 고정 +export SOURCE_DATE_EPOCH=0 + +curl -sSfL -o "$TMP_DIR/font.ttf" "$BASE_URL/fonts/variable/JetBrainsMono%5Bwght%5D.ttf" +curl -sSfL -o "$TMP_DIR/OFL.txt" "$BASE_URL/OFL.txt" +echo "$FONT_SHA256 $TMP_DIR/font.ttf" | shasum -a 256 -c - +echo "$OFL_SHA256 $TMP_DIR/OFL.txt" | shasum -a 256 -c - + +"$PYTHON" -m fontTools.varLib.instancer "$TMP_DIR/font.ttf" "wght=$WEIGHT_MIN:$WEIGHT_MAX" \ + -o "$TMP_DIR/instanced.ttf" -q + +# 리거처 등 레이아웃 기능은 제외하고, 라이선스 정보(name ID 13, 14)는 유지 +"$PYTHON" -m fontTools.subset "$TMP_DIR/instanced.ttf" \ + --unicodes="$UNICODES" \ + --layout-features='' \ + --name-IDs=0,1,2,3,4,5,6,13,14 \ + --flavor=woff2 \ + --output-file="$TMP_DIR/subset.woff2" + +FONT_BASE64="$(base64 < "$TMP_DIR/subset.woff2" | tr -d '\n')" + +mkdir -p "$OUT_DIR" +cp "$TMP_DIR/OFL.txt" "$OUT_DIR/OFL.txt" +cat > "$OUT_DIR/jetbrainsMono.ts" < { expect(result).toContain('fill="#21262d"'); // cardBackground expect(result).toContain('stroke="#30363d"'); // border }); + + it('폰트를 외부에서 불러오지 않고 SVG에 임베드해야 함', () => { + const mockStats: TCategoryStats = { + nickname: 'weakness', + total_score: 5000, + categories: [ + { name: 'web', score: 2000, rank: 50, color: '#ff6b6b' } + ] + }; + + const result = generateCategorySvg(mockStats); + + // 로 렌더링되는 SVG는 외부 리소스(@import)를 불러올 수 없음 + expect(result).not.toContain('@import'); + expect(result).toContain("font-family: 'JetBrains Mono'"); + expect(result).toContain('url(data:font/woff2;base64,'); + }); }); \ No newline at end of file diff --git a/src/__tests__/utils/generateStatsSvg.test.ts b/src/__tests__/utils/generateStatsSvg.test.ts index 8b3b76e..15687fd 100644 --- a/src/__tests__/utils/generateStatsSvg.test.ts +++ b/src/__tests__/utils/generateStatsSvg.test.ts @@ -73,4 +73,13 @@ describe('generateStatsSvg 유틸리티 함수 테스트', () => { expect(result).toContain('fill="#21262d"'); // cardBackground expect(result).toContain('stroke="#30363d"'); // border }); + + it('폰트를 외부에서 불러오지 않고 SVG에 임베드해야 함', () => { + const result = generateStatsSvg(mockStats); + + // 로 렌더링되는 SVG는 외부 리소스(@import)를 불러올 수 없음 + expect(result).not.toContain('@import'); + expect(result).toContain("font-family: 'JetBrains Mono'"); + expect(result).toContain('url(data:font/woff2;base64,'); + }); }); \ No newline at end of file diff --git a/src/utils/fonts/OFL.txt b/src/utils/fonts/OFL.txt new file mode 100644 index 0000000..8bee414 --- /dev/null +++ b/src/utils/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/utils/fonts/jetbrainsMono.ts b/src/utils/fonts/jetbrainsMono.ts new file mode 100644 index 0000000..1749106 --- /dev/null +++ b/src/utils/fonts/jetbrainsMono.ts @@ -0,0 +1,5 @@ +// 이 파일은 scripts/generate-font-subset.sh 로 생성되었습니다. 직접 수정하지 마세요. +// JetBrains Mono v2.304 가변 폰트 (wght 400~700, U+0020-007E 서브셋) +// SIL Open Font License 1.1 (같은 디렉터리의 OFL.txt 참고) +export const jetBrainsMonoFontFace = + "@font-face { font-family: 'JetBrains Mono'; font-weight: 400 700; src: url(data:font/woff2;base64,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) format('woff2'); }"; diff --git a/src/utils/generateCategorySvg.ts b/src/utils/generateCategorySvg.ts index dfe4ed4..2892701 100644 --- a/src/utils/generateCategorySvg.ts +++ b/src/utils/generateCategorySvg.ts @@ -1,4 +1,5 @@ import { TCategoryStats, Theme, ThemeColors } from '../types'; +import { jetBrainsMonoFontFace } from './fonts/jetbrainsMono'; const themes: Record = { light: { @@ -123,7 +124,7 @@ export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light return ` > > ``` diff --git a/src/__tests__/pages/api/most-solved.test.ts b/src/__tests__/pages/api/most-solved.test.ts index 08e87b1..fa3f0a5 100644 --- a/src/__tests__/pages/api/most-solved.test.ts +++ b/src/__tests__/pages/api/most-solved.test.ts @@ -133,48 +133,48 @@ describe('most-solved API 엔드포인트 테스트', () => { expect(generateSvgArg.categories).toHaveLength(0); }); - it('사용자 이름이 없을 때 400 에러를 반환해야 함', async () => { - // HTTP 요청 모킹 (사용자 이름 없음) + it('사용자 이름이 없을 때 안내 카드를 반환해야 함', async () => { const { req, res } = createMocks({ method: 'GET', query: {}, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'Username is required' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('Most Solved Categories'); + expect(res._getData()).toContain('Missing username'); }); - it('사용자를 찾을 수 없을 때 400 에러를 반환해야 함', async () => { - // 모킹된 함수 구현 (사용자 ID가 null) + it('사용자를 찾을 수 없을 때 안내 카드를 반환해야 함', async () => { (dreamhackUtils.getUserId as jest.Mock).mockResolvedValueOnce(null); - // HTTP 요청 모킹 const { req, res } = createMocks({ method: 'GET', query: { username: 'nonexistentuser', + theme: 'dark', }, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'User not found' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('User not found'); + expect(res._getData()).toContain('nonexistentuser'); + // 다크 테마 배경색 적용 + expect(res._getData()).toContain('#0d1117'); }); - it('사용자 데이터를 가져올 수 없을 때 400 에러를 반환해야 함', async () => { - // 모킹된 함수 구현 + it('사용자 데이터를 가져올 수 없을 때 오류 카드를 반환해야 함', async () => { const username = 'weakness'; (dreamhackUtils.getUserId as jest.Mock).mockResolvedValueOnce(123); (dreamhackUtils.getUserData as jest.Mock).mockResolvedValueOnce(null); - // HTTP 요청 모킹 const { req, res } = createMocks({ method: 'GET', query: { @@ -182,20 +182,18 @@ describe('most-solved API 엔드포인트 테스트', () => { }, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'User information cannot be read.' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('Failed to load stats'); expect(dreamhackUtils.getUserData).toHaveBeenCalledWith(123, username); }); - it('예외 발생 시 500 에러를 반환해야 함', async () => { - // 모킹된 함수 구현 (예외 발생) + it('예외 발생 시 오류 카드를 반환해야 함', async () => { (dreamhackUtils.getUserId as jest.Mock).mockRejectedValueOnce(new Error('테스트 에러')); - // HTTP 요청 모킹 const { req, res } = createMocks({ method: 'GET', query: { @@ -203,11 +201,11 @@ describe('most-solved API 엔드포인트 테스트', () => { }, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(500); - expect(JSON.parse(res._getData())).toEqual({ error: 'Internal Server Error' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('Failed to load stats'); }); }); \ No newline at end of file diff --git a/src/__tests__/pages/api/stats.test.ts b/src/__tests__/pages/api/stats.test.ts index 1e28941..84911a5 100644 --- a/src/__tests__/pages/api/stats.test.ts +++ b/src/__tests__/pages/api/stats.test.ts @@ -123,26 +123,23 @@ describe('stats API 엔드포인트 테스트', () => { ); }); - it('사용자 이름이 없을 때 400 에러를 반환해야 함', async () => { - // HTTP 요청 모킹 (사용자 이름 없음) + it('사용자 이름이 없을 때 안내 카드를 반환해야 함', async () => { const { req, res } = createMocks({ method: 'GET', query: {}, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'Username is required' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('Missing username'); }); - it('사용자를 찾을 수 없을 때 400 에러를 반환해야 함', async () => { - // 모킹된 함수 구현 (사용자 ID가 null) + it('사용자를 찾을 수 없을 때 안내 카드를 반환해야 함', async () => { (dreamhackUtils.getUserId as jest.Mock).mockResolvedValueOnce(null); - // HTTP 요청 모킹 const { req, res } = createMocks({ method: 'GET', query: { @@ -150,20 +147,37 @@ describe('stats API 엔드포인트 테스트', () => { }, }); - // API 핸들러 호출 await handler(req, res); - // 응답 검증 - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'User not found' }); + expect(res._getStatusCode()).toBe(200); + expect(res._getHeaders()).toHaveProperty('content-type', 'image/svg+xml'); + expect(res._getHeaders()).toHaveProperty('cache-control', 'no-cache, no-store, must-revalidate'); + expect(res._getData()).toContain('User not found'); + expect(res._getData()).toContain('nonexistentuser'); + // 사용자가 없으면 마지막 랭크는 조회하지 않음 + expect(dreamhackUtils.getLastRank).not.toHaveBeenCalled(); }); - it('마지막 랭크를 가져올 수 없을 때 500 에러를 반환해야 함', async () => { - // 모킹된 함수 구현 + it('안내 카드에 표시하는 닉네임을 이스케이프해야 함', async () => { + (dreamhackUtils.getUserId as jest.Mock).mockResolvedValueOnce(null); + + const { req, res } = createMocks({ + method: 'GET', + query: { + username: '', + }, + }); + + await handler(req, res); + + expect(res._getData()).not.toContain('', 'system("/bin/sh")']) { + const result = generateStatsSvg({ ...mockStats, nickname }); - const result = generateStatsSvg(specialStats); - - // 특수 문자가 포함된 사용자 이름이 SVG에 포함되어 있는지 확인 - expect(result).toContain('test'); + // XML로 파싱되고, 닉네임이 태그가 아닌 텍스트로 들어가야 함 + const doc = new DOMParser().parseFromString(result.trim(), 'image/svg+xml'); + expect(doc.getElementsByTagName('parsererror')).toHaveLength(0); + expect(doc.getElementsByTagName('script')).toHaveLength(0); + expect(doc.querySelector('.user-name')?.textContent).toBe(nickname); + } }); it('긴 사용자 이름을 처리할 수 있어야 함', () => { diff --git a/src/__tests__/utils/redis.test.ts b/src/__tests__/utils/redis.test.ts index 9be7f72..ce50f2b 100644 --- a/src/__tests__/utils/redis.test.ts +++ b/src/__tests__/utils/redis.test.ts @@ -1,4 +1,11 @@ -import { saveUserIdToCache, getUserIdFromCache, __setRedisClientForTesting } from '../../utils/redis'; +import { + saveUserIdToCache, + getUserIdFromCache, + deleteUserIdFromCache, + saveUserNotFoundToCache, + isUserNotFoundCached, + __setRedisClientForTesting, +} from '../../utils/redis'; import Redis from 'ioredis'; // 테스트 환경 설정 (process.env.NODE_ENV는 jest에 의해 'test'로 설정됨) @@ -11,6 +18,7 @@ jest.mock('ioredis'); const mockRedis = new Redis() as jest.Mocked; mockRedis.get = jest.fn(); mockRedis.set = jest.fn(); +mockRedis.del = jest.fn(); // 테스트용 Redis 클라이언트 설정 beforeAll(() => { @@ -31,6 +39,7 @@ describe('Redis 유틸리티 함수 테스트', () => { await saveUserIdToCache('testuser', 12345); expect(mockRedis.set).toHaveBeenCalledWith('userId:testuser', 12345); + expect(mockRedis.del).toHaveBeenCalledWith('userNotFound:testuser'); expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Redis에 사용자 ID 저장 완료')); }); @@ -75,4 +84,52 @@ describe('Redis 유틸리티 함수 테스트', () => { expect(console.error).toHaveBeenCalledWith('Redis에서 사용자 ID 조회 실패:', mockError); }); }); -}); \ No newline at end of file + + describe('deleteUserIdFromCache 함수', () => { + it('Redis에서 사용자 ID를 삭제해야 함', async () => { + mockRedis.del.mockResolvedValueOnce(1); + + await deleteUserIdFromCache('testuser'); + + expect(mockRedis.del).toHaveBeenCalledWith('userId:testuser'); + }); + + it('예외 발생 시 오류를 기록해야 함', async () => { + const mockError = new Error('Redis 연결 오류'); + mockRedis.del.mockRejectedValueOnce(mockError); + + await deleteUserIdFromCache('testuser'); + + expect(console.error).toHaveBeenCalledWith('Redis에서 사용자 ID 삭제 실패:', mockError); + }); + }); + + describe('미존재 사용자 캐시 함수', () => { + it('찾지 못한 닉네임을 10분 동안 저장해야 함', async () => { + mockRedis.set.mockResolvedValueOnce('OK'); + + await saveUserNotFoundToCache('nonexistentuser'); + + expect(mockRedis.set).toHaveBeenCalledWith('userNotFound:nonexistentuser', '1', 'EX', 600); + }); + + it('저장된 닉네임이면 true를 반환해야 함', async () => { + mockRedis.get.mockResolvedValueOnce('1'); + + expect(await isUserNotFoundCached('nonexistentuser')).toBe(true); + expect(mockRedis.get).toHaveBeenCalledWith('userNotFound:nonexistentuser'); + }); + + it('저장되지 않은 닉네임이면 false를 반환해야 함', async () => { + mockRedis.get.mockResolvedValueOnce(null); + + expect(await isUserNotFoundCached('weakness')).toBe(false); + }); + + it('조회 중 예외가 나면 false를 반환해야 함', async () => { + mockRedis.get.mockRejectedValueOnce(new Error('Redis 연결 오류')); + + expect(await isUserNotFoundCached('weakness')).toBe(false); + }); + }); +}); diff --git a/src/__tests__/utils/svg.test.ts b/src/__tests__/utils/svg.test.ts new file mode 100644 index 0000000..c6908a7 --- /dev/null +++ b/src/__tests__/utils/svg.test.ts @@ -0,0 +1,23 @@ +import { escapeXml } from '../../utils/svg'; + +describe('escapeXml 함수 테스트', () => { + it('XML 특수 문자를 이스케이프해야 함', () => { + expect(escapeXml(`a&bd"e'f`)).toBe('a&b<c>d"e'f'); + }); + + it('이미 이스케이프된 문자열의 &도 다시 이스케이프해야 함', () => { + expect(escapeXml('&')).toBe('&amp;'); + }); + + it('XML에서 허용되지 않는 제어 문자를 제거해야 함', () => { + expect(escapeXml('a\u0000b\u0008c\u001Fd')).toBe('abcd'); + }); + + it('탭, 줄바꿈, 한글, 이모지는 그대로 두어야 함', () => { + expect(escapeXml('꿈나라역1번출구\t🐿\n')).toBe('꿈나라역1번출구\t🐿\n'); + }); + + it('숫자도 문자열로 변환해야 함', () => { + expect(escapeXml(5000)).toBe('5000'); + }); +}); diff --git a/src/pages/api/most-solved.ts b/src/pages/api/most-solved.ts index 77b4f17..b9753b8 100644 --- a/src/pages/api/most-solved.ts +++ b/src/pages/api/most-solved.ts @@ -1,8 +1,16 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { TCategoryData, TCategoryStats, Theme } from '../../types'; import { generateCategorySvg } from '../../utils/generateCategorySvg'; +import { generateErrorSvg, errorCards } from '../../utils/generateErrorSvg'; +import { sendSvg } from '../../utils/svg'; import { getUserId, getUserData } from '../../utils/dreamhack'; +// 처음 조회하는 닉네임은 Dreamhack 검색이 여러 번 필요해 기본 10초를 넘을 수 있음 +// (요청당 타임아웃 10초 × 최대 5단계 + 여유, Hobby 플랜 최대값) +export const config = { maxDuration: 60 }; + +const CARD_TITLE = 'Most Solved Categories'; + // 성능 측정을 위한 유틸리티 함수 const measureTime = async (name: string, fn: () => Promise): Promise => { console.time(`⏱️ ${name}`); @@ -30,30 +38,30 @@ const defaultColors = [ ]; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - console.time('⏱️ 전체 API 실행 시간'); const { username, theme: themeParam } = req.query; - if (!username || typeof username !== 'string') { - return res.status(400).json({ error: 'Username is required' }); - } - // 테마 파라미터 검증 const theme: Theme = themeParam === 'dark' ? 'dark' : 'light'; + if (!username || typeof username !== 'string') { + return sendSvg(res, generateErrorSvg(errorCards.missingUsername(CARD_TITLE), theme)); + } + + console.time('⏱️ 전체 API 실행 시간'); try { // 개별 API 호출 시간 측정 - const userId = await measureTime('getUserId', () => getUserId(username as string)); + const userId = await measureTime('getUserId', () => getUserId(username)); if (!userId) { console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(400).json({ error: 'User not found' }); + return sendSvg(res, generateErrorSvg(errorCards.userNotFound(CARD_TITLE, username), theme)); } // username을 함께 전달하여 캐시된 ID가 유효하지 않을 경우 재시도할 수 있도록 함 - const userData = await measureTime('getUserData', () => getUserData(userId, username as string)); + const userData = await measureTime('getUserData', () => getUserData(userId, username)); if (!userData) { console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(400).json({ error: 'User information cannot be read.' }); + return sendSvg(res, generateErrorSvg(errorCards.fetchFailed(CARD_TITLE), theme)); } console.time('⏱️ 데이터 가공 및 SVG 생성'); @@ -74,10 +82,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }; const svg = generateCategorySvg(emptyStats, theme); - res.setHeader('Content-Type', 'image/svg+xml'); - res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + console.timeEnd('⏱️ 데이터 가공 및 SVG 생성'); console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.send(svg); + return sendSvg(res, svg); } // 카테고리 데이터가 있는 경우 가공 @@ -105,13 +112,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const svg = generateCategorySvg(categoryStats, theme); console.timeEnd('⏱️ 데이터 가공 및 SVG 생성'); - res.setHeader('Content-Type', 'image/svg+xml'); - res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.send(svg); + return sendSvg(res, svg); } catch (error) { console.error(error); console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(500).json({ error: 'Internal Server Error' }); + return sendSvg(res, generateErrorSvg(errorCards.fetchFailed(CARD_TITLE), theme)); } } \ No newline at end of file diff --git a/src/pages/api/stats.ts b/src/pages/api/stats.ts index fe81df1..4039b56 100644 --- a/src/pages/api/stats.ts +++ b/src/pages/api/stats.ts @@ -1,6 +1,8 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { Tstats, Theme } from '../../types'; import { generateStatsSvg } from '../../utils/generateStatsSvg'; +import { generateErrorSvg, errorCards } from '../../utils/generateErrorSvg'; +import { sendSvg } from '../../utils/svg'; import { getLastRank, calculateTopPercentage, @@ -8,6 +10,12 @@ import { getUserData } from '../../utils/dreamhack'; +// 처음 조회하는 닉네임은 Dreamhack 검색이 여러 번 필요해 기본 10초를 넘을 수 있음 +// (요청당 타임아웃 10초 × 최대 5단계 + 여유, Hobby 플랜 최대값) +export const config = { maxDuration: 60 }; + +const CARD_TITLE = 'Dreamhack Wargame Stats'; + // 성능 측정을 위한 유틸리티 함수 const measureTime = async (name: string, fn: () => Promise): Promise => { console.time(`⏱️ ${name}`); @@ -19,35 +27,35 @@ const measureTime = async (name: string, fn: () => Promise): Promise => }; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - console.time('⏱️ 전체 API 실행 시간'); const { username, theme: themeParam } = req.query; - if (!username || typeof username !== 'string') { - return res.status(400).json({ error: 'Username is required' }); - } - // 테마 파라미터 검증 const theme: Theme = themeParam === 'dark' ? 'dark' : 'light'; + if (!username || typeof username !== 'string') { + return sendSvg(res, generateErrorSvg(errorCards.missingUsername(CARD_TITLE), theme)); + } + + console.time('⏱️ 전체 API 실행 시간'); try { // 개별 API 호출 시간 측정 - const userId = await measureTime('getUserId', () => getUserId(username as string)); - const lastRank = await measureTime('getLastRank', () => getLastRank()); - + const userId = await measureTime('getUserId', () => getUserId(username)); if (!userId) { console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(400).json({ error: 'User not found' }); + return sendSvg(res, generateErrorSvg(errorCards.userNotFound(CARD_TITLE, username), theme)); } + + const lastRank = await measureTime('getLastRank', () => getLastRank()); if (!lastRank) { console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(500).json({ error: 'Failed to fetch last rank' }); + return sendSvg(res, generateErrorSvg(errorCards.fetchFailed(CARD_TITLE), theme)); } // username을 함께 전달하여 캐시된 ID가 유효하지 않을 경우 재시도할 수 있도록 함 - const userData = await measureTime('getUserData', () => getUserData(userId, username as string)); + const userData = await measureTime('getUserData', () => getUserData(userId, username)); if (!userData) { console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(400).json({ error: 'User information cannot be read.' }); + return sendSvg(res, generateErrorSvg(errorCards.fetchFailed(CARD_TITLE), theme)); } console.time('⏱️ 데이터 가공 및 SVG 생성'); @@ -70,13 +78,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const svg = generateStatsSvg(stats, theme); console.timeEnd('⏱️ 데이터 가공 및 SVG 생성'); - res.setHeader('Content-Type', 'image/svg+xml'); - res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.send(svg); + return sendSvg(res, svg); } catch (error) { console.error(error); console.timeEnd('⏱️ 전체 API 실행 시간'); - return res.status(500).json({ error: 'Internal Server Error' }); + return sendSvg(res, generateErrorSvg(errorCards.fetchFailed(CARD_TITLE), theme)); } -} \ No newline at end of file +} diff --git a/src/pages/api/user-id.ts b/src/pages/api/user-id.ts new file mode 100644 index 0000000..de5a45d --- /dev/null +++ b/src/pages/api/user-id.ts @@ -0,0 +1,30 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { getUserId } from '../../utils/dreamhack'; + +// 처음 조회하는 닉네임은 Dreamhack 검색이 여러 번 필요해 기본 10초를 넘을 수 있음 +// (요청당 타임아웃 10초 × 최대 5단계 + 여유, Hobby 플랜 최대값) +export const config = { maxDuration: 60 }; + +// 메인 페이지에서 프로필 링크(숫자 ID 주소)를 만들 때 사용 +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { username } = req.query; + + if (!username || typeof username !== 'string') { + return res.status(400).json({ error: 'Username is required' }); + } + + try { + const userId = await getUserId(username); + if (!userId) { + return res.status(404).json({ error: 'User not found' }); + } + + return res.status(200).json({ + id: userId, + profileUrl: `https://dreamhack.io/users/${userId}`, + }); + } catch (error) { + console.error('Error fetching user id:', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0272762..3ad1202 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import Head from 'next/head'; import styles from '../styles/Home.module.css'; +import { encodeUsername } from '../utils/encodeUsername'; type Theme = 'light' | 'dark'; type CardType = 'stats' | 'most-solved'; @@ -77,6 +78,21 @@ export default function Home() { const [selectedCard, setSelectedCard] = useState('stats'); const [previewKey, setPreviewKey] = useState(0); const [isLoading, setIsLoading] = useState(false); + // 닉네임별 프로필 주소 (찾지 못하면 null). 늦게 도착한 다른 닉네임의 응답이 현재 결과를 덮어쓰지 않도록 닉네임별로 보관 + const [profileUrls, setProfileUrls] = useState>(new Map()); + const profileUrl = profileUrls.get(confirmedUsername) ?? null; + + // Dreamhack 프로필 주소는 닉네임이 아닌 숫자 ID를 사용하므로 미리보기한 닉네임의 ID를 조회 + const loadProfileUrl = (user: string) => { + fetch(`/api/user-id?username=${encodeUsername(user)}`) + .then((response) => { + if (response.status === 404) return null; + if (!response.ok) throw new Error(`user-id ${response.status}`); + return response.json(); + }) + .then((data) => setProfileUrls((prev) => new Map(prev).set(user, data?.profileUrl ?? null))) + .catch(() => {}); + }; const cardOptions: DropdownOption[] = [ { value: 'stats', label: 'Wargame Stats' }, @@ -92,13 +108,13 @@ export default function Home() { const getApiUrl = (card: CardType, theme: Theme, user: string) => { const endpoint = card === 'stats' ? 'stats' : 'most-solved'; - return `${baseUrl}/api/${endpoint}?username=${user || '사용자명'}&theme=${theme}`; + return `${baseUrl}/api/${endpoint}?username=${user ? encodeUsername(user) : '사용자명'}&theme=${theme}`; }; const getPreviewUrl = (card: CardType, theme: Theme, user: string) => { if (!user) return ''; const endpoint = card === 'stats' ? 'stats' : 'most-solved'; - return `/api/${endpoint}?username=${user}&theme=${theme}`; + return `/api/${endpoint}?username=${encodeUsername(user)}&theme=${theme}`; }; const getMarkdownCode = () => { @@ -108,8 +124,7 @@ export default function Home() { const getHtmlCode = () => { const altText = selectedCard === 'stats' ? 'Dreamhack Stats' : 'Dreamhack Category Chart'; - const user = confirmedUsername || '사용자명'; - return ` + return ` ${altText} `; }; @@ -127,13 +142,17 @@ export default function Home() { alert('사용자명을 입력해주세요.'); return; } - setConfirmedUsername(username); + setConfirmedUsername(username.trim()); setIsLoading(true); setPreviewKey(prev => prev + 1); }; const handleImageLoad = () => { setIsLoading(false); + // 카드 요청이 서버 캐시를 채운 뒤에 조회해야 Dreamhack 검색이 중복되지 않음 + if (!profileUrls.has(confirmedUsername)) { + loadProfileUrl(confirmedUsername); + } }; const handleImageError = () => { @@ -248,6 +267,11 @@ export default function Home() { {getHtmlCode()} + {!profileUrl && ( +

+ 사용자ID는 Dreamhack 프로필 주소(https://dreamhack.io/users/12345)의 숫자입니다. 미리보기에 성공하면 자동으로 채워집니다. +

+ )} diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css index 01b0b0a..76588a7 100644 --- a/src/styles/Home.module.css +++ b/src/styles/Home.module.css @@ -87,6 +87,12 @@ line-height: 1.5; } +.codeHint { + margin: 0.5rem 0 0 0; + font-size: 0.8rem; + color: #666; +} + .example { margin: 0 0 2rem 0; text-align: center; diff --git a/src/types/index.ts b/src/types/index.ts index dab1400..364d82a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -54,6 +54,7 @@ export interface TgetLastRankResponse { } export interface TUserRankingResponse { + count: number; results: TUserRankingResult[]; } @@ -77,6 +78,12 @@ export interface TCategoryStats { export type Theme = 'light' | 'dark'; +export interface TErrorCard { + title: string; + message: string; + details: string[]; +} + export interface ThemeColors { background: string; cardBackground: string; diff --git a/src/utils/dreamhack.ts b/src/utils/dreamhack.ts index 8a79526..711ad86 100644 --- a/src/utils/dreamhack.ts +++ b/src/utils/dreamhack.ts @@ -1,17 +1,57 @@ import { TgetLastRankResponse, TUserRankingResponse, TuserData, TUserRankingResult } from '../types'; -import { getUserIdFromCache, saveUserIdToCache } from './redis'; +import { + getUserIdFromCache, + saveUserIdToCache, + deleteUserIdFromCache, + isUserNotFoundCached, + saveUserNotFoundToCache, +} from './redis'; + +// Dreamhack이 응답하지 않을 때 요청 하나를 기다리는 최대 시간 +const FETCH_TIMEOUT_MS = 10000; +// 랭킹 API는 limit을 100으로 보내도 50개까지만 돌려줌 +const SEARCH_PAGE_SIZE = 50; +// 검색 결과는 랭킹 순이라, 흔한 문자열이 들어간 닉네임은 뒤쪽 페이지에 있을 수 있음 (최대 250명 확인) +const SEARCH_MAX_PAGES = 5; +// 실제 닉네임은 이보다 훨씬 짧음 +const MAX_USERNAME_LENGTH = 64; // 네트워크 요청 시간 측정을 위한 래퍼 함수 async function measureFetch(url: string, name: string): Promise { console.time(`🌐 ${name} 네트워크 요청`); try { - const response = await fetch(url); + const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) }); return response; } finally { console.timeEnd(`🌐 ${name} 네트워크 요청`); } } +// 랭킹 검색 한 페이지 조회 (닉네임의 &, #, + 등이 쿼리를 깨뜨리지 않도록 인코딩) +async function searchRanking(username: string, offset: number): Promise { + const params = new URLSearchParams({ + filter: 'global', + limit: String(SEARCH_PAGE_SIZE), + offset: String(offset), + search: username, + scope: 'all', + name: '', + category: '', + }); + const response = await measureFetch( + `https://dreamhack.io/api/v1/ranking/wargame/?${params}`, + `getUserId(offset=${offset})` + ); + if (!response.ok) { + throw new Error(`Failed to search ranking: ${response.status}`); + } + return await response.json() as TUserRankingResponse; +} + +function findExactUser(results: TUserRankingResult[], username: string): TUserRankingResult | undefined { + return results.find((user) => user.nickname === username); +} + export async function getLastRank(): Promise { try { console.time('⚙️ getLastRank 함수 전체'); @@ -33,7 +73,13 @@ export async function getLastRank(): Promise { } } +// 사용자를 찾지 못하면 null, 검색 자체가 실패하면 예외를 던짐 (오류 카드에서 둘을 구분하기 위함) export async function getUserId(username: string): Promise { + // 실제 닉네임일 수 없는 긴 값은 Redis 키를 만들거나 Dreamhack을 검색하지 않음 + if (Array.from(username).length > MAX_USERNAME_LENGTH) { + return null; + } + try { console.time('⚙️ getUserId 함수 전체'); @@ -45,33 +91,50 @@ export async function getUserId(username: string): Promise { return cachedUserId; } + // 최근 검색에서 찾지 못한 닉네임이면 Dreamhack 검색을 반복하지 않음 + if (await isUserNotFoundCached(username)) { + console.log(`🚫 최근 검색에서 찾지 못한 사용자입니다: ${username}`); + console.timeEnd('⚙️ getUserId 함수 전체'); + return null; + } + console.log(`🔍 Redis 캐시에서 사용자 ID를 찾지 못했습니다. API 호출을 시도합니다: ${username}`); - const response = await measureFetch( - `https://dreamhack.io/api/v1/ranking/wargame/?filter=global&limit=100&offset=0&search=${username}&scope=all&name=&category=`, - 'getUserId' - ); - - console.time('⚙️ getUserId JSON 파싱'); - const data = await response.json() as TUserRankingResponse; - console.timeEnd('⚙️ getUserId JSON 파싱'); - - console.time('⚙️ getUserId 사용자 검색'); - const user = data.results.find((user: TUserRankingResult) => user.nickname === username); - console.timeEnd('⚙️ getUserId 사용자 검색'); - + const firstPage = await searchRanking(username, 0); + let user = findExactUser(firstPage.results, username); + + // 첫 페이지에 없고 검색 결과가 더 있으면 다음 페이지들을 동시에 확인 + if (!user && firstPage.count > SEARCH_PAGE_SIZE) { + const lastOffset = Math.min(firstPage.count, SEARCH_PAGE_SIZE * SEARCH_MAX_PAGES); + const offsets: number[] = []; + for (let offset = SEARCH_PAGE_SIZE; offset < lastOffset; offset += SEARCH_PAGE_SIZE) { + offsets.push(offset); + } + // 일부 페이지가 실패해도 성공한 페이지에서 찾으면 사용 + const pages = await Promise.allSettled(offsets.map((offset) => searchRanking(username, offset))); + const results = pages.flatMap((page) => (page.status === 'fulfilled' ? page.value.results : [])); + user = findExactUser(results, username); + + // 못 찾았는데 실패한 페이지가 있으면 없는 사용자라고 단정할 수 없음 + if (!user && pages.some((page) => page.status === 'rejected')) { + throw new Error('Failed to search some ranking pages'); + } + } + const userId = user ? user.id : null; - - // 사용자 ID를 찾았다면 Redis에 저장 + + // 찾았으면 ID를, 못 찾았으면 미존재 사실을 Redis에 저장 (검색 자체가 실패한 경우는 저장하지 않음) if (userId !== null) { await saveUserIdToCache(username, userId); + } else { + await saveUserNotFoundToCache(username); } - + console.timeEnd('⚙️ getUserId 함수 전체'); return userId; } catch (error) { console.error('Error fetching user ID:', error); console.timeEnd('⚙️ getUserId 함수 전체'); - return null; + throw error; } } @@ -89,10 +152,11 @@ export async function getUserData(userId: number, username?: string): Promise ${newUserId}`); // 새로운 사용자 ID로 재귀적으로 다시 호출 return getUserData(newUserId); diff --git a/src/utils/encodeUsername.ts b/src/utils/encodeUsername.ts new file mode 100644 index 0000000..584a905 --- /dev/null +++ b/src/utils/encodeUsername.ts @@ -0,0 +1,8 @@ +// 카드 URL의 username 값 인코딩 +// &, #, +, 공백, 괄호 등 URL·Markdown을 깨뜨리는 ASCII 문자만 인코딩하고, 한글 등은 README에서 읽기 쉽게 그대로 둠 +export function encodeUsername(username: string): string { + return username.replace( + /[^A-Za-z0-9\-_.~\u0080-￿]/g, + (char) => `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}` + ); +} diff --git a/src/utils/generateCategorySvg.ts b/src/utils/generateCategorySvg.ts index 2892701..8855187 100644 --- a/src/utils/generateCategorySvg.ts +++ b/src/utils/generateCategorySvg.ts @@ -1,27 +1,7 @@ -import { TCategoryStats, Theme, ThemeColors } from '../types'; +import { TCategoryStats, Theme } from '../types'; +import { escapeXml, themes } from './svg'; import { jetBrainsMonoFontFace } from './fonts/jetbrainsMono'; -const themes: Record = { - light: { - background: '#ffffff', - cardBackground: '#f8fafc', - border: '#e2e8f0', - title: '#64748b', - text: '#0f172a', - subText: '#94a3b8', - accent: '#3b82f6', - }, - dark: { - background: '#0d1117', - cardBackground: '#21262d', - border: '#30363d', - title: '#8b949e', - text: '#e6edf3', - subText: '#7d8590', - accent: '#58a6ff', - }, -}; - export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light'): string { const colors = themes[theme]; // SVG 크기 및 차트 설정 @@ -107,7 +87,7 @@ export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light const capitalizedName = category.name.charAt(0).toUpperCase() + category.name.slice(1); legends += ` - ${capitalizedName} + ${escapeXml(capitalizedName)} ${percentage}% `; }); @@ -117,7 +97,7 @@ export function generateCategorySvg(stats: TCategoryStats, theme: Theme = 'light const centerCircle = ` Total - ${totalScore} + ${escapeXml(totalScore)} `; // SVG 구조 반환 diff --git a/src/utils/generateErrorSvg.ts b/src/utils/generateErrorSvg.ts new file mode 100644 index 0000000..0ab106d --- /dev/null +++ b/src/utils/generateErrorSvg.ts @@ -0,0 +1,66 @@ +import { TErrorCard, Theme } from '../types'; +import { escapeXml, themes } from './svg'; +import { jetBrainsMonoFontFace } from './fonts/jetbrainsMono'; + +// 카드 폭(390px)을 넘지 않도록 닉네임 표시 길이 제한 +const MAX_NICKNAME_LENGTH = 30; + +function truncate(value: string, maxLength: number): string { + const chars = Array.from(value); + return chars.length > maxLength ? `${chars.slice(0, maxLength - 3).join('')}...` : value; +} + +// README에서 깨진 이미지 대신 보여줄 오류 카드 문구 +export const errorCards = { + missingUsername: (title: string): TErrorCard => ({ + title, + message: 'Missing username', + details: ['Add ?username= to the card URL.'], + }), + userNotFound: (title: string, username: string): TErrorCard => ({ + title, + message: 'User not found', + details: [ + `Nickname: ${truncate(username, MAX_NICKNAME_LENGTH)}`, + 'Nicknames are case-sensitive.', + 'Only users on the wargame ranking are shown.', + ], + }), + fetchFailed: (title: string): TErrorCard => ({ + title, + message: 'Failed to load stats', + details: ['Could not load data from Dreamhack.', 'Please try again later.'], + }), +}; + +export function generateErrorSvg(error: TErrorCard, theme: Theme = 'light'): string { + const colors = themes[theme]; + + const details = error.details + .map((line, index) => `${escapeXml(line)}`) + .join('\n '); + + return ` + + + + + + + + ${escapeXml(error.title)} + + + ${escapeXml(error.message)} + ${details} + + + + + `; +} diff --git a/src/utils/generateStatsSvg.ts b/src/utils/generateStatsSvg.ts index cd3a6f3..07fbb34 100644 --- a/src/utils/generateStatsSvg.ts +++ b/src/utils/generateStatsSvg.ts @@ -1,34 +1,14 @@ -import { Tstats, Theme, ThemeColors } from '../types'; +import { Tstats, Theme } from '../types'; +import { escapeXml, themes } from './svg'; import { jetBrainsMonoFontFace } from './fonts/jetbrainsMono'; -const themes: Record = { - light: { - background: '#ffffff', - cardBackground: '#f8fafc', - border: '#e2e8f0', - title: '#64748b', - text: '#0f172a', - subText: '#94a3b8', - accent: '#3b82f6', - }, - dark: { - background: '#0d1117', - cardBackground: '#21262d', - border: '#30363d', - title: '#8b949e', - text: '#e6edf3', - subText: '#7d8590', - accent: '#58a6ff', - }, -}; - export function generateStatsSvg(stats: Tstats, theme: Theme = 'light'): string { const colors = themes[theme]; const rankParts = stats.wargame_rank.split('/'); const rankDisplay = rankParts.length === 2 - ? `${rankParts[0]}/${rankParts[1]}` - : `${stats.wargame_rank}`; + ? `${escapeXml(rankParts[0])}/${escapeXml(rankParts[1])}` + : `${escapeXml(stats.wargame_rank)}`; return ` @@ -50,19 +30,19 @@ export function generateStatsSvg(stats: Tstats, theme: Theme = 'light'): string Dreamhack Wargame Stats - ${stats.nickname} + ${escapeXml(stats.nickname)} TOP - ${stats.wargameRankPercentage}% + ${escapeXml(stats.wargameRankPercentage)}% Solved Challenges - ${stats.wargame_solved} + ${escapeXml(stats.wargame_solved)} @@ -74,7 +54,7 @@ export function generateStatsSvg(stats: Tstats, theme: Theme = 'light'): string Score - ${stats.wargame_score} + ${escapeXml(stats.wargame_score)} diff --git a/src/utils/redis.ts b/src/utils/redis.ts index 58c6e6a..5fa236a 100644 --- a/src/utils/redis.ts +++ b/src/utils/redis.ts @@ -74,6 +74,8 @@ export async function saveUserIdToCache(username: string, userId: number): Promi try { await redis.set(`userId:${username}`, userId); + // 동시에 실행된 다른 검색이 남긴 미존재 기록이 404 재시도를 막지 않도록 함께 삭제 + await redis.del(`userNotFound:${username}`); console.log(`✅ Redis에 사용자 ID 저장 완료: ${username} -> ${userId}`); } catch (error) { console.error('Redis에 사용자 ID 저장 실패:', error); @@ -100,6 +102,43 @@ export async function getUserIdFromCache(username: string): Promise { + if (!redis) return; + + try { + await redis.del(`userId:${username}`); + console.log(`🗑️ Redis에서 사용자 ID 삭제 완료: ${username}`); + } catch (error) { + console.error('Redis에서 사용자 ID 삭제 실패:', error); + } +} + +// 찾지 못한 닉네임을 잠시 기록해, README가 조회될 때마다 느린 Dreamhack 검색을 반복하지 않도록 함 +const USER_NOT_FOUND_TTL_SECONDS = 600; + +export async function saveUserNotFoundToCache(username: string): Promise { + if (!redis) return; + + try { + await redis.set(`userNotFound:${username}`, '1', 'EX', USER_NOT_FOUND_TTL_SECONDS); + console.log(`✅ Redis에 미존재 사용자 저장 완료: ${username}`); + } catch (error) { + console.error('Redis에 미존재 사용자 저장 실패:', error); + } +} + +export async function isUserNotFoundCached(username: string): Promise { + if (!redis) return false; + + try { + return (await redis.get(`userNotFound:${username}`)) !== null; + } catch (error) { + console.error('Redis에서 미존재 사용자 조회 실패:', error); + return false; + } +} + // 테스트를 위한 Redis 클라이언트 설정 함수 export function __setRedisClientForTesting(client: Redis | null): void { if (isTestEnvironment) { diff --git a/src/utils/svg.ts b/src/utils/svg.ts new file mode 100644 index 0000000..c7a5b17 --- /dev/null +++ b/src/utils/svg.ts @@ -0,0 +1,42 @@ +import type { NextApiResponse } from 'next'; +import { Theme, ThemeColors } from '../types'; + +export const themes: Record = { + light: { + background: '#ffffff', + cardBackground: '#f8fafc', + border: '#e2e8f0', + title: '#64748b', + text: '#0f172a', + subText: '#94a3b8', + accent: '#3b82f6', + }, + dark: { + background: '#0d1117', + cardBackground: '#21262d', + border: '#30363d', + title: '#8b949e', + text: '#e6edf3', + subText: '#7d8590', + accent: '#58a6ff', + }, +}; + +// SVG(XML)에 넣는 문자열의 특수문자를 이스케이프 (닉네임에 &, < 등이 있으면 SVG가 깨지거나 태그로 해석됨) +// XML에서 허용되지 않는 제어 문자는 제거 +export function escapeXml(value: string | number): string { + return String(value) + .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F￾￿]/g, '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +// SVG 카드 응답 (GitHub camo는 200이 아닌 응답을 이미지로 보여주지 않으므로 오류 카드도 200으로 응답) +export function sendSvg(res: NextApiResponse, svg: string) { + res.setHeader('Content-Type', 'image/svg+xml'); + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + return res.status(200).send(svg); +} From 61c4a03a0b1484649b4d48a89f46f79829d9ad31 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 2 Nov 2026 13:08:47 +0900 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EB=B3=B4=EC=95=88=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=8F=20CI=C2=B7lint=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - next, eslint-config-next 14.2.24 -> 14.2.35 (14.x 최신 보안 패치)와 npm audit fix로 하위 의존성 업데이트. 운영 의존성 취약점 4개 -> 2개 (남은 next 권고는 15.5 이상에서만 수정되며 미들웨어·i18n·App Router· next/image 등 이 앱에서 쓰지 않는 기능 대상) - GitHub Actions를 Node 24 기반 버전으로 업데이트 (checkout v7, setup-node v7, vercel-action v42)해 Node 20 지원 종료 경고 해소 - 동작하지 않던 lint 설정 수리: Next 14의 next lint가 인식하지 못하는 flat config(없는 패키지를 import)를 .eslintrc.json으로 교체하고 CI에 lint 단계 추가 - 사용하지 않는 node-fetch, msw, @testing-library/react, eslint-plugin-prettier와 @vercel/kv 목, verify-tests.sh 제거 Claude-Session: https://claude.ai/code/session_01AGcght9Lc8cAYdvYCrdSzd --- .eslintrc.json | 7 + .github/workflows/ci-cd.yml | 15 +- eslint.config.mjs | 28 - jest.config.js | 3 - package-lock.json | 2017 ++++++++++++++--------------------- package.json | 8 +- scripts/verify-tests.sh | 33 - src/__mocks__/@vercel/kv.ts | 23 - 8 files changed, 805 insertions(+), 1329 deletions(-) create mode 100644 .eslintrc.json delete mode 100644 eslint.config.mjs delete mode 100755 scripts/verify-tests.sh delete mode 100644 src/__mocks__/@vercel/kv.ts diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..680b45b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["next/core-web-vitals", "prettier"], + "rules": { + // 페이지의 는 모두 API가 동적으로 만드는 SVG 카드라 next/image 최적화 대상이 아님 + "@next/next/no-img-element": "off" + } +} diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b6caa88..ee18bd5 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -15,10 +15,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v7 with: node-version: '24' cache: 'npm' @@ -26,6 +26,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Run lint + run: npm run lint + - name: Run tests run: npm test @@ -36,11 +39,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Deploy to Vercel (Production) id: deploy - uses: amondnet/vercel-action@v20 + uses: amondnet/vercel-action@v42 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} @@ -79,11 +82,11 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v7 - name: Deploy to Vercel (Preview) id: deploy - uses: amondnet/vercel-action@v20 + uses: amondnet/vercel-action@v42 with: vercel-token: ${{ secrets.VERCEL_TOKEN }} vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 05303ca..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import js from '@eslint/js'; -import nextPlugin from 'eslint-plugin-next'; -import { nextConfig } from 'eslint-config-next'; -import prettierPlugin from 'eslint-plugin-prettier'; -import prettierConfig from 'eslint-config-prettier'; - -export default [ - js.configs.recommended, - nextConfig, - prettierConfig, - { - plugins: { - prettier: prettierPlugin, - next: nextPlugin - }, - rules: { - 'prettier/prettier': 'error', - 'arrow-body-style': 'off', - 'prefer-arrow-callback': 'off', - }, - ignores: [ - 'node_modules/', - '.next/', - 'dist/', - '.vercel/', - ], - }, -]; diff --git a/jest.config.js b/jest.config.js index 48fb9a4..66c6220 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,8 +11,5 @@ module.exports = { tsconfig: 'tsconfig.json', }], }, - transformIgnorePatterns: [ - '/node_modules/(?!node-fetch).+\\.js$' - ], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d56d905..85284fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,29 +9,28 @@ "version": "1.0.0", "dependencies": { "ioredis": "^5.5.0", - "next": "^14.1.4", - "node-fetch": "^3.3.2", + "next": "^14.2.35", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", "@types/node": "^20.14.10", "@types/react": "^18.2.73", "@types/react-dom": "^18.2.23", "eslint": "^8.57.0", - "eslint-config-next": "^14.1.4", + "eslint-config-next": "^14.2.35", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "msw": "^2.7.3", "node-mocks-http": "^1.16.2", "prettier": "^3.3.2", "ts-jest": "^29.2.6", "typescript": "^5.5.3" + }, + "engines": { + "node": "24.x" } }, "node_modules/@adobe/css-tools": { @@ -40,58 +39,48 @@ "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", "dev": true }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -128,15 +117,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.29.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.12.tgz", + "integrity": "sha512-2W76mhjrpQFO1lK5e8IvGYB2D0YZ7S2R1Z2BjO2ZUYuW+amcpK/z60U6ZZmzskwoa7sJ6XohP/Wg8H5KxXI/IA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.29.12", + "@babel/types": "^7.29.11", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -144,13 +134,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -164,6 +155,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -173,32 +165,45 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -217,52 +222,57 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.29.12", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.12.tgz", + "integrity": "sha512-WJ7JBp/Q4+z/ImydtI7jS6NVco5++632ExBemSX+Ysb+G1lCwQ5BVo3SY+jGGOcZXI4ZccbZSUSJLNBW6eEpJg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -493,67 +503,49 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.29.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.12.tgz", + "integrity": "sha512-MscXe/NAZcOkYEsRajHZFt3ynsb22PUi6CTrp5U7+RYycR9By84mn9mFN54YgfITiqK4hFx3Ju3tZSyzfpS5ew==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.12", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.12", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.11", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -565,54 +557,31 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@bundled-es-modules/cookie": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", - "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", - "dev": true, - "dependencies": { - "cookie": "^0.7.2" - } - }, - "node_modules/@bundled-es-modules/statuses": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", - "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", - "dev": true, - "dependencies": { - "statuses": "^2.0.1" - } - }, - "node_modules/@bundled-es-modules/tough-cookie": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", - "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", - "dev": true, - "dependencies": { - "@types/tough-cookie": "^4.0.5", - "tough-cookie": "^4.1.4" - } - }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -684,114 +653,6 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, - "node_modules/@inquirer/confirm": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.6.tgz", - "integrity": "sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==", - "dev": true, - "dependencies": { - "@inquirer/core": "^10.1.7", - "@inquirer/type": "^3.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.1.7", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.7.tgz", - "integrity": "sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==", - "dev": true, - "dependencies": { - "@inquirer/figures": "^1.0.10", - "@inquirer/type": "^3.0.4", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.10.tgz", - "integrity": "sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.4.tgz", - "integrity": "sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, "node_modules/@ioredis/commands": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", @@ -802,6 +663,7 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -815,10 +677,11 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -827,12 +690,13 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -880,10 +744,11 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1260,17 +1125,25 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1282,15 +1155,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", @@ -1298,51 +1162,38 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mswjs/interceptors": { - "version": "0.37.6", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.37.6.tgz", - "integrity": "sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==", - "dev": true, - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "strict-event-emitter": "^0.5.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@next/env": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", - "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==" + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", + "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.24.tgz", - "integrity": "sha512-FDL3qs+5DML0AJz56DCVr+KnFYivxeAX73En8QbPw9GjJZ6zbfvqDy+HrarHFzbsIASn7y8y5ySJ/lllSruNVQ==", + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.35.tgz", + "integrity": "sha512-Jw9A3ICz2183qSsqwi7fgq4SBPiNfmOLmTPXKvlnzstUwyvBrtySiY+8RXJweNAs9KThb1+bYhZh9XWcNOr2zQ==", "dev": true, + "license": "MIT", "dependencies": { "glob": "10.3.10" } }, "node_modules/@next/eslint-plugin-next/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -1351,7 +1202,9 @@ "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.5", @@ -1370,12 +1223,13 @@ } }, "node_modules/@next/eslint-plugin-next/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1385,12 +1239,13 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", - "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1400,12 +1255,13 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.24.tgz", - "integrity": "sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1415,12 +1271,13 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.24.tgz", - "integrity": "sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1430,12 +1287,13 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.24.tgz", - "integrity": "sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1445,12 +1303,13 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.24.tgz", - "integrity": "sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1460,12 +1319,13 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.24.tgz", - "integrity": "sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" @@ -1475,12 +1335,13 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.24.tgz", - "integrity": "sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1490,12 +1351,13 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.24.tgz", - "integrity": "sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", "cpu": [ "ia32" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1505,12 +1367,13 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.24.tgz", - "integrity": "sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -1563,50 +1426,17 @@ "node": ">=12.4.0" } }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1657,36 +1487,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "peer": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/@testing-library/jest-dom": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", @@ -1726,49 +1526,16 @@ "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", "dev": true }, - "node_modules/@testing-library/react": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz", - "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0 || ^19.0.0", - "@types/react-dom": "^18.0.0 || ^19.0.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1810,12 +1577,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "dev": true - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1948,12 +1709,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/@types/statuses": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", - "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", - "dev": true - }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -1976,20 +1731,20 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz", - "integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/type-utils": "8.25.0", - "@typescript-eslint/utils": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.72.1.tgz", + "integrity": "sha512-5CokTWXpmbylfFabsbvHC0CAw3XLaGnoa+nFJX5FR592h/LoZsENUE6p/rg5xvkKrFEXz6pYmgI29AgXc2N3BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/type-utils": "8.72.1", + "@typescript-eslint/utils": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1999,22 +1754,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "@typescript-eslint/parser": "^8.72.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz", - "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.72.1.tgz", + "integrity": "sha512-ZGwZP4OCfyA0AQTpEAMWz+rtX01DRhQWW5hB0ucKDEIX5U7jdmPSaMx94rQfaPAQzIYz2BimOgKOJtjfpRFQ9g==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/typescript-estree": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2024,18 +1790,41 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.72.1.tgz", + "integrity": "sha512-fAuVMkwdO/huI8nyo/WKnNKf6Gag93S56Rh3Ts5mGcavwSdaN8tRg1gCeIGX8PF+nzp1BjGfO4gHhGzW1J6jTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.72.1", + "@typescript-eslint/types": "^8.72.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz", - "integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.72.1.tgz", + "integrity": "sha512-lC/t6eMCZZQAAKZEYGL6RDVSuFehpdmMT6h+nJIjIa6oylN/PwDgDVtEIbB7O9xFfZYtZAYLoLlDNWKJhtF+uQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0" + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2045,16 +1834,35 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.72.1.tgz", + "integrity": "sha512-e4D5R5vpeAEPBxT2BAzsFQL3sbwyeeeo5+aczgNSRK1V734jE7pTqaGFU+fXapWEQwnZbpFaTn32+Z8DwS6OTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz", - "integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.72.1.tgz", + "integrity": "sha512-iY2wlmyrXJmx4yxUGP3aYCFF3cA/6ShddOKmLU2dx7LnEykbAM8WThlxZEfd1NddqceF6eJmZUayxx9oKIYNlg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.25.0", - "@typescript-eslint/utils": "8.25.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1", + "@typescript-eslint/utils": "8.72.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2064,15 +1872,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz", - "integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.72.1.tgz", + "integrity": "sha512-LmE5IhQet1BWOWEXqBrVtBPsMAOQ6Nzk35d+WLh3mk4EUF33sqlfxjio1MdYeSvKSG5skactKKuixBIKXhL4Rw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2082,19 +1891,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz", - "integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.72.1.tgz", + "integrity": "sha512-DAlUoUV93hIvrtIQLwCbmZQYVZwOMhiHJrOVUUIWicFZaIYjTRqlY3I/NZevfkLmSqkNHRu2qYDcq9lrn16oOQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/visitor-keys": "8.25.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "@typescript-eslint/project-service": "8.72.1", + "@typescript-eslint/tsconfig-utils": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/visitor-keys": "8.72.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2104,43 +1915,59 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.8.0" + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz", - "integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.72.1.tgz", + "integrity": "sha512-5v5liypurJBFHCBNqh/RjsymAD+VWcE0djuGY/BUqp5fC3Qn+luWDEksjuTtWjPn6i5zQlUtkZCRG/CYG0yCkA==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.25.0", - "@typescript-eslint/types": "8.25.0", - "@typescript-eslint/typescript-estree": "8.25.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.72.1", + "@typescript-eslint/types": "8.72.1", + "@typescript-eslint/typescript-estree": "8.72.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2150,18 +1977,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.8.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz", - "integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==", + "version": "8.72.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.72.1.tgz", + "integrity": "sha512-SB8xqipSzZHUePCCLNeYxqnxq83X61/qzNhjQGbTfjxGZIeOEP7tBgWYX05Tcd6CgE02WdTJnh5b8kMZJ/+bnQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.25.0", - "eslint-visitor-keys": "^4.2.0" + "@typescript-eslint/types": "8.72.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2172,12 +2000,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2265,10 +2094,11 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2376,17 +2206,20 @@ } }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.2.0.tgz", + "integrity": "sha512-VXY5eFRarnXcYxwBjJzPmEhH55+rmP79/+ueDhi0F+TuqfHCItagIHqxeUZrmgrOPa31QTh9H85DjX3FfJ0FTg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "es-shim-unscopables": "^1.1.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2416,17 +2249,19 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2514,12 +2349,6 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true - }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -2693,11 +2522,25 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.34", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.34.tgz", + "integrity": "sha512-Aqr8mA1kIvyJ3UY5YAY7ib8LSo1DmDMbMnVqGPtsKW1LxaFQFwn9Eb3yvq2fUKJsRvaXjnAV7R5osqETapzNiA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2716,9 +2559,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.29.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz", + "integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==", "dev": true, "funding": [ { @@ -2734,11 +2577,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.11.33", + "caniuse-lite": "^1.0.30001819", + "electron-to-chromium": "^1.5.464", + "node-releases": "^2.0.60", + "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" @@ -2786,14 +2631,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -2817,13 +2663,14 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -2851,9 +2698,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001701", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", - "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", + "version": "1.0.30001820", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001820.tgz", + "integrity": "sha512-gfWD6MxqVCqAOwmODfZf7DchWvlrA+SRdNX7ceLfg3xRqe5ssjjpF7EgslC/0tiLiNTajdaNN/q8c/5u/7Gz8w==", "funding": [ { "type": "opencollective", @@ -2867,7 +2714,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -2915,15 +2763,6 @@ "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -3058,15 +2897,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -3144,14 +2974,6 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, "node_modules/data-urls": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", @@ -3218,9 +3040,10 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3328,16 +3151,6 @@ "node": ">= 0.6" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3368,13 +3181,6 @@ "node": ">=6.0.0" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true - }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -3406,28 +3212,15 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.107", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.107.tgz", - "integrity": "sha512-dJr1o6yCntRkXElnhsHh1bAV19bo/hKyFf7tCcWgpXbuFIF0Lakjgqv5LRfSDaNzAII8Fnxg2tqgHkgCvxdbxw==", - "dev": true + "version": "1.5.472", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.472.tgz", + "integrity": "sha512-s4+nfT29b0FKpVKoaIxl2pWsoNcn7A/nMPSrxayw4T3BsmPeV2lF9Y06BsAQeMGhCTQ4nYJ5XST7jkqe4ZEqFw==", + "dev": true, + "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", @@ -3482,27 +3275,28 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -3514,21 +3308,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -3537,7 +3334,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -3592,10 +3389,11 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3746,12 +3544,13 @@ } }, "node_modules/eslint-config-next": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.24.tgz", - "integrity": "sha512-9r1ujK++Pgpfixr5+DQ6rXDIQmSzuDbBlAQYMkJRMz9KWqovX7ESUTC0EAyBfOCl3ubkoeplw+aoXDuih3A8fw==", + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.35.tgz", + "integrity": "sha512-BpLsv01UisH193WyT/1lpHqq5iJ/Orfz9h/NOOlAmTUq4GY349PextQ62K4XpnaM9supeiEn3TaOTeQO07gURg==", "dev": true, + "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "14.2.24", + "@next/eslint-plugin-next": "14.2.35", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -3773,10 +3572,11 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz", + "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, + "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3839,10 +3639,11 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -3860,34 +3661,36 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", + "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", - "is-core-module": "^2.15.1", + "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", + "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "engines": { @@ -3956,41 +3759,12 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -4002,7 +3776,7 @@ "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", @@ -4228,40 +4002,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4292,28 +4032,6 @@ "bser": "2.1.1" } }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4326,36 +4044,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4392,33 +4080,18 @@ "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "rimraf": "^3.0.2" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.5", @@ -4440,6 +4113,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -4452,31 +4126,22 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -4736,13 +4401,26 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "node_modules/handlebars": { + "version": "4.7.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.10.tgz", + "integrity": "sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==", "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.8", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, "node_modules/has-bigints": { @@ -4821,10 +4499,11 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -4832,12 +4511,6 @@ "node": ">= 0.4" } }, - "node_modules/headers-polyfill": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", - "dev": true - }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -5246,11 +4919,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-node-process": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", - "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", - "dev": true + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/is-number": { "version": "7.0.0", @@ -5540,6 +5220,7 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5553,24 +5234,6 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -6456,10 +6119,21 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -6655,10 +6329,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", @@ -6697,17 +6372,8 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "peer": true, - "bin": { - "lz-string": "bin/bin.js" - } + "license": "ISC" }, "node_modules/make-dir": { "version": "4.0.0", @@ -6772,15 +6438,6 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -6855,10 +6512,11 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6876,10 +6534,11 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -6889,81 +6548,17 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/msw": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.7.3.tgz", - "integrity": "sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@bundled-es-modules/cookie": "^2.0.1", - "@bundled-es-modules/statuses": "^1.0.1", - "@bundled-es-modules/tough-cookie": "^0.1.6", - "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.37.0", - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/until": "^2.1.0", - "@types/cookie": "^0.6.0", - "@types/statuses": "^2.0.4", - "graphql": "^16.8.1", - "headers-polyfill": "^4.0.2", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "path-to-regexp": "^6.3.0", - "picocolors": "^1.1.1", - "strict-event-emitter": "^0.5.1", - "type-fest": "^4.26.1", - "yargs": "^17.7.2" - }, - "bin": { - "msw": "cli/index.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/mswjs" - }, - "peerDependencies": { - "typescript": ">= 4.8.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz", - "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "dev": true, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6986,12 +6581,20 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/next": { - "version": "14.2.24", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.24.tgz", - "integrity": "sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==", + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", + "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", + "license": "MIT", "dependencies": { - "@next/env": "14.2.24", + "@next/env": "14.2.35", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -7006,15 +6609,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.24", - "@next/swc-darwin-x64": "14.2.24", - "@next/swc-linux-arm64-gnu": "14.2.24", - "@next/swc-linux-arm64-musl": "14.2.24", - "@next/swc-linux-x64-gnu": "14.2.24", - "@next/swc-linux-x64-musl": "14.2.24", - "@next/swc-win32-arm64-msvc": "14.2.24", - "@next/swc-win32-ia32-msvc": "14.2.24", - "@next/swc-win32-x64-msvc": "14.2.24" + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -7035,41 +6638,6 @@ } } }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7110,10 +6678,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true + "version": "2.0.63", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.63.tgz", + "integrity": "sha512-6li2QanLsAXpIH2R6j77MzRyvOyrvdTqlKtO4CgBdHGNvN99ZZfyxszHc5o5+IZBWD5u6Hzwg83JoGXmqZNG9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -7193,14 +6765,16 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -7297,12 +6871,6 @@ "node": ">= 0.8.0" } }, - "node_modules/outvariant": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", - "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", - "dev": true - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -7448,6 +7016,7 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -7459,22 +7028,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -7615,53 +7179,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -7841,12 +7358,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -7957,6 +7468,23 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8079,10 +7607,11 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -8266,9 +7795,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -8321,13 +7851,18 @@ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, "node_modules/streamsearch": { @@ -8338,12 +7873,6 @@ "node": ">=10.0.0" } }, - "node_modules/strict-event-emitter": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", - "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", - "dev": true - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -8362,6 +7891,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -8380,6 +7910,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8393,13 +7924,15 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -8408,12 +7941,13 @@ } }, "node_modules/string-width/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -8547,6 +8081,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -8648,22 +8183,6 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "dev": true, - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -8694,13 +8213,14 @@ "dev": true }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, + "license": "MIT", "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -8710,10 +8230,14 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -8724,10 +8248,11 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -8781,10 +8306,11 @@ } }, "node_modules/ts-api-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.12" }, @@ -8793,19 +8319,20 @@ } }, "node_modules/ts-jest": { - "version": "29.2.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz", - "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==", + "version": "29.4.15", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.15.tgz", + "integrity": "sha512-s2qmEUYFRqbjz1wv0EmccNPVOb45HOFpFuhzQJLKShYVisuTbCFPIIAIV2EuK5rIxb4BejLDbH7dnBZfYjybUQ==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", - "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", + "handlebars": "^4.7.10", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.7.1", + "semver": "^7.8.5", + "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { @@ -8815,12 +8342,13 @@ "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" + "@babel/core": ">=7.0.0-beta.0 <9", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" }, "peerDependenciesMeta": { "@babel/core": { @@ -8837,6 +8365,9 @@ }, "esbuild": { "optional": true + }, + "jest-util": { + "optional": true } } }, @@ -8852,6 +8383,19 @@ "node": ">=6" } }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -9002,6 +8546,20 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -9036,9 +8594,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", "dev": true, "funding": [ { @@ -9054,6 +8612,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -9119,14 +8678,6 @@ "makeerror": "1.0.12" } }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -9250,15 +8801,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.24.tgz", + "integrity": "sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, @@ -9278,11 +8831,19 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -9301,6 +8862,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9317,13 +8879,15 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9334,10 +8898,11 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9346,10 +8911,11 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -9358,12 +8924,13 @@ } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -9398,10 +8965,11 @@ "dev": true }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -9446,7 +9014,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", @@ -9506,18 +9075,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/package.json b/package.json index 4d73cf8..e6c0895 100644 --- a/package.json +++ b/package.json @@ -16,25 +16,21 @@ }, "dependencies": { "ioredis": "^5.5.0", - "next": "^14.1.4", - "node-fetch": "^3.3.2", + "next": "^14.2.35", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", "@types/node": "^20.14.10", "@types/react": "^18.2.73", "@types/react-dom": "^18.2.23", "eslint": "^8.57.0", - "eslint-config-next": "^14.1.4", + "eslint-config-next": "^14.2.35", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.1.3", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", - "msw": "^2.7.3", "node-mocks-http": "^1.16.2", "prettier": "^3.3.2", "ts-jest": "^29.2.6", diff --git a/scripts/verify-tests.sh b/scripts/verify-tests.sh deleted file mode 100755 index a38ede9..0000000 --- a/scripts/verify-tests.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# 메인 브랜치에서만 실행 -if [ "$VERCEL_GIT_COMMIT_REF" != "main" ]; then - echo "메인 브랜치가 아닙니다. 배포 계속 진행합니다." - exit 0 -fi - -# GitHub API 호출을 위한 준비 -AUTH_HEADER="Authorization: token $GITHUB_TOKEN" -GITHUB_USER="with-developer" -GITHUB_REPO="dreamhack-readme-stats" -API_URL="https://api.github.com/repos/$GITHUB_USER/$GITHUB_REPO/commits/$VERCEL_GIT_COMMIT_SHA/check-runs" - -echo "GitHub API 호출: $API_URL" - -# jq 설치 -apt-get update && apt-get install -y jq - -# API 호출 및 테스트 상태 확인 -RESPONSE=$(curl -s -H "$AUTH_HEADER" "$API_URL") -TEST_STATUS=$(echo "$RESPONSE" | jq -r '.check_runs[] | select(.name | contains("test")) | .conclusion') - -echo "테스트 상태: $TEST_STATUS" - -# 테스트 성공 확인 -if [[ "$TEST_STATUS" == "success" ]]; then - echo "테스트 성공. 배포 진행합니다." - exit 0 -else - echo "테스트 실패 또는 미완료. 배포 중단합니다." - exit 1 -fi \ No newline at end of file diff --git a/src/__mocks__/@vercel/kv.ts b/src/__mocks__/@vercel/kv.ts deleted file mode 100644 index 8eaf056..0000000 --- a/src/__mocks__/@vercel/kv.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Vercel KV 모킹 -const mockStorage = new Map(); - -export const kv = { - get: jest.fn(async (key: string) => { - return mockStorage.get(key) || null; - }), - set: jest.fn(async (key: string, value: any) => { - mockStorage.set(key, value); - return 'OK'; - }), - del: jest.fn(async (key: string) => { - mockStorage.delete(key); - return 1; - }), - // 테스트를 위한 추가 메서드 - _reset: () => { - mockStorage.clear(); - }, - _getMockStorage: () => { - return mockStorage; - } -}; \ No newline at end of file