From 045b2ef624df4f6b260e89d4e99f84ee5f48e813 Mon Sep 17 00:00:00 2001 From: bingbingmo Date: Tue, 12 Aug 2025 16:27:41 +0900 Subject: [PATCH] test --- .cursor/mcp.json | 4 +- SETUP.md | 58 + issue_body.md | Bin 0 -> 1154 bytes package-lock.json | 2519 ++++++++++++++++- package.json | 3 + ...5da76d18fe7e63ab7cb8f434b4fe5ba0faa3c8c.md | 111 + ...a6e3f481a316869b874683879d9094020130c21.md | 116 + ...e0bb665eb9c038554b49d32db23f52fd050064e.md | 43 + ...32e6aeb30cf8b8698861f248263f29aefd65169.md | 43 + ...8d07e94ad2a9b1d29fc3fe0ec49cce930a8d2a6.md | 24 + playwright-report/index.html | 77 + src/app/globals.css | 3 + src/app/page.tsx | 8 +- src/components/MemoForm.tsx | 62 +- src/components/MemoItem.tsx | 55 +- src/components/MemoList.tsx | 26 +- src/components/MemoViewer.tsx | 192 ++ src/hooks/useMemos.ts | 100 +- src/lib/supabase.ts | 43 + src/types/memo.ts | 7 +- src/utils/seedData.ts | 48 +- src/utils/storageAdapter.ts | 124 + src/utils/supabaseStorage.ts | 225 ++ test-results/.last-run.json | 22 + .../error-context.md" | 116 + .../error-context.md" | 24 + .../error-context.md" | 43 + .../error-context.md" | 43 + .../error-context.md" | 111 + test-results/results.json | 987 +++++++ test-results/results.xml | 331 +++ tests/e2e/memo-creation.spec.ts | 205 ++ tests/e2e/scenarios/README.md | 222 ++ tests/e2e/scenarios/memo-management.md | 321 +++ .../e2e/scenarios/performance-reliability.md | 453 +++ tests/e2e/scenarios/ui-accessibility.md | 410 +++ 36 files changed, 6943 insertions(+), 236 deletions(-) create mode 100644 SETUP.md create mode 100644 issue_body.md create mode 100644 playwright-report/data/25da76d18fe7e63ab7cb8f434b4fe5ba0faa3c8c.md create mode 100644 playwright-report/data/2a6e3f481a316869b874683879d9094020130c21.md create mode 100644 playwright-report/data/2e0bb665eb9c038554b49d32db23f52fd050064e.md create mode 100644 playwright-report/data/332e6aeb30cf8b8698861f248263f29aefd65169.md create mode 100644 playwright-report/data/58d07e94ad2a9b1d29fc3fe0ec49cce930a8d2a6.md create mode 100644 playwright-report/index.html create mode 100644 src/components/MemoViewer.tsx create mode 100644 src/lib/supabase.ts create mode 100644 src/utils/storageAdapter.ts create mode 100644 src/utils/supabaseStorage.ts create mode 100644 test-results/.last-run.json create mode 100644 "test-results/e2e-memo-creation-\353\251\224\353\252\250-\354\203\235\354\204\261-\352\270\260\353\212\245-\354\213\234\353\202\230\353\246\254\354\230\244-1-\353\247\210\355\201\254\353\213\244\354\232\264-\353\257\270\353\246\254\353\263\264\352\270\260-\352\270\260\353\212\245-chromium/error-context.md" create mode 100644 "test-results/e2e-memo-creation-\353\251\224\353\252\250-\354\203\235\354\204\261-\352\270\260\353\212\245-\354\213\234\353\202\230\353\246\254\354\230\244-1-\353\251\224\353\252\250-\354\236\221\354\204\261-\354\267\250\354\206\214-\352\270\260\353\212\245-chromium/error-context.md" create mode 100644 "test-results/e2e-memo-creation-\353\251\224\353\252\250-\354\203\235\354\204\261-\352\270\260\353\212\245-\354\213\234\353\202\230\353\246\254\354\230\244-1-\354\203\210-\353\251\224\353\252\250-\354\203\235\354\204\261-\354\204\261\352\263\265-chromium/error-context.md" create mode 100644 "test-results/e2e-memo-creation-\353\251\224\353\252\250-\354\203\235\354\204\261-\352\270\260\353\212\245-\354\213\234\353\202\230\353\246\254\354\230\244-1-\355\225\204\354\210\230-\355\225\204\353\223\234-\352\262\200\354\246\235---\353\202\264\354\232\251-\354\227\206\354\235\214-chromium/error-context.md" create mode 100644 "test-results/e2e-memo-creation-\353\251\224\353\252\250-\354\203\235\354\204\261-\352\270\260\353\212\245-\354\213\234\353\202\230\353\246\254\354\230\244-1-\355\225\204\354\210\230-\355\225\204\353\223\234-\352\262\200\354\246\235---\354\240\234\353\252\251-\354\227\206\354\235\214-chromium/error-context.md" create mode 100644 test-results/results.json create mode 100644 test-results/results.xml create mode 100644 tests/e2e/memo-creation.spec.ts create mode 100644 tests/e2e/scenarios/README.md create mode 100644 tests/e2e/scenarios/memo-management.md create mode 100644 tests/e2e/scenarios/performance-reliability.md create mode 100644 tests/e2e/scenarios/ui-accessibility.md diff --git a/.cursor/mcp.json b/.cursor/mcp.json index 80e201f..dec4c3e 100644 --- a/.cursor/mcp.json +++ b/.cursor/mcp.json @@ -5,10 +5,10 @@ "args": [ "-y", "@supabase/mcp-server-supabase@0.4.5", - "--project-ref=" + "--project-ref=dgxvvtwnxdobchufhmut" ], "env": { - "SUPABASE_ACCESS_TOKEN": "" + "SUPABASE_ACCESS_TOKEN": "sbp_6e4d4762b48a3b9b6f18f65d748fa3cfadbafeb8" } }, "playwright-mcp": { diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 0000000..643ffd0 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,58 @@ +# 메모 앱 설정 가이드 + +## 환경 변수 설정 (선택사항) + +이 앱은 Supabase와 로컬 스토리지 두 가지 방식을 지원합니다: + +- **Supabase**: 클라우드 데이터베이스 (환경 변수 설정 필요) +- **로컬 스토리지**: 브라우저 로컬 스토리지 (설정 불필요, 기본값) + +### 현재 상태 +환경 변수가 설정되지 않았으므로 **로컬 스토리지**를 사용합니다. + +### Supabase 사용하기 (선택사항) + +1. [Supabase 대시보드](https://supabase.com/dashboard)에서 프로젝트 생성 +2. 프로젝트 루트에 `.env.local` 파일 생성: + +```bash +# .env.local +NEXT_PUBLIC_SUPABASE_URL=your_supabase_url_here +NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here +``` + +3. Supabase 프로젝트에서 다음 테이블 생성: + +```sql +CREATE TABLE memos ( + id UUID DEFAULT gen_random_uuid() PRIMARY KEY, + title TEXT NOT NULL, + content TEXT NOT NULL, + category TEXT NOT NULL DEFAULT 'general', + tags TEXT[] DEFAULT '{}', + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- RLS 정책 (선택사항 - 인증 없이 모든 사용자 허용) +ALTER TABLE memos ENABLE ROW LEVEL SECURITY; +CREATE POLICY "Allow all operations" ON memos FOR ALL USING (true) WITH CHECK (true); +``` + +4. 앱 재시작 + +### 데이터 스토리지 확인 + +앱을 실행하면 콘솔에서 현재 사용 중인 스토리지 타입을 확인할 수 있습니다: +- `Using supabase for data storage` - Supabase 사용 중 +- `Using localStorage for data storage` - 로컬 스토리지 사용 중 + +## 실행 방법 + +```bash +npm run dev +# 또는 +yarn dev +``` + +브라우저에서 [http://localhost:3000](http://localhost:3000)을 열어 앱을 확인하세요. \ No newline at end of file diff --git a/issue_body.md b/issue_body.md new file mode 100644 index 0000000000000000000000000000000000000000..f7365572ab4eac340800f08ea12518e4730b0a35 GIT binary patch literal 1154 zcmZ`&O-LJ25FWvUEGea+;K9NJJt(vi`vcNbOG9f*3bv+@Lk_j2RH0Pcz7`7}dZ-{& zZ#m@P!9xUvO-M~7YlvZYardoYWK;VB1p__gm}3tfr0vWuA+ffH4KHuzee=zH-~9g7 z5Bm|ojt5HEuYDua-W+fiBHtcj@n zfz=j8&box0{1{fUmyHqQ1S9p$3wsQ@x4Mg5Buxh2v%z$vN^D|s*JHXU!eSWT*3CYw z>?5pt*)ffQv(3eEs!QV{Y7j^2Yf26ye+8#NbD#+@7%GlgA&j5yaRdl?0^Y$aOu|$A z-okUd>69_;qN9CfFktjx)$>IL<1m9!4lm#h=1jmt2xDeD9|qwH7u_WWHGRZT;L3l{ z?_-_!_)Wkw#G1z1oOrtPc`h`Is?sS?YUEX%;(!swSrY@|qQF%THJIjSbYO5B9>HS> zA=;GeHS0O*_-uE#u$Oz7Gg@6K(+tN`xqSt$dAOkxDZn7HPqnG4Yc=u==43r*;0^gl zf$Lsw1U?{lnnimby1Rf?dz>i#()-gBN}5U6K^$M_mP$3fLv4B`jy{F#m_;stESG%- z^}bNxvgOXp$o~$`j|WDH`&$RX z`M0N=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -935,6 +947,80 @@ "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", "dev": true }, + "node_modules/@supabase/auth-js": { + "version": "2.71.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.71.1.tgz", + "integrity": "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.5.tgz", + "integrity": "sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", + "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.0.tgz", + "integrity": "sha512-SEIWApsxyoAe68WU2/5PCCuBwa11LL4Bb8K3r2FHCt3ROpaTthmDiWEhnLMGayP05N4QeYrMk0kyTZOwid/Hjw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.10.4.tgz", + "integrity": "sha512-cvL02GarJVFcNoWe36VBybQqTVRq6wQSOCvTS64C+eyuxOruFIm1utZAY0xi2qKtHJO3EjKaj8iWJKySusDmAQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.54.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.54.0.tgz", + "integrity": "sha512-DLw83YwBfAaFiL3oWV26+sHRdeCGtxmIKccjh/Pndze3BWM4fZghzYKhk3ElOQU8Bluq4AkkCJ5bM5Szl/sfRg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.71.1", + "@supabase/functions-js": "2.4.5", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.15.0", + "@supabase/storage-js": "^2.10.4" + } + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1214,11 +1300,37 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -1232,20 +1344,45 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, "dependencies": { "csstype": "^3.0.2" } @@ -1259,12 +1396,27 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.38.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", @@ -1538,6 +1690,82 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uiw/copy-to-clipboard": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.17.tgz", + "integrity": "sha512-O2GUHV90Iw2VrSLVLK0OmNIMdZ5fgEg4NhvtwINsX+eZ/Wf6DWD0TdsK9xwV7dNRnK/UI2mQtl0a2/kRgm1m1A==", + "license": "MIT", + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/@uiw/react-markdown-preview": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-5.1.5.tgz", + "integrity": "sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@uiw/copy-to-clipboard": "~1.0.12", + "react-markdown": "~9.0.1", + "rehype-attr": "~3.0.1", + "rehype-autolink-headings": "~7.1.0", + "rehype-ignore": "^2.0.0", + "rehype-prism-plus": "2.0.0", + "rehype-raw": "^7.0.0", + "rehype-rewrite": "~4.0.0", + "rehype-slug": "~6.0.0", + "remark-gfm": "~4.0.0", + "remark-github-blockquote-alert": "^1.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@uiw/react-markdown-preview/node_modules/rehype-prism-plus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz", + "integrity": "sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==", + "license": "MIT", + "dependencies": { + "hast-util-to-string": "^3.0.0", + "parse-numeric-range": "^1.3.0", + "refractor": "^4.8.0", + "rehype-parse": "^9.0.0", + "unist-util-filter": "^5.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/@uiw/react-md-editor": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@uiw/react-md-editor/-/react-md-editor-4.0.8.tgz", + "integrity": "sha512-S3mOzZeGmJNhzdXJxRTCwsFMDp8nBWeQUf59cK3L6QHzDUHnRoHpcmWpfVRyKGKSg8zaI2+meU5cYWf8kYn3mQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.14.6", + "@uiw/react-markdown-preview": "^5.0.6", + "rehype": "~13.0.0", + "rehype-prism-plus": "~2.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", @@ -2055,12 +2283,38 @@ "node": ">= 0.4" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2158,6 +2412,16 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2174,6 +2438,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -2229,6 +2533,16 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2249,11 +2563,26 @@ "node": ">= 8" } }, + "node_modules/css-selector-parser": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.1.3.tgz", + "integrity": "sha512-gJMigczVZqYAk0hPVzx/M4Hm1D9QOtqkdQk9005TNzDIUGzo5cnHEDiKUT7jGPximL/oYb+LIitcHFQ4aKupxg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -2316,7 +2645,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -2329,6 +2657,19 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2369,6 +2710,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2378,6 +2728,32 @@ "node": ">=8" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "license": "MIT", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2423,6 +2799,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -2999,6 +3387,16 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3008,6 +3406,12 @@ "node": ">=0.10.0" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3255,6 +3659,12 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3406,101 +3816,415 @@ "node": ">= 0.4" } }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "@types/hast": "^3.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, + "node_modules/hast-util-heading-rank": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" + "@types/hast": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "optional": true - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" + "@types/hast": "^3.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.4.tgz", + "integrity": "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "nth-check": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, "dependencies": { "has-bigints": "^1.0.2" }, @@ -3596,6 +4320,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3650,6 +4384,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -3699,6 +4443,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4225,6 +4981,16 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4246,6 +5012,16 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4255,51 +5031,896 @@ "node": ">= 0.4" } }, - "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, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=8.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, - "engines": { - "node": "*" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minipass": { - "version": "7.1.2", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, @@ -4337,8 +5958,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { "version": "3.3.11", @@ -4456,6 +6076,18 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4647,6 +6279,49 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==", + "license": "ISC" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4790,6 +6465,16 @@ "react-is": "^16.13.1" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4844,6 +6529,32 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-markdown": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.3.tgz", + "integrity": "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4866,6 +6577,77 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/refractor": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.9.0.tgz", + "integrity": "sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^7.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/refractor/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/refractor/node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/refractor/node_modules/hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/refractor/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -4886,6 +6668,247 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", + "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-attr": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-3.0.3.tgz", + "integrity": "sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==", + "license": "MIT", + "dependencies": { + "unified": "~11.0.0", + "unist-util-visit": "~5.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-autolink-headings": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.1.0.tgz", + "integrity": "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-ignore": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-2.0.2.tgz", + "integrity": "sha512-BpAT/3lU9DMJ2siYVD/dSR0A/zQgD6Fb+fxkJd4j+wDVy6TYbYpK+FZqu8eM9EuNKGvi4BJR7XTZ/+zF02Dq8w==", + "license": "MIT", + "dependencies": { + "hast-util-select": "^6.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", + "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-prism-plus": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.1.tgz", + "integrity": "sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==", + "license": "MIT", + "dependencies": { + "hast-util-to-string": "^3.0.0", + "parse-numeric-range": "^1.3.0", + "refractor": "^4.8.0", + "rehype-parse": "^9.0.0", + "unist-util-filter": "^5.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-rewrite": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-4.0.2.tgz", + "integrity": "sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg==", + "license": "MIT", + "dependencies": { + "hast-util-select": "^6.0.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "github-slugger": "^2.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-github-blockquote-alert": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/remark-github-blockquote-alert/-/remark-github-blockquote-alert-1.3.1.tgz", + "integrity": "sha512-OPNnimcKeozWN1w8KVQEuHOxgN3L4rah8geMOLhA5vN9wITqU4FWD+G26tkEsCGHiOVDbISx+Se5rGZ+D1p0Jg==", + "license": "MIT", + "dependencies": { + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -5224,6 +7247,16 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -5350,6 +7383,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -5371,6 +7418,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", + "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.9" + } + }, + "node_modules/style-to-object": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", + "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -5503,6 +7568,32 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -5652,8 +7743,105 @@ "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-filter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz", + "integrity": "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, "node_modules/unrs-resolver": { "version": "1.11.1", @@ -5710,6 +7898,74 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5819,6 +8075,27 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -5839,6 +8116,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index e321af1..6f36559 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,9 @@ "test:install": "playwright install" }, "dependencies": { + "@supabase/supabase-js": "^2.54.0", + "@uiw/react-markdown-preview": "^5.1.5", + "@uiw/react-md-editor": "^4.0.8", "next": "15.4.4", "react": "19.1.0", "react-dom": "19.1.0", diff --git a/playwright-report/data/25da76d18fe7e63ab7cb8f434b4fe5ba0faa3c8c.md b/playwright-report/data/25da76d18fe7e63ab7cb8f434b4fe5ba0faa3c8c.md new file mode 100644 index 0000000..20f1416 --- /dev/null +++ b/playwright-report/data/25da76d18fe7e63ab7cb8f434b4fe5ba0faa3c8c.md @@ -0,0 +1,111 @@ +# Page snapshot + +```yaml +- banner: + - heading "📝 메모 앱" [level=1] + - button "새 메모": + - img + - text: 새 메모 +- main: + - img + - textbox "메모 검색..." + - combobox: + - option "전체 카테고리" [selected] + - option "개인 (0)" + - option "업무 (0)" + - option "학습 (0)" + - option "아이디어 (0)" + - option "기타 (0)" + - text: 총 0개의 메모 + - img + - heading "아직 메모가 없습니다" [level=3] + - paragraph: 첫 번째 메모를 작성해보세요! +- heading "새 메모 작성" [level=2] +- button: + - img +- text: 제목 * +- textbox "제목 *" +- text: 카테고리 +- combobox "카테고리": + - option "개인" + - option "업무" [selected] + - option "학습" + - option "아이디어" + - option "기타" +- text: 내용 * (마크다운 지원) +- list: + - listitem: + - button "Add bold text (ctrl + b)": + - img + - listitem: + - button "Add italic text (ctrl + i)": + - img + - listitem: + - button "Add strikethrough text (ctrl + shift + x)": + - img + - listitem: + - button "Insert HR (ctrl + h)": + - img + - listitem: + - button "Insert title": + - img + - listitem + - listitem: + - button "Add a link (ctrl + l)": + - img + - listitem: + - button "Insert a quote (ctrl + q)": + - img + - listitem: + - button "Insert code (ctrl + j)": + - img + - listitem: + - button "Insert Code Block (ctrl + shift + j)": + - img + - listitem: + - button "Insert comment (ctrl + /)": + - img + - listitem: + - button "Add image (ctrl + k)": + - img + - listitem: + - button "Add table": + - img + - listitem + - listitem: + - button "Add unordered list (ctrl + shift + u)": + - img + - listitem: + - button "Add ordered list (ctrl + shift + o)": + - img + - listitem: + - button "Add checked list (ctrl + shift + c)": + - img + - listitem + - listitem: + - button "Open help": + - img +- list: + - listitem: + - button "Edit code (ctrl + 7)": + - img + - listitem: + - button "Live code (ctrl + 8)": + - img + - listitem: + - button "Preview code (ctrl + 9)": + - img + - listitem + - listitem: + - button "Toggle fullscreen (ctrl + 0)": + - img +- code: 내용만 있는 메모 +- textbox: 내용만 있는 메모 +- paragraph: 내용만 있는 메모 +- text: 태그 +- textbox "태그를 입력하고 Enter를 누르세요" +- button "추가" +- button "취소" +- button "저장하기" +- alert +``` \ No newline at end of file diff --git a/playwright-report/data/2a6e3f481a316869b874683879d9094020130c21.md b/playwright-report/data/2a6e3f481a316869b874683879d9094020130c21.md new file mode 100644 index 0000000..72a20fc --- /dev/null +++ b/playwright-report/data/2a6e3f481a316869b874683879d9094020130c21.md @@ -0,0 +1,116 @@ +# Page snapshot + +```yaml +- banner: + - heading "📝 메모 앱" [level=1] + - button "새 메모": + - img + - text: 새 메모 +- main: + - img + - textbox "메모 검색..." + - combobox: + - option "전체 카테고리" [selected] + - option "개인 (0)" + - option "업무 (0)" + - option "학습 (0)" + - option "아이디어 (0)" + - option "기타 (0)" + - text: 총 0개의 메모 + - img + - heading "아직 메모가 없습니다" [level=3] + - paragraph: 첫 번째 메모를 작성해보세요! +- heading "새 메모 작성" [level=2] +- button: + - img +- text: 제목 * +- textbox "제목 *": 마크다운 테스트 +- text: 카테고리 +- combobox "카테고리": + - option "개인" + - option "업무" [selected] + - option "학습" + - option "아이디어" + - option "기타" +- text: 내용 * (마크다운 지원) +- list: + - listitem: + - button "Add bold text (ctrl + b)": + - img + - listitem: + - button "Add italic text (ctrl + i)": + - img + - listitem: + - button "Add strikethrough text (ctrl + shift + x)": + - img + - listitem: + - button "Insert HR (ctrl + h)": + - img + - listitem: + - button "Insert title": + - img + - listitem + - listitem: + - button "Add a link (ctrl + l)": + - img + - listitem: + - button "Insert a quote (ctrl + q)": + - img + - listitem: + - button "Insert code (ctrl + j)": + - img + - listitem: + - button "Insert Code Block (ctrl + shift + j)": + - img + - listitem: + - button "Insert comment (ctrl + /)": + - img + - listitem: + - button "Add image (ctrl + k)": + - img + - listitem: + - button "Add table": + - img + - listitem + - listitem: + - button "Add unordered list (ctrl + shift + u)": + - img + - listitem: + - button "Add ordered list (ctrl + shift + o)": + - img + - listitem: + - button "Add checked list (ctrl + shift + c)": + - img + - listitem + - listitem: + - button "Open help": + - img +- list: + - listitem: + - button "Edit code (ctrl + 7)": + - img + - listitem: + - button "Live code (ctrl + 8)": + - img + - listitem: + - button "Preview code (ctrl + 9)": + - img + - listitem + - listitem: + - button "Toggle fullscreen (ctrl + 0)": + - img +- code: "# 제목 **굵은 글씨** - 목록 항목 1 - 목록 항목 2" +- textbox: "# 제목 **굵은 글씨** - 목록 항목 1 - 목록 항목 2" +- heading "제목" [level=1] +- paragraph: + - strong: 굵은 글씨 +- list: + - listitem: 목록 항목 1 + - listitem: 목록 항목 2 +- text: 태그 +- textbox "태그를 입력하고 Enter를 누르세요" +- button "추가" +- button "취소" +- button "저장하기" +- alert +``` \ No newline at end of file diff --git a/playwright-report/data/2e0bb665eb9c038554b49d32db23f52fd050064e.md b/playwright-report/data/2e0bb665eb9c038554b49d32db23f52fd050064e.md new file mode 100644 index 0000000..bf52ae0 --- /dev/null +++ b/playwright-report/data/2e0bb665eb9c038554b49d32db23f52fd050064e.md @@ -0,0 +1,43 @@ +# Page snapshot + +```yaml +- banner: + - heading "📝 메모 앱" [level=1] + - button "새 메모": + - img + - text: 새 메모 +- main: + - img + - textbox "메모 검색..." + - combobox: + - option "전체 카테고리" [selected] + - option "개인 (0)" + - option "업무 (0)" + - option "학습 (0)" + - option "아이디어 (0)" + - option "기타 (0)" + - text: 총 0개의 메모 + - img + - heading "아직 메모가 없습니다" [level=3] + - paragraph: 첫 번째 메모를 작성해보세요! +- heading "새 메모 작성" [level=2] +- button: + - img +- text: 제목 * +- textbox "제목 *" +- text: 카테고리 +- combobox "카테고리": + - option "개인" [selected] + - option "업무" + - option "학습" + - option "아이디어" + - option "기타" +- text: 내용 * (마크다운 지원) +- paragraph: 내용을 입력해주세요. +- text: 태그 +- textbox "태그를 입력하고 Enter를 누르세요" +- button "추가" +- button "취소" +- button "저장하기" +- alert +``` \ No newline at end of file diff --git a/playwright-report/data/332e6aeb30cf8b8698861f248263f29aefd65169.md b/playwright-report/data/332e6aeb30cf8b8698861f248263f29aefd65169.md new file mode 100644 index 0000000..b9b009d --- /dev/null +++ b/playwright-report/data/332e6aeb30cf8b8698861f248263f29aefd65169.md @@ -0,0 +1,43 @@ +# Page snapshot + +```yaml +- banner: + - heading "📝 메모 앱" [level=1] + - button "새 메모": + - img + - text: 새 메모 +- main: + - img + - textbox "메모 검색..." + - combobox: + - option "전체 카테고리" [selected] + - option "개인 (0)" + - option "업무 (0)" + - option "학습 (0)" + - option "아이디어 (0)" + - option "기타 (0)" + - text: 총 0개의 메모 + - img + - heading "아직 메모가 없습니다" [level=3] + - paragraph: 첫 번째 메모를 작성해보세요! +- heading "새 메모 작성" [level=2] +- button: + - img +- text: 제목 * +- textbox "제목 *": 제목만 있는 메모 +- text: 카테고리 +- combobox "카테고리": + - option "개인" + - option "업무" [selected] + - option "학습" + - option "아이디어" + - option "기타" +- text: 내용 * (마크다운 지원) +- paragraph: 내용을 입력해주세요. +- text: 태그 +- textbox "태그를 입력하고 Enter를 누르세요" +- button "추가" +- button "취소" +- button "저장하기" +- alert +``` \ No newline at end of file diff --git a/playwright-report/data/58d07e94ad2a9b1d29fc3fe0ec49cce930a8d2a6.md b/playwright-report/data/58d07e94ad2a9b1d29fc3fe0ec49cce930a8d2a6.md new file mode 100644 index 0000000..dc03d20 --- /dev/null +++ b/playwright-report/data/58d07e94ad2a9b1d29fc3fe0ec49cce930a8d2a6.md @@ -0,0 +1,24 @@ +# Page snapshot + +```yaml +- banner: + - heading "📝 메모 앱" [level=1] + - button "새 메모": + - img + - text: 새 메모 +- main: + - img + - textbox "메모 검색..." + - combobox: + - option "전체 카테고리" [selected] + - option "개인 (0)" + - option "업무 (0)" + - option "학습 (0)" + - option "아이디어 (0)" + - option "기타 (0)" + - text: 총 0개의 메모 + - img + - heading "아직 메모가 없습니다" [level=3] + - paragraph: 첫 번째 메모를 작성해보세요! +- alert +``` \ No newline at end of file diff --git a/playwright-report/index.html b/playwright-report/index.html new file mode 100644 index 0000000..5c333bb --- /dev/null +++ b/playwright-report/index.html @@ -0,0 +1,77 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 0d0ba91..12d967b 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,5 +1,8 @@ @import 'tailwindcss'; +@import '@uiw/react-md-editor/markdown-editor.css'; +@import '@uiw/react-markdown-preview/markdown.css'; + :root { --background: #ffffff; --foreground: #171717; diff --git a/src/app/page.tsx b/src/app/page.tsx index ef1a7d8..3f90f8f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -23,14 +23,14 @@ export default function Home() { const [isFormOpen, setIsFormOpen] = useState(false) const [editingMemo, setEditingMemo] = useState(null) - const handleCreateMemo = (formData: MemoFormData) => { - createMemo(formData) + const handleCreateMemo = async (formData: MemoFormData) => { + await createMemo(formData) setIsFormOpen(false) } - const handleUpdateMemo = (formData: MemoFormData) => { + const handleUpdateMemo = async (formData: MemoFormData) => { if (editingMemo) { - updateMemo(editingMemo.id, formData) + await updateMemo(editingMemo.id, formData) setEditingMemo(null) } } diff --git a/src/components/MemoForm.tsx b/src/components/MemoForm.tsx index b4337f0..f2a9bfe 100644 --- a/src/components/MemoForm.tsx +++ b/src/components/MemoForm.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect } from 'react' +import dynamic from 'next/dynamic' import { Memo, MemoFormData, @@ -8,10 +9,16 @@ import { DEFAULT_CATEGORIES, } from '@/types/memo' +// MDEditor를 동적으로 로드 (SSR 방지) +const MDEditor = dynamic( + () => import('@uiw/react-md-editor'), + { ssr: false } +) + interface MemoFormProps { isOpen: boolean onClose: () => void - onSubmit: (data: MemoFormData) => void + onSubmit: (data: MemoFormData) => Promise editingMemo?: Memo | null } @@ -49,14 +56,19 @@ export default function MemoForm({ setTagInput('') }, [editingMemo, isOpen]) - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!formData.title.trim() || !formData.content.trim()) { alert('제목과 내용을 모두 입력해주세요.') return } - onSubmit(formData) - onClose() + try { + await onSubmit(formData) + onClose() + } catch (error) { + console.error('Failed to submit memo:', error) + alert('메모 저장에 실패했습니다.') + } } const handleAddTag = () => { @@ -168,28 +180,30 @@ export default function MemoForm({ - {/* 내용 */} + {/* 내용 - 마크다운 편집기 */}
- aka getByText('제목 *', { exact: true }) + 2) aka getByText('# 제목', { exact: true }) + 3) aka locator('textarea') + 4)

aka getByRole('heading', { name: '제목' }) + + Call log: + - Expect "toBeVisible" with timeout 5000ms + - waiting for getByText('제목') + + + 193 | // 미리보기 영역에서 렌더링된 결과 확인 + 194 | // (미리보기가 실시간으로 업데이트되는 경우) + > 195 | await expect(page.getByText('제목')).toBeVisible(); + | ^ + 196 | await expect(page.locator('strong')).toContainText('굵은 글씨'); + 197 | + 198 | // 저장 + at C:\Users\masocampus\memo-app\tests\e2e\memo-creation.spec.ts:195:40 + + Error Context: ..\test-results\e2e-memo-creation-메모-생성-기능-시나리오-1-마크다운-미리보기-기능-chromium\error-context.md +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/e2e/memo-creation.spec.ts b/tests/e2e/memo-creation.spec.ts new file mode 100644 index 0000000..947d927 --- /dev/null +++ b/tests/e2e/memo-creation.spec.ts @@ -0,0 +1,205 @@ +import { test, expect } from '@playwright/test'; + +/** + * 시나리오 1: 메모 생성 (Critical) + * + * 목적: 사용자가 새 메모를 성공적으로 생성할 수 있는지 확인 + * 파일: memo-management.md, 시나리오 1 + */ + +test.describe('메모 생성 기능', () => { + test.beforeEach(async ({ page }) => { + // 전제조건: 브라우저가 http://localhost:3000에 접속되어 있음 + await page.goto('http://localhost:3000'); + + // 페이지가 완전히 로드될 때까지 대기 + await page.waitForLoadState('networkidle'); + + // localStorage 초기화 (깨끗한 상태에서 테스트 시작) + await page.evaluate(() => localStorage.clear()); + await page.reload(); + await page.waitForLoadState('networkidle'); + }); + + test('시나리오 1: 새 메모 생성 성공', async ({ page }) => { + // 초기 상태 확인: "아직 메모가 없습니다" 메시지 표시 + await expect(page.getByText('아직 메모가 없습니다')).toBeVisible(); + await expect(page.getByText('총 0개의 메모')).toBeVisible(); + + // 1. "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 메모 작성 폼이 표시되는지 확인 + await expect(page.getByHeading('새 메모 작성')).toBeVisible(); + + // 2. 제목 필드에 "E2E 테스트 메모" 입력 + await page.getByRole('textbox', { name: '제목 *' }).fill('E2E 테스트 메모'); + + // 3. 카테고리 드롭다운에서 "업무" 선택 + await page.getByLabel('카테고리').selectOption('업무'); + + // 4. 마크다운 에디터에 테스트 내용 입력 + const markdownContent = `이것은 **E2E 테스트**를 위한 메모입니다. + +## 테스트 항목 +- 메모 생성 ✓ +- 메모 편집 +- 메모 삭제`; + + // 마크다운 에디터의 textarea 찾기 (여러 textarea가 있을 수 있으므로 구체적으로 지정) + const markdownEditor = page.locator('textarea').first(); // 또는 더 구체적인 셀렉터 사용 + await markdownEditor.fill(markdownContent); + + // 5. 태그 입력 필드에 "e2e" 입력 후 "추가" 버튼 클릭 + await page.getByRole('textbox', { name: '태그를 입력하고 Enter를 누르세요' }).fill('e2e'); + await page.getByRole('button', { name: '추가' }).click(); + + // 태그가 추가되었는지 확인 + await expect(page.getByText('#e2e')).toBeVisible(); + + // 6. "저장하기" 버튼 클릭 + await page.getByRole('button', { name: '저장하기' }).click(); + + // 예상 결과 검증 + + // 메모 작성 폼이 닫힘 (새 메모 작성 헤딩이 사라짐) + await expect(page.getByHeading('새 메모 작성')).not.toBeVisible(); + + // 메인 화면에 새 메모가 표시됨 + await expect(page.getByRole('heading', { name: 'E2E 테스트 메모', level: 3 })).toBeVisible(); + + // 메모 카운터가 "총 1개의 메모"로 업데이트됨 + await expect(page.getByText('총 1개의 메모')).toBeVisible(); + + // 메모 제목이 올바르게 표시됨 + await expect(page.getByText('E2E 테스트 메모')).toBeVisible(); + + // 카테고리가 올바르게 표시됨 + await expect(page.getByText('업무')).toBeVisible(); + + // 작성일시가 표시됨 (현재 날짜 포함) + const today = new Date(); + const currentYear = today.getFullYear(); + await expect(page.getByText(new RegExp(`${currentYear}년`))).toBeVisible(); + + // 태그가 "#e2e"로 표시됨 + await expect(page.getByText('#e2e')).toBeVisible(); + + // 마크다운 내용이 HTML로 렌더링되어 표시됨 + await expect(page.getByText('이것은')).toBeVisible(); + await expect(page.getByText('E2E 테스트', { exact: false })).toBeVisible(); + await expect(page.getByText('테스트 항목')).toBeVisible(); + await expect(page.getByText('메모 생성 ✓')).toBeVisible(); + + // 마크다운 볼드 텍스트가 올바르게 렌더링됨 + await expect(page.locator('strong')).toContainText('E2E 테스트'); + }); + + test('시나리오 1: 필수 필드 검증 - 제목 없음', async ({ page }) => { + // "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 제목은 입력하지 않고 내용만 입력 + await page.getByLabel('카테고리').selectOption('업무'); + await page.locator('textarea').first().fill('내용만 있는 메모'); + + // "저장하기" 버튼 클릭 + await page.getByRole('button', { name: '저장하기' }).click(); + + // 실패 조건: 필수 필드가 비어있을 때 저장되지 않음 + // 메모 작성 폼이 여전히 열려있어야 함 + await expect(page.getByHeading('새 메모 작성')).toBeVisible(); + + // 메인 화면에 메모가 추가되지 않음 + await expect(page.getByText('총 0개의 메모')).toBeVisible(); + }); + + test('시나리오 1: 필수 필드 검증 - 내용 없음', async ({ page }) => { + // "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 제목만 입력하고 내용은 입력하지 않음 + await page.getByRole('textbox', { name: '제목 *' }).fill('제목만 있는 메모'); + await page.getByLabel('카테고리').selectOption('업무'); + + // "저장하기" 버튼 클릭 + await page.getByRole('button', { name: '저장하기' }).click(); + + // 실패 조건: 필수 필드가 비어있을 때 저장되지 않음 + // 메모 작성 폼이 여전히 열려있어야 함 + await expect(page.getByHeading('새 메모 작성')).toBeVisible(); + + // 메인 화면에 메모가 추가되지 않음 + await expect(page.getByText('총 0개의 메모')).toBeVisible(); + }); + + test('시나리오 1: 메모 작성 취소 기능', async ({ page }) => { + // "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 일부 데이터 입력 + await page.getByRole('textbox', { name: '제목 *' }).fill('취소할 메모'); + + // "취소" 버튼 클릭 + await page.getByRole('button', { name: '취소' }).click(); + + // 메모 작성 폼이 닫힘 + await expect(page.getByHeading('새 메모 작성')).not.toBeVisible(); + + // 메인 화면에 메모가 추가되지 않음 + await expect(page.getByText('총 0개의 메모')).toBeVisible(); + await expect(page.getByText('아직 메모가 없습니다')).toBeVisible(); + }); + + test('시나리오 1: 다중 태그 추가', async ({ page }) => { + // "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 기본 정보 입력 + await page.getByRole('textbox', { name: '제목 *' }).fill('다중 태그 테스트'); + await page.getByLabel('카테고리').selectOption('업무'); + await page.locator('textarea').first().fill('다중 태그 테스트 내용'); + + // 첫 번째 태그 추가 + await page.getByRole('textbox', { name: '태그를 입력하고 Enter를 누르세요' }).fill('태그1'); + await page.getByRole('button', { name: '추가' }).click(); + await expect(page.getByText('#태그1')).toBeVisible(); + + // 두 번째 태그 추가 + await page.getByRole('textbox', { name: '태그를 입력하고 Enter를 누르세요' }).fill('태그2'); + await page.getByRole('button', { name: '추가' }).click(); + await expect(page.getByText('#태그2')).toBeVisible(); + + // 저장 + await page.getByRole('button', { name: '저장하기' }).click(); + + // 저장 후 두 태그 모두 표시되는지 확인 + await expect(page.getByText('#태그1')).toBeVisible(); + await expect(page.getByText('#태그2')).toBeVisible(); + }); + + test('시나리오 1: 마크다운 미리보기 기능', async ({ page }) => { + // "새 메모" 버튼 클릭 + await page.getByRole('button', { name: '새 메모' }).click(); + + // 기본 정보 입력 + await page.getByRole('textbox', { name: '제목 *' }).fill('마크다운 테스트'); + await page.getByLabel('카테고리').selectOption('업무'); + + // 마크다운 내용 입력 + const markdownText = '# 제목\n\n**굵은 글씨**\n\n- 목록 항목 1\n- 목록 항목 2'; + await page.locator('textarea').first().fill(markdownText); + + // 미리보기 영역에서 렌더링된 결과 확인 + // (미리보기가 실시간으로 업데이트되는 경우) + await expect(page.getByText('제목')).toBeVisible(); + await expect(page.locator('strong')).toContainText('굵은 글씨'); + + // 저장 + await page.getByRole('button', { name: '저장하기' }).click(); + + // 저장 후 메인 화면에서도 마크다운이 올바르게 렌더링되는지 확인 + await expect(page.getByText('제목')).toBeVisible(); + await expect(page.locator('strong')).toContainText('굵은 글씨'); + }); +}); \ No newline at end of file diff --git a/tests/e2e/scenarios/README.md b/tests/e2e/scenarios/README.md new file mode 100644 index 0000000..8fa3977 --- /dev/null +++ b/tests/e2e/scenarios/README.md @@ -0,0 +1,222 @@ +# 메모 앱 E2E 테스트 시나리오 가이드 + +## 📋 개요 + +이 디렉토리는 메모 앱 (http://localhost:3000)의 포괄적인 E2E 테스트 시나리오를 포함합니다. + +## 📁 시나리오 구성 + +### 1. 📝 [memo-management.md](./memo-management.md) +**메모 관리 기능 테스트 시나리오** +- 메모 CRUD 작업 (생성, 조회, 수정, 삭제) +- 검색 및 필터링 기능 +- 태그 관리 +- 마크다운 에디터 기능 + +**포함된 시나리오**: 8개 +- 우선순위: Critical 3개, High 3개, Medium 2개 +- 예상 실행 시간: 15분 + +### 2. 🎨 [ui-accessibility.md](./ui-accessibility.md) +**UI/UX 및 접근성 테스트 시나리오** +- 반응형 디자인 +- 키보드 내비게이션 +- 스크린 리더 지원 +- 시각적 일관성 +- 사용자 피드백 + +**포함된 시나리오**: 10개 +- 우선순위: High 2개, Medium 6개, Low 2개 +- 예상 실행 시간: 25분 + +### 3. ⚡ [performance-reliability.md](./performance-reliability.md) +**성능 및 신뢰성 테스트 시나리오** +- 페이지 로딩 성능 +- 대용량 데이터 처리 +- 네트워크 장애 대응 +- 브라우저 호환성 +- 장시간 사용 안정성 + +**포함된 시나리오**: 10개 +- 우선순위: Critical 2개, High 5개, Medium 3개 +- 예상 실행 시간: 45분 + +## 🚀 빠른 시작 가이드 + +### 1. 환경 준비 +```bash +# 개발 서버 실행 +npm run dev + +# 브라우저에서 http://localhost:3000 접속 확인 +``` + +### 2. 기본 테스트 실행 순서 +1. **memo-management.md** - 핵심 기능 검증 +2. **ui-accessibility.md** - 사용성 및 접근성 검증 +3. **performance-reliability.md** - 성능 및 안정성 검증 + +### 3. 우선순위별 실행 가이드 + +#### 🔥 Critical (최우선) +반드시 통과해야 하는 핵심 기능 테스트 +- memo-management.md: 시나리오 1, 4, 5 +- performance-reliability.md: 시나리오 1, 2 + +#### ⚠️ High (높음) +주요 기능 및 사용자 경험에 영향을 주는 테스트 +- memo-management.md: 시나리오 2, 3, 6 +- ui-accessibility.md: 시나리오 1, 2 +- performance-reliability.md: 시나리오 3, 4, 6, 7, 8 + +#### 📋 Medium (보통) +추가적인 품질 보증을 위한 테스트 +- memo-management.md: 시나리오 7, 8 +- ui-accessibility.md: 시나리오 3, 4, 5, 6, 9, 10 +- performance-reliability.md: 시나리오 9, 10 + +#### 📝 Low (낮음) +완성도를 높이기 위한 선택적 테스트 +- ui-accessibility.md: 시나리오 7, 8 + +## 🛠️ 테스트 도구 및 환경 + +### 필수 도구 +- **Playwright**: E2E 테스트 자동화 +- **브라우저**: Chrome, Firefox, Safari, Edge +- **개발자 도구**: 성능 측정, 접근성 검사 + +### 권장 확장 도구 +- **axe-core**: 접근성 자동 검사 +- **Lighthouse**: 성능 및 품질 감사 +- **WAVE**: 웹 접근성 평가 + +### 테스트 환경 설정 +```javascript +// playwright.config.ts 기본 설정 +export default { + testDir: './tests/e2e', + timeout: 30000, + use: { + baseURL: 'http://localhost:3000', + screenshot: 'only-on-failure', + video: 'retain-on-failure' + }, + projects: [ + { name: 'chromium' }, + { name: 'firefox' }, + { name: 'webkit' } + ] +}; +``` + +## 📊 전체 테스트 매트릭스 + +| 카테고리 | Critical | High | Medium | Low | 총합 | +|----------|----------|------|--------|-----|------| +| 메모 관리 | 3 | 3 | 2 | 0 | 8 | +| UI/접근성 | 0 | 2 | 6 | 2 | 10 | +| 성능/신뢰성 | 2 | 5 | 3 | 0 | 10 | +| **총합** | **5** | **10** | **11** | **2** | **28** | + +## ⏱️ 예상 실행 시간 + +### 우선순위별 실행 시간 +- **Critical**: 15분 (5개 시나리오) +- **High**: 35분 (10개 시나리오) +- **Medium**: 30분 (11개 시나리오) +- **Low**: 5분 (2개 시나리오) + +### 총 실행 시간 +- **최소 필수**: 15분 (Critical만) +- **기본 권장**: 50분 (Critical + High) +- **전체 완료**: 85분 (모든 시나리오) + +## 🎯 성공 기준 + +### 최소 통과 기준 (릴리스 차단) +- Critical 시나리오 100% 통과 +- High 시나리오 90% 이상 통과 +- 접근성 기본 요구사항 충족 + +### 품질 목표 (권장) +- 전체 시나리오 95% 이상 통과 +- 성능 지표 모든 기준 충족 +- WCAG 2.1 AA 레벨 90% 이상 준수 + +## 📝 테스트 실행 체크리스트 + +### 테스트 전 준비사항 +- [ ] 개발 서버 실행 확인 +- [ ] 브라우저 캐시 정리 +- [ ] 개발자 도구 준비 +- [ ] 테스트 데이터 초기화 +- [ ] 네트워크 상태 안정 확인 + +### 테스트 실행 중 +- [ ] 콘솔 에러 모니터링 +- [ ] 성능 지표 기록 +- [ ] 스크린샷/비디오 캡처 +- [ ] 실패 시나리오 상세 기록 +- [ ] 브라우저별 차이점 확인 + +### 테스트 완료 후 +- [ ] 결과 문서화 +- [ ] 버그 리포트 작성 +- [ ] 개선사항 제안 +- [ ] 다음 테스트 계획 수립 +- [ ] 테스트 환경 정리 + +## 🐛 일반적인 문제 해결 + +### 테스트 실행 오류 +```bash +# 포트 충돌 시 +lsof -ti:3000 | xargs kill -9 +npm run dev + +# 캐시 문제 시 +rm -rf .next +npm run build +npm run dev +``` + +### 성능 테스트 이슈 +- 브라우저 확장 프로그램 비활성화 +- 백그라운드 앱 최소화 +- 안정된 네트워크 환경 확보 + +### 접근성 테스트 이슈 +- 스크린 리더 설정 확인 +- 키보드 설정 초기화 +- 고대비 모드 테스트 + +## 📈 지속적 개선 + +### 정기 업데이트 필요사항 +- 새로운 기능 추가 시 시나리오 확장 +- 브라우저 업데이트에 따른 호환성 확인 +- 성능 기준 재검토 및 조정 +- 접근성 가이드라인 업데이트 반영 + +### 메트릭 추적 +- 테스트 통과율 추이 +- 성능 지표 변화 +- 사용자 피드백 반영 +- 버그 발견율 및 해결 시간 + +## 📞 지원 및 문의 + +### 테스트 관련 문의 +- **기술 지원**: 개발팀 +- **접근성 문의**: UX팀 +- **성능 이슈**: 인프라팀 + +### 문서 개선 제안 +이 테스트 시나리오 문서에 대한 개선 제안이나 새로운 시나리오 추가 요청은 언제든 환영합니다. + +--- + +**마지막 업데이트**: 2025년 8월 12일 +**버전**: 1.0.0 +**작성자**: E2E 테스트 팀 \ No newline at end of file diff --git a/tests/e2e/scenarios/memo-management.md b/tests/e2e/scenarios/memo-management.md new file mode 100644 index 0000000..2c3aa96 --- /dev/null +++ b/tests/e2e/scenarios/memo-management.md @@ -0,0 +1,321 @@ +# 메모 관리 기능 E2E 테스트 시나리오 + +## 📋 개요 + +메모 앱의 핵심 CRUD 기능과 검색/필터링 기능에 대한 종합적인 E2E 테스트 시나리오입니다. + +**테스트 대상 URL**: http://localhost:3000 + +## 🎯 테스트 범위 + +### 핵심 기능 +- ✅ 메모 생성 (Create) +- ✅ 메모 조회 (Read) +- ✅ 메모 수정 (Update) +- ✅ 메모 삭제 (Delete) +- ✅ 메모 검색 +- ✅ 카테고리 필터링 +- ✅ 태그 관리 +- ✅ 마크다운 에디터 + +## 🏷️ 시나리오 분류 + +| 우선순위 | 카테고리 | 예상 시간 | 복잡도 | +|---------|----------|----------|--------| +| Critical | 메모 CRUD | 5분 | 중간 | +| High | 검색/필터 | 3분 | 낮음 | +| Medium | UI/UX | 2분 | 낮음 | + +--- + +## 📝 테스트 시나리오 + +### 시나리오 1: 메모 생성 (Critical) + +**목적**: 사용자가 새 메모를 성공적으로 생성할 수 있는지 확인 + +**전제조건**: +- 브라우저가 http://localhost:3000에 접속되어 있음 +- 페이지가 완전히 로드됨 + +**테스트 단계**: +1. "새 메모" 버튼 클릭 +2. 제목 필드에 "E2E 테스트 메모" 입력 +3. 카테고리 드롭다운에서 "업무" 선택 +4. 마크다운 에디터에 다음 내용 입력: + ```markdown + 이것은 **E2E 테스트**를 위한 메모입니다. + + ## 테스트 항목 + - 메모 생성 ✓ + - 메모 편집 + - 메모 삭제 + ``` +5. 태그 입력 필드에 "e2e" 입력 후 "추가" 버튼 클릭 +6. "저장하기" 버튼 클릭 + +**예상 결과**: +- 메모 작성 폼이 닫힘 +- 메인 화면에 새 메모가 표시됨 +- 메모 카운터가 "총 1개의 메모"로 업데이트됨 +- 메모 제목, 카테고리, 작성일시가 올바르게 표시됨 +- 태그가 "#e2e"로 표시됨 +- 마크다운 내용이 HTML로 렌더링되어 표시됨 + +**실패 조건**: +- 필수 필드(제목, 내용)가 비어있을 때 저장되지 않음 +- 폼 검증 오류 메시지가 표시됨 + +--- + +### 시나리오 2: 메모 검색 (High) + +**목적**: 검색 기능이 제목과 내용을 기반으로 올바르게 작동하는지 확인 + +**전제조건**: +- 최소 1개 이상의 메모가 존재함 +- 메모에 "테스트"라는 키워드가 포함되어 있음 + +**테스트 단계**: +1. 검색 입력 필드에 "테스트" 입력 +2. 검색 결과 확인 +3. 검색 입력 필드에 "존재하지않는키워드" 입력 +4. "필터 초기화" 버튼 클릭 + +**예상 결과**: +- "테스트" 검색 시: 해당 키워드가 포함된 메모만 표시 +- 메모 카운터가 "X개 메모 (전체 Y개 중)" 형태로 표시 +- "존재하지않는키워드" 검색 시: "검색 결과가 없습니다" 메시지 표시 +- 필터 초기화 시: 모든 메모가 다시 표시됨 + +**실패 조건**: +- 검색이 대소문자를 구분함 (구분하지 않아야 함) +- 부분 문자열 검색이 작동하지 않음 + +--- + +### 시나리오 3: 카테고리 필터링 (High) + +**목적**: 카테고리별 메모 필터링 기능이 올바르게 작동하는지 확인 + +**전제조건**: +- 서로 다른 카테고리의 메모가 여러 개 존재함 + +**테스트 단계**: +1. 카테고리 드롭다운 확인 (각 카테고리별 메모 개수 포함) +2. "업무" 카테고리 선택 +3. 결과 확인 +4. "전체 카테고리" 선택 + +**예상 결과**: +- 카테고리 드롭다운에 올바른 개수가 표시됨 (예: "업무 (2)") +- 선택한 카테고리의 메모만 표시됨 +- "전체 카테고리" 선택 시 모든 메모가 표시됨 + +**실패 조건**: +- 카테고리 개수가 일치하지 않음 +- 잘못된 카테고리의 메모가 표시됨 + +--- + +### 시나리오 4: 메모 편집 (Critical) + +**목적**: 기존 메모를 성공적으로 수정할 수 있는지 확인 + +**전제조건**: +- 편집할 메모가 1개 이상 존재함 + +**테스트 단계**: +1. 메모 카드의 "편집" 버튼 클릭 +2. 제목을 "수정된 제목"으로 변경 +3. 카테고리를 "학습"으로 변경 +4. 내용에 새로운 텍스트 추가 +5. 새 태그 "수정됨" 추가 +6. "수정하기" 버튼 클릭 + +**예상 결과**: +- 편집 폼이 기존 데이터로 미리 채워져 있음 +- 수정 후 메인 화면에 변경사항이 반영됨 +- 수정된 제목, 카테고리, 내용, 태그가 올바르게 표시됨 + +**실패 조건**: +- 편집 폼이 빈 상태로 열림 +- 수정사항이 저장되지 않음 +- 기존 데이터가 손실됨 + +--- + +### 시나리오 5: 메모 삭제 (Critical) + +**목적**: 메모 삭제 기능이 올바르게 작동하는지 확인 + +**전제조건**: +- 삭제할 메모가 1개 이상 존재함 + +**테스트 단계**: +1. 메모 카드의 "삭제" 버튼 클릭 +2. 확인 다이얼로그가 표시되면 "확인" 클릭 +3. 결과 확인 + +**예상 결과**: +- 삭제 확인 다이얼로그가 표시됨 +- 확인 후 해당 메모가 목록에서 제거됨 +- 메모 카운터가 감소됨 +- 메모가 없을 경우 "아직 메모가 없습니다" 메시지 표시 + +**실패 조건**: +- 확인 다이얼로그 없이 즉시 삭제됨 +- 삭제가 실행되지 않음 +- 다른 메모가 삭제됨 + +--- + +### 시나리오 6: 마크다운 에디터 (Medium) + +**목적**: 마크다운 에디터의 기본 기능이 올바르게 작동하는지 확인 + +**전제조건**: +- 메모 작성 또는 편집 폼이 열려 있음 + +**테스트 단계**: +1. 에디터 도구모음의 **Bold** 버튼 클릭 +2. "굵은 텍스트" 입력 +3. *Italic* 버튼 클릭 +4. "기울임 텍스트" 입력 +5. 목록 버튼 클릭 +6. 목록 항목 입력 +7. 미리보기 탭 확인 + +**예상 결과**: +- 각 도구모음 버튼이 해당 마크다운 문법을 삽입함 +- 미리보기에서 마크다운이 HTML로 올바르게 렌더링됨 +- 에디터와 미리보기 간 실시간 동기화 + +**실패 조건**: +- 도구모음 버튼이 작동하지 않음 +- 마크다운 렌더링이 깨짐 +- 미리보기가 업데이트되지 않음 + +--- + +### 시나리오 7: 태그 관리 (Medium) + +**목적**: 태그 추가/제거 기능이 올바르게 작동하는지 확인 + +**전제조건**: +- 메모 작성 또는 편집 폼이 열려 있음 + +**테스트 단계**: +1. 태그 입력 필드에 "새태그" 입력 +2. "추가" 버튼 클릭 +3. 같은 태그 다시 입력하여 중복 확인 +4. 태그 옆 삭제 버튼(X) 클릭 +5. 빈 태그 입력 시도 + +**예상 결과**: +- 유효한 태그가 추가됨 +- 중복 태그는 추가되지 않음 또는 경고 메시지 표시 +- 태그 삭제 버튼으로 태그 제거 가능 +- 빈 태그는 추가되지 않음 + +**실패 조건**: +- 중복 태그가 추가됨 +- 태그 삭제가 작동하지 않음 +- 빈 태그가 추가됨 + +--- + +### 시나리오 8: 반응형 UI (Low) + +**목적**: 모바일 및 태블릿 화면에서 UI가 올바르게 표시되는지 확인 + +**전제조건**: +- 다양한 화면 크기 테스트 환경 + +**테스트 단계**: +1. 브라우저 창 크기를 모바일 크기(375px)로 조정 +2. 메모 목록 레이아웃 확인 +3. 메모 작성 폼 확인 +4. 태블릿 크기(768px)로 조정하여 재확인 + +**예상 결과**: +- 모든 요소가 화면에 맞게 조정됨 +- 버튼과 입력 필드가 터치하기 쉬운 크기로 표시됨 +- 텍스트가 읽기 쉽게 표시됨 +- 스크롤 없이 주요 기능에 접근 가능 + +**실패 조건**: +- 요소가 화면을 벗어남 +- 버튼이 너무 작아 터치하기 어려움 +- 텍스트가 잘림 + +--- + +## 🧪 엣지 케이스 테스트 + +### 대용량 데이터 처리 +- **시나리오**: 50개 이상의 메모가 있는 상황에서 성능 확인 +- **예상 결과**: 페이지 로딩 및 검색이 2초 이내 완료 + +### 특수 문자 처리 +- **시나리오**: 제목에 이모지, 특수문자 포함 +- **예상 결과**: 모든 문자가 올바르게 표시됨 + +### 긴 텍스트 처리 +- **시나리오**: 10,000자 이상의 메모 내용 +- **예상 결과**: 성능 저하 없이 저장 및 표시 + +### 네트워크 오류 +- **시나리오**: 네트워크 연결 끊김 상황 +- **예상 결과**: localStorage 폴백으로 기능 유지 + +--- + +## 🔍 접근성 테스트 + +### 키보드 내비게이션 +- Tab 키로 모든 요소 접근 가능 +- Enter 키로 버튼 활성화 가능 +- Esc 키로 모달/폼 닫기 가능 + +### 스크린 리더 지원 +- 모든 폼 요소에 적절한 라벨 존재 +- 에러 메시지가 스크린 리더에 전달됨 +- 상태 변화가 적절히 안내됨 + +--- + +## 📊 테스트 커버리지 요약 + +| 기능 영역 | 커버된 시나리오 | 우선순위 분포 | +|-----------|----------------|--------------| +| 메모 CRUD | 4개 시나리오 | Critical: 3, High: 1 | +| 검색/필터링 | 2개 시나리오 | High: 2 | +| UI/UX | 3개 시나리오 | Medium: 2, Low: 1 | +| **총계** | **9개 시나리오** | **Critical: 3, High: 3, Medium: 2, Low: 1** | + +## 🛠️ 테스트 실행 전 체크리스트 + +- [ ] 로컬 개발 서버 실행 (http://localhost:3000) +- [ ] 브라우저 개발자 도구 콘솔 확인 +- [ ] localStorage 초기화 (필요시) +- [ ] 테스트 데이터 준비 +- [ ] Playwright 환경 설정 완료 + +## 📋 추가 고려사항 + +### 성능 테스트 +- 페이지 로딩 시간: < 2초 +- 메모 저장 시간: < 1초 +- 검색 응답 시간: < 500ms + +### 보안 테스트 +- XSS 공격 방지 (마크다운 내용) +- 입력값 검증 +- 안전한 HTML 렌더링 + +### 호환성 테스트 +- Chrome 최신 버전 +- Firefox 최신 버전 +- Safari 최신 버전 (macOS) +- Edge 최신 버전 \ No newline at end of file diff --git a/tests/e2e/scenarios/performance-reliability.md b/tests/e2e/scenarios/performance-reliability.md new file mode 100644 index 0000000..0935672 --- /dev/null +++ b/tests/e2e/scenarios/performance-reliability.md @@ -0,0 +1,453 @@ +# 성능 및 신뢰성 E2E 테스트 시나리오 + +## 📋 개요 + +메모 앱의 성능, 신뢰성, 내구성에 대한 전문적인 E2E 테스트 시나리오입니다. + +**테스트 대상 URL**: http://localhost:3000 + +## 🎯 테스트 범위 + +### 성능 기능 +- ✅ 페이지 로딩 성능 +- ✅ 메모 처리 성능 +- ✅ 검색 성능 +- ✅ 메모리 사용량 +- ✅ 네트워크 효율성 + +### 신뢰성 기능 +- ✅ 데이터 일관성 +- ✅ 에러 복구 +- ✅ 네트워크 장애 대응 +- ✅ 브라우저 호환성 +- ✅ 장시간 사용 안정성 + +## 🏷️ 시나리오 분류 + +| 우선순위 | 카테고리 | 예상 시간 | 복잡도 | +|---------|----------|----------|--------| +| Critical | 기본 성능 | 5분 | 중간 | +| High | 대용량 데이터 | 10분 | 높음 | +| High | 에러 복구 | 7분 | 중간 | +| Medium | 장기 안정성 | 15분 | 높음 | + +--- + +## ⚡ 성능 테스트 시나리오 + +### 시나리오 1: 페이지 로딩 성능 (Critical) + +**목적**: 초기 페이지 로딩이 성능 기준을 만족하는지 확인 + +**전제조건**: +- 네트워크 상태: 일반적인 3G/4G 환경 +- 브라우저 캐시 비활성화 + +**테스트 단계**: +1. 브라우저 성능 도구 활성화 +2. http://localhost:3000 접속 +3. First Contentful Paint (FCP) 측정 +4. Largest Contentful Paint (LCP) 측정 +5. Time to Interactive (TTI) 측정 +6. Cumulative Layout Shift (CLS) 측정 + +**성능 기준**: +- **FCP**: < 1.8초 +- **LCP**: < 2.5초 +- **TTI**: < 3.8초 +- **CLS**: < 0.1 +- **Total Blocking Time**: < 300ms + +**예상 결과**: +- 모든 성능 지표가 기준치 이하 +- 사용자가 의미 있는 콘텐츠를 빠르게 볼 수 있음 +- 레이아웃 이동 최소화 + +**실패 조건**: +- 기준치를 초과하는 성능 지표 +- 화면 깜빡임이나 레이아웃 점프 +- 장시간 빈 화면 표시 + +--- + +### 시나리오 2: 메모 CRUD 성능 (Critical) + +**목적**: 메모 생성, 수정, 삭제 작업의 응답 시간이 적절한지 확인 + +**전제조건**: +- 기존 메모 10개 이상 존재 + +**테스트 단계**: +1. 새 메모 작성 시간 측정 (폼 열기 → 저장 완료) +2. 기존 메모 편집 시간 측정 (편집 시작 → 저장 완료) +3. 메모 삭제 시간 측정 (삭제 클릭 → UI 업데이트) +4. 연속 작업 시 성능 저하 확인 +5. 메모리 사용량 모니터링 + +**성능 기준**: +- **메모 저장**: < 500ms +- **메모 편집**: < 300ms +- **메모 삭제**: < 200ms +- **UI 업데이트**: < 100ms + +**예상 결과**: +- 빠른 응답 시간으로 원활한 사용자 경험 +- 연속 작업에도 성능 저하 없음 +- 메모리 누수 없음 + +**실패 조건**: +- 기준치 초과하는 응답 시간 +- 연속 작업 시 점진적 성능 저하 +- 메모리 사용량 지속 증가 + +--- + +### 시나리오 3: 검색 성능 (High) + +**목적**: 다양한 검색 조건에서 검색 성능이 일정한지 확인 + +**전제조건**: +- 테스트용 메모 100개 이상 준비 +- 다양한 키워드와 카테고리 포함 + +**테스트 단계**: +1. 단순 키워드 검색 성능 측정 +2. 복합 키워드 검색 성능 측정 +3. 카테고리 필터링 성능 측정 +4. 검색어 입력 중 실시간 필터링 성능 측정 +5. 검색 결과 없는 경우 성능 측정 + +**성능 기준**: +- **검색 응답 시간**: < 300ms +- **필터링 응답 시간**: < 200ms +- **실시간 검색**: < 150ms (키 입력당) + +**예상 결과**: +- 메모 수에 관계없이 일정한 검색 성능 +- 타이핑 중에도 부드러운 반응 +- 검색 결과 하이라이팅 빠른 적용 + +**실패 조건**: +- 메모 수 증가에 따른 검색 성능 저하 +- 타이핑 중 지연 또는 끊김 +- 검색 결과 표시 지연 + +--- + +## 📊 대용량 데이터 시나리오 + +### 시나리오 4: 대용량 메모 처리 (High) + +**목적**: 많은 수의 메모가 있을 때도 앱이 정상 작동하는지 확인 + +**전제조건**: +- 테스트용 메모 500개 이상 생성 +- 다양한 크기의 메모 포함 (단문 ~ 10KB) + +**테스트 단계**: +1. 500개 메모 환경에서 페이지 로딩 확인 +2. 메모 목록 스크롤 성능 확인 +3. 새 메모 추가 시 성능 영향 확인 +4. 전체 검색 성능 확인 +5. 메모리 사용량 모니터링 + +**성능 기준**: +- **페이지 로딩**: < 3초 +- **스크롤 FPS**: > 30fps +- **메모 추가**: < 1초 +- **전체 검색**: < 500ms + +**예상 결과**: +- 많은 메모에도 부드러운 스크롤 +- 메모 추가 시 성능 저하 최소 +- 검색 성능 유지 +- 가상화 또는 페이지네이션으로 최적화 + +**실패 조건**: +- 페이지 로딩 실패 또는 과도한 지연 +- 스크롤 중 끊김 또는 지연 +- 메모 추가 시 전체 앱 느려짐 + +--- + +### 시나리오 5: 대용량 메모 내용 처리 (High) + +**목적**: 큰 크기의 메모 내용을 효율적으로 처리하는지 확인 + +**전제조건**: +- 10KB 이상의 긴 텍스트 메모 준비 +- 복잡한 마크다운 구조 포함 + +**테스트 단계**: +1. 10KB 메모 열기 성능 측정 +2. 대용량 메모 편집 반응성 확인 +3. 마크다운 렌더링 성능 확인 +4. 메모 저장 시간 측정 +5. 검색에서 대용량 메모 처리 확인 + +**성능 기준**: +- **메모 열기**: < 1초 +- **편집 반응성**: < 100ms (키 입력당) +- **마크다운 렌더링**: < 500ms +- **저장 시간**: < 2초 + +**예상 결과**: +- 큰 메모도 빠르게 열림 +- 편집 중 지연 없음 +- 마크다운 미리보기 부드러운 업데이트 +- 점진적 로딩으로 최적화 + +**실패 조건**: +- 대용량 메모 열기 실패 +- 편집 중 심한 지연 +- 마크다운 렌더링 실패 또는 과도한 지연 + +--- + +## 🛡️ 신뢰성 테스트 시나리오 + +### 시나리오 6: 네트워크 장애 대응 (High) + +**목적**: 네트워크 문제 상황에서 앱의 복구 능력 확인 + +**전제조건**: +- 브라우저 개발자 도구로 네트워크 제어 가능 +- localStorage 기반 백업 시스템 활용 + +**테스트 단계**: +1. 정상 상태에서 메모 작성 +2. 네트워크 연결 차단 +3. 오프라인 상태에서 메모 작성 시도 +4. 네트워크 복구 +5. 데이터 동기화 확인 + +**예상 결과**: +- 오프라인 상태 감지 및 사용자 알림 +- localStorage로 데이터 임시 저장 +- 네트워크 복구 시 자동 동기화 +- 데이터 손실 없음 + +**실패 조건**: +- 오프라인 상태 미감지 +- 데이터 손실 발생 +- 네트워크 복구 후 동기화 실패 + +--- + +### 시나리오 7: 브라우저 호환성 (Medium) + +**목적**: 주요 브라우저에서 일관된 성능과 기능 제공 확인 + +**전제조건**: +- Chrome, Firefox, Safari, Edge 최신 버전 +- 동일한 테스트 데이터셋 + +**테스트 단계**: +1. 각 브라우저에서 기본 기능 테스트 +2. 성능 지표 비교 측정 +3. localStorage 동작 확인 +4. 마크다운 렌더링 일관성 확인 +5. 키보드 단축키 동작 확인 + +**성능 기준**: +- 브라우저 간 성능 차이 < 20% +- 모든 기능 정상 동작 +- 일관된 UI/UX + +**예상 결과**: +- 모든 브라우저에서 동일한 기능 제공 +- 성능 차이 최소화 +- 브라우저별 최적화 적용 + +**실패 조건**: +- 특정 브라우저에서 기능 제한 +- 심각한 성능 차이 +- UI 렌더링 문제 + +--- + +### 시나리오 8: 데이터 일관성 (High) + +**목적**: 동시 편집이나 복잡한 작업에서 데이터 무결성 유지 확인 + +**전제조건**: +- 여러 브라우저 탭에서 동일 앱 열기 + +**테스트 단계**: +1. 탭 A에서 메모 생성 +2. 탭 B에서 동일 메모 편집 +3. 동시 저장 시도 +4. localStorage 동기화 확인 +5. 데이터 충돌 해결 확인 + +**예상 결과**: +- 데이터 충돌 감지 및 해결 +- 마지막 수정 우선 또는 병합 전략 +- 사용자에게 충돌 상황 알림 +- 데이터 손실 방지 + +**실패 조건**: +- 데이터 손실 또는 손상 +- 충돌 상황 미감지 +- 예측 불가능한 동작 + +--- + +## 🔄 장기 안정성 시나리오 + +### 시나리오 9: 장시간 사용 안정성 (Medium) + +**목적**: 장시간 연속 사용 시에도 성능 저하나 오류 없이 작동하는지 확인 + +**전제조건**: +- 최소 30분 연속 테스트 환경 +- 자동화된 반복 작업 스크립트 + +**테스트 단계**: +1. 30분간 메모 CRUD 작업 반복 (100회 이상) +2. 10분마다 성능 지표 측정 +3. 메모리 사용량 모니터링 +4. 브라우저 콘솔 에러 확인 +5. UI 반응성 확인 + +**성능 기준**: +- 메모리 사용량 선형 증가 없음 +- 성능 지표 10% 이상 저하 없음 +- JavaScript 에러 발생 없음 + +**예상 결과**: +- 장시간 사용에도 일정한 성능 유지 +- 메모리 누수 없음 +- 안정적인 동작 + +**실패 조건**: +- 점진적 성능 저하 +- 메모리 사용량 지속 증가 +- 에러 발생 또는 앱 중단 + +--- + +### 시나리오 10: 스트레스 테스트 (Medium) + +**목적**: 극한 상황에서의 앱 동작과 복구 능력 확인 + +**전제조건**: +- 대용량 테스트 데이터 +- 시스템 리소스 모니터링 도구 + +**테스트 단계**: +1. 1000개 메모 환경 구성 +2. 동시에 여러 메모 편집 +3. 빠른 속도로 연속 검색 +4. 브라우저 탭 다중 열기 +5. 시스템 리소스 한계 상황 시뮬레이션 + +**예상 결과**: +- 극한 상황에서도 기본 기능 유지 +- 적절한 에러 처리 및 사용자 알림 +- 시스템 리소스 효율적 사용 +- 복구 가능한 실패 + +**실패 조건**: +- 앱 완전 중단 +- 데이터 손실 +- 시스템 리소스 과다 사용 +- 복구 불가능한 상태 + +--- + +## 📈 성능 모니터링 지표 + +### Core Web Vitals +- **Largest Contentful Paint (LCP)**: < 2.5초 +- **First Input Delay (FID)**: < 100ms +- **Cumulative Layout Shift (CLS)**: < 0.1 + +### 추가 성능 지표 +- **First Contentful Paint (FCP)**: < 1.8초 +- **Time to Interactive (TTI)**: < 3.8초 +- **Total Blocking Time (TBT)**: < 300ms +- **Speed Index**: < 3.4초 + +### 리소스 사용량 +- **메모리 사용량**: < 100MB +- **CPU 사용률**: < 50% (유휴 시) +- **네트워크 요청**: 최소화 +- **번들 크기**: < 1MB + +--- + +## 🛠️ 성능 측정 도구 + +### 브라우저 도구 +- **Chrome DevTools**: Performance, Network, Memory 탭 +- **Firefox Profiler**: 성능 프로파일링 +- **Safari Web Inspector**: 타임라인 분석 + +### 외부 도구 +- **Lighthouse**: 전체적인 성능 감사 +- **WebPageTest**: 상세한 성능 분석 +- **GTmetrix**: 성능 및 최적화 권장사항 + +### 모니터링 메트릭 +```javascript +// 성능 측정 예시 코드 +const observer = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + console.log(entry.name, entry.duration); + } +}); +observer.observe({entryTypes: ['measure', 'navigation']}); +``` + +--- + +## 🎯 성능 최적화 체크리스트 + +### 프론트엔드 최적화 +- [ ] 코드 스플리팅 적용 +- [ ] 이미지 최적화 (WebP, 적절한 크기) +- [ ] CSS/JS 압축 및 번들링 +- [ ] 불필요한 라이브러리 제거 +- [ ] 트리 쉐이킹 적용 + +### 런타임 최적화 +- [ ] 가상화 스크롤링 구현 (대용량 목록) +- [ ] 메모이제이션 적용 (React.memo, useMemo) +- [ ] 디바운싱/스로틀링 적용 (검색) +- [ ] 지연 로딩 구현 +- [ ] 효율적인 상태 관리 + +### 데이터 최적화 +- [ ] localStorage 효율적 사용 +- [ ] 불필요한 데이터 정리 +- [ ] 압축 알고리즘 적용 +- [ ] 인덱싱 최적화 + +--- + +## 📊 테스트 보고서 템플릿 + +### 성능 측정 결과 +| 지표 | 목표값 | 측정값 | 상태 | +|------|--------|--------|------| +| LCP | < 2.5초 | X.X초 | ✅/❌ | +| FID | < 100ms | XXms | ✅/❌ | +| CLS | < 0.1 | X.XX | ✅/❌ | + +### 신뢰성 테스트 결과 +- **네트워크 장애 복구**: 성공/실패 +- **브라우저 호환성**: X/4 통과 +- **데이터 일관성**: 유지/손실 +- **장기 안정성**: XX시간 테스트 완료 + +### 권장사항 +1. **즉시 수정 필요**: + - +2. **성능 개선 권장**: + - +3. **장기 계획**: + - + +### 결론 +전체적인 성능과 신뢰성 평가 및 권장사항 요약 \ No newline at end of file diff --git a/tests/e2e/scenarios/ui-accessibility.md b/tests/e2e/scenarios/ui-accessibility.md new file mode 100644 index 0000000..7bea1a8 --- /dev/null +++ b/tests/e2e/scenarios/ui-accessibility.md @@ -0,0 +1,410 @@ +# UI/UX 및 접근성 E2E 테스트 시나리오 + +## 📋 개요 + +메모 앱의 사용자 인터페이스, 사용자 경험, 그리고 접근성 기능에 대한 전문적인 E2E 테스트 시나리오입니다. + +**테스트 대상 URL**: http://localhost:3000 + +## 🎯 테스트 범위 + +### UI/UX 기능 +- ✅ 반응형 디자인 +- ✅ 사용자 피드백 (로딩, 성공, 오류 상태) +- ✅ 애니메이션 및 전환 효과 +- ✅ 다크모드 지원 (있는 경우) +- ✅ 레이아웃 일관성 + +### 접근성 기능 +- ✅ 키보드 내비게이션 +- ✅ 스크린 리더 지원 +- ✅ 색상 대비 +- ✅ 포커스 표시 +- ✅ ARIA 속성 + +## 🏷️ 시나리오 분류 + +| 우선순위 | 카테고리 | 예상 시간 | 복잡도 | +|---------|----------|----------|--------| +| High | 반응형 UI | 3분 | 중간 | +| Medium | 키보드 접근성 | 4분 | 중간 | +| Medium | 사용자 피드백 | 2분 | 낮음 | +| Low | 시각적 일관성 | 2분 | 낮음 | + +--- + +## 📱 반응형 디자인 시나리오 + +### 시나리오 1: 모바일 화면 최적화 (High) + +**목적**: 모바일 기기에서 모든 기능이 올바르게 작동하는지 확인 + +**전제조건**: +- 브라우저가 http://localhost:3000에 접속되어 있음 +- 개발자 도구로 화면 크기 조절 가능 + +**테스트 단계**: +1. 브라우저 창 크기를 375px × 667px (iPhone SE)로 조정 +2. 페이지 레이아웃 확인 +3. "새 메모" 버튼 클릭 가능성 확인 +4. 메모 작성 폼의 입력 필드 사용성 확인 +5. 검색 및 필터 기능 접근성 확인 +6. 메모 목록 스크롤 및 상호작용 확인 + +**예상 결과**: +- 모든 UI 요소가 화면 너비에 맞게 조정됨 +- 버튼 크기가 터치하기 적절함 (최소 44px) +- 텍스트가 읽기 쉬운 크기로 표시됨 +- 가로 스크롤이 발생하지 않음 +- 입력 필드가 키보드로 가려지지 않음 + +**실패 조건**: +- UI 요소가 화면을 벗어남 +- 버튼이 너무 작아서 정확한 터치 어려움 +- 가로 스크롤 발생 +- 텍스트가 잘리거나 겹침 + +--- + +### 시나리오 2: 태블릿 화면 최적화 (High) + +**목적**: 태블릿에서 데스크톱과 모바일 사이의 적절한 레이아웃을 제공하는지 확인 + +**전제조건**: +- 브라우저가 http://localhost:3000에 접속되어 있음 + +**테스트 단계**: +1. 브라우저 창 크기를 768px × 1024px (iPad)로 조정 +2. 메모 목록 표시 방식 확인 +3. 메모 작성 폼 레이아웃 확인 +4. 검색 및 필터 영역 배치 확인 +5. 세로/가로 모드 전환 시뮬레이션 + +**예상 결과**: +- 메모 카드가 적절한 크기로 표시됨 +- 여백과 패딩이 균형있게 조정됨 +- 폼 요소들이 읽기 쉽게 배치됨 +- 세로/가로 모드 모두에서 최적화된 레이아웃 + +**실패 조건**: +- 데스크톱과 동일한 레이아웃으로 표시됨 +- 공간 활용이 비효율적임 +- 세로/가로 모드에서 레이아웃 깨짐 + +--- + +## ⌨️ 키보드 접근성 시나리오 + +### 시나리오 3: 키보드 전용 내비게이션 (Medium) + +**목적**: 마우스 없이 키보드만으로 모든 기능을 사용할 수 있는지 확인 + +**전제조건**: +- 브라우저가 http://localhost:3000에 접속되어 있음 +- 최소 1개의 메모가 존재함 + +**테스트 단계**: +1. Tab 키로 "새 메모" 버튼까지 이동 +2. Enter 키로 메모 작성 폼 열기 +3. Tab 키로 각 입력 필드 간 이동 +4. Shift+Tab으로 역방향 이동 확인 +5. Esc 키로 폼 닫기 +6. Tab 키로 기존 메모의 편집/삭제 버튼 접근 + +**예상 결과**: +- 모든 상호작용 요소에 Tab으로 접근 가능 +- 포커스 순서가 논리적임 +- 포커스가 시각적으로 명확하게 표시됨 +- Enter/Space 키로 버튼 활성화 가능 +- Esc 키로 모달/폼 닫기 가능 + +**실패 조건**: +- 일부 요소에 Tab으로 접근 불가 +- 포커스 순서가 비논리적 +- 포커스 표시가 불명확하거나 없음 +- 키보드로 활성화되지 않는 버튼 + +--- + +### 시나리오 4: 폼 키보드 내비게이션 (Medium) + +**목적**: 메모 작성/편집 폼에서 키보드 내비게이션이 원활한지 확인 + +**전제조건**: +- 메모 작성 또는 편집 폼이 열려 있음 + +**테스트 단계**: +1. Tab 키로 제목 필드에 포커스 +2. Tab 키로 카테고리 드롭다운 이동 +3. Arrow 키로 카테고리 선택 +4. Tab 키로 내용 에디터 이동 +5. Tab 키로 태그 입력 필드 이동 +6. Enter 키로 태그 추가 +7. Tab 키로 저장/취소 버튼 이동 + +**예상 결과**: +- 모든 폼 요소가 Tab 순서에 포함됨 +- 드롭다운에서 Arrow 키 내비게이션 작동 +- 에디터에서 적절한 키보드 단축키 지원 +- Enter 키로 폼 제출 가능 + +**실패 조건**: +- 폼 요소가 Tab 순서에서 제외됨 +- 드롭다운 키보드 조작 불가 +- 에디터에서 키보드 기능 제한 + +--- + +## 🔊 스크린 리더 지원 시나리오 + +### 시나리오 5: 스크린 리더 라벨링 (Medium) + +**목적**: 스크린 리더 사용자가 앱을 이해하고 사용할 수 있는지 확인 + +**전제조건**: +- 스크린 리더 테스트 도구 또는 브라우저 접근성 검사 도구 사용 + +**테스트 단계**: +1. 페이지 제목과 주요 랜드마크 확인 +2. 폼 라벨과 입력 필드 연결 확인 +3. 버튼의 목적 설명 확인 +4. 에러 메시지와 성공 메시지 전달 확인 +5. 메모 목록의 구조적 정보 전달 확인 + +**예상 결과**: +- 모든 폼 요소에 적절한 라벨 존재 +- 버튼에 명확한 목적 설명 +- 에러/성공 상태가 음성으로 전달됨 +- 리스트 구조와 항목 수 정보 제공 +- 랜드마크 역할로 페이지 구조 명확 + +**실패 조건**: +- 라벨이 없는 폼 요소 존재 +- 버튼 목적이 불명확 +- 상태 변화가 전달되지 않음 +- 구조 정보 부족 + +--- + +### 시나리오 6: ARIA 속성 활용 (Medium) + +**목적**: ARIA 속성이 올바르게 구현되어 접근성을 향상시키는지 확인 + +**전제조건**: +- 브라우저 개발자 도구의 접근성 탭 활용 + +**테스트 단계**: +1. 라이브 리전(검색 결과) 업데이트 확인 +2. 확장 가능한 요소의 aria-expanded 상태 확인 +3. 비활성화된 요소의 aria-disabled 상태 확인 +4. 필수 입력 필드의 aria-required 속성 확인 +5. 에러 상태의 aria-invalid 속성 확인 + +**예상 결과**: +- 검색 결과 변경 시 라이브 리전 업데이트 +- 드롭다운/모달 상태가 정확히 전달됨 +- 필수/선택 필드 구분 명확 +- 에러 상태가 접근성 API에 전달됨 + +**실패 조건**: +- ARIA 속성이 누락되거나 잘못됨 +- 동적 콘텐츠 변화가 전달되지 않음 +- 상태 정보가 부정확함 + +--- + +## 🎨 시각적 일관성 시나리오 + +### 시나리오 7: 색상 대비 및 가독성 (Low) + +**목적**: 시각적 요소들이 접근성 가이드라인을 준수하는지 확인 + +**전제조건**: +- 색상 대비 측정 도구 사용 가능 + +**테스트 단계**: +1. 주요 텍스트의 배경 대비 측정 (4.5:1 이상) +2. 버튼과 링크의 색상 대비 확인 +3. 플레이스홀더 텍스트 가독성 확인 +4. 에러/성공 메시지 색상 의존성 확인 +5. 다크모드 지원 시 대비 확인 + +**예상 결과**: +- 모든 텍스트가 WCAG AA 기준 (4.5:1) 충족 +- 색상만으로 정보를 전달하지 않음 +- 충분한 시각적 구분 +- 다크모드에서도 적절한 대비 유지 + +**실패 조건**: +- 색상 대비 부족으로 가독성 저하 +- 색상에만 의존한 정보 전달 +- 시각적 구분 부족 + +--- + +### 시나리오 8: 애니메이션 및 모션 (Low) + +**목적**: 애니메이션이 사용자 경험을 향상시키고 접근성을 해치지 않는지 확인 + +**전제조건**: +- 브라우저에서 prefers-reduced-motion 설정 테스트 가능 + +**테스트 단계**: +1. 메모 추가/삭제 시 애니메이션 확인 +2. 모달 열기/닫기 전환 효과 확인 +3. 로딩 스피너 동작 확인 +4. prefers-reduced-motion 설정 시 동작 확인 +5. 애니메이션 지속 시간 적절성 확인 + +**예상 결과**: +- 부드럽고 자연스러운 전환 효과 +- 과도하지 않은 애니메이션 (< 500ms) +- reduced-motion 설정 시 애니메이션 비활성화 +- 로딩 상태의 명확한 시각적 피드백 + +**실패 조건**: +- 애니메이션이 너무 빠르거나 느림 +- reduced-motion 설정 무시 +- 깜빡임이나 번쩍임 효과로 발작 위험 + +--- + +## 💬 사용자 피드백 시나리오 + +### 시나리오 9: 로딩 상태 표시 (Medium) + +**목적**: 사용자에게 적절한 로딩 피드백이 제공되는지 확인 + +**전제조건**: +- 네트워크 속도 제한으로 로딩 시간 시뮬레이션 가능 + +**테스트 단계**: +1. 페이지 첫 로딩 시 로딩 인디케이터 확인 +2. 메모 저장 시 버튼 상태 변화 확인 +3. 검색 중 로딩 표시 확인 +4. 대용량 메모 로딩 시 피드백 확인 + +**예상 결과**: +- 명확한 로딩 인디케이터 표시 +- 저장 중 버튼 비활성화 및 텍스트 변경 +- 검색 중 적절한 피드백 +- 예상 대기 시간 안내 (필요시) + +**실패 조건**: +- 로딩 상태 표시 없음 +- 중복 클릭 방지 부족 +- 사용자가 진행 상황을 알 수 없음 + +--- + +### 시나리오 10: 에러 및 성공 메시지 (Medium) + +**목적**: 사용자 액션에 대한 적절한 피드백이 제공되는지 확인 + +**전제조건**: +- 다양한 에러 상황 시뮬레이션 가능 + +**테스트 단계**: +1. 필수 필드 미입력 시 에러 메시지 확인 +2. 메모 저장 성공 시 성공 메시지 확인 +3. 네트워크 에러 시 에러 처리 확인 +4. 메시지 자동 소멸 기능 확인 +5. 메시지 위치 및 스타일 적절성 확인 + +**예상 결과**: +- 명확하고 이해하기 쉬운 메시지 +- 적절한 위치에 표시 (사용자 시선 범위) +- 자동 소멸 또는 수동 닫기 기능 +- 에러 시 해결 방법 제시 + +**실패 조건**: +- 모호하거나 기술적인 에러 메시지 +- 메시지가 눈에 띄지 않는 위치 +- 에러 해결 방법 안내 부족 +- 성공 피드백 누락 + +--- + +## 📊 접근성 체크리스트 + +### WCAG 2.1 AA 준수 항목 + +#### 인식 가능성 (Perceivable) +- [ ] 텍스트 대안 제공 (이미지 alt 속성) +- [ ] 충분한 색상 대비 (4.5:1 이상) +- [ ] 색상 외 다른 방법으로 정보 전달 +- [ ] 텍스트 크기 조정 가능 (200%까지) + +#### 조작 가능성 (Operable) +- [ ] 키보드로 모든 기능 접근 가능 +- [ ] 키보드 트랩 없음 +- [ ] 적절한 시간 제한 설정 +- [ ] 발작 유발 콘텐츠 없음 + +#### 이해 가능성 (Understandable) +- [ ] 페이지 언어 설정 +- [ ] 일관된 내비게이션 +- [ ] 명확한 라벨과 지침 +- [ ] 에러 식별 및 해결 방법 제시 + +#### 견고성 (Robust) +- [ ] 유효한 HTML 마크업 +- [ ] 적절한 ARIA 속성 사용 +- [ ] 호환성 높은 코드 작성 + +--- + +## 🛠️ 테스트 도구 및 환경 + +### 권장 테스트 도구 +- **접근성 검사**: axe-core, Lighthouse +- **스크린 리더**: NVDA, JAWS, VoiceOver +- **색상 대비**: Colour Contrast Analyser +- **키보드 테스트**: 브라우저 내장 도구 + +### 테스트 환경 +- **브라우저**: Chrome, Firefox, Safari, Edge +- **화면 크기**: 375px, 768px, 1024px, 1440px +- **운영체제**: Windows, macOS, iOS, Android + +--- + +## 🎯 성공 기준 + +### 최소 통과 기준 +- 모든 Critical 시나리오 100% 통과 +- WCAG 2.1 AA 기준 90% 이상 준수 +- 키보드 내비게이션 100% 지원 + +### 우수 기준 +- 모든 시나리오 95% 이상 통과 +- WCAG 2.1 AAA 기준 80% 이상 준수 +- 다양한 보조 기술에서 완벽 동작 + +--- + +## 📝 테스트 보고서 템플릿 + +### 실행 정보 +- **테스트 일시**: +- **테스트 환경**: +- **테스터**: + +### 결과 요약 +- **전체 시나리오**: X개 +- **통과**: X개 +- **실패**: X개 +- **성공률**: X% + +### 주요 발견사항 +1. **긍정적 측면**: + - +2. **개선 필요사항**: + - +3. **중요 버그**: + - + +### 권장사항 +1. +2. +3. \ No newline at end of file