diff --git a/.github/workflows/github-jira-issue-sync.yml b/.github/workflows/github-jira-issue-sync.yml index 21d30e9..22d9f80 100644 --- a/.github/workflows/github-jira-issue-sync.yml +++ b/.github/workflows/github-jira-issue-sync.yml @@ -126,34 +126,51 @@ jobs: if: steps.check-jira.outputs.result == 'false' id: description uses: actions/github-script@v7 + env: + TEMPLATE_TYPE: ${{ steps.parse.outputs.template_type }} + ISSUE_URL: ${{ github.event.issue.html_url }} + ISSUE_AUTHOR: ${{ github.event.issue.user.login }} + FIELD_GOAL: ${{ steps.parse.outputs.goal }} + FIELD_SCOPE: ${{ steps.parse.outputs.scope }} + FIELD_BREAKDOWN: ${{ steps.parse.outputs.breakdown }} + FIELD_BACKGROUND: ${{ steps.parse.outputs.background }} + FIELD_AC: ${{ steps.parse.outputs.ac }} + FIELD_DESIGN: ${{ steps.parse.outputs.design }} + FIELD_PARENT: ${{ steps.parse.outputs.parent }} + FIELD_CHANGE: ${{ steps.parse.outputs.change }} + FIELD_IMPACT: ${{ steps.parse.outputs.impact }} + FIELD_TIMEBOX: ${{ steps.parse.outputs.timebox }} + FIELD_QUESTIONS: ${{ steps.parse.outputs.questions }} with: script: | - const templateType = '${{ steps.parse.outputs.template_type }}'; + const templateType = process.env.TEMPLATE_TYPE || 'task'; + const issueUrl = process.env.ISSUE_URL || ''; + const author = process.env.ISSUE_AUTHOR || ''; - let desc = `h3. GitHub Issue\n${{ github.event.issue.html_url }}\n\nh3. Author\n${{ github.event.issue.user.login }}\n\n`; + let desc = 'h3. GitHub Issue\n' + issueUrl + '\n\nh3. Author\n' + author + '\n\n'; switch (templateType) { case 'epic': - desc += `h3. 목표\n${{ steps.parse.outputs.goal }}\n\n`; - desc += `h3. 범위\n${{ steps.parse.outputs.scope }}\n\n`; - desc += `h3. 하위 스토리\n${{ steps.parse.outputs.breakdown }}\n`; + desc += 'h3. 목표\n' + (process.env.FIELD_GOAL || '-') + '\n\n'; + desc += 'h3. 범위\n' + (process.env.FIELD_SCOPE || '-') + '\n\n'; + desc += 'h3. 하위 스토리\n' + (process.env.FIELD_BREAKDOWN || '-') + '\n'; break; case 'story': - desc += `h3. 배경\n${{ steps.parse.outputs.background }}\n\n`; - desc += `h3. 수용 기준(AC)\n${{ steps.parse.outputs.ac }}\n\n`; - desc += `h3. 디자인\n${{ steps.parse.outputs.design }}\n`; + desc += 'h3. 배경\n' + (process.env.FIELD_BACKGROUND || '-') + '\n\n'; + desc += 'h3. 수용 기준(AC)\n' + (process.env.FIELD_AC || '-') + '\n\n'; + desc += 'h3. 디자인\n' + (process.env.FIELD_DESIGN || '-') + '\n'; break; case 'cr': - desc += `h3. 제안 변경 사항\n${{ steps.parse.outputs.change }}\n\n`; - desc += `h3. 영향도\n${{ steps.parse.outputs.impact }}\n`; + desc += 'h3. 제안 변경 사항\n' + (process.env.FIELD_CHANGE || '-') + '\n\n'; + desc += 'h3. 영향도\n' + (process.env.FIELD_IMPACT || '-') + '\n'; break; case 'spike': - desc += `h3. 타임박스\n${{ steps.parse.outputs.timebox }}\n\n`; - desc += `h3. 핵심 질문\n${{ steps.parse.outputs.questions }}\n`; + desc += 'h3. 타임박스\n' + (process.env.FIELD_TIMEBOX || '-') + '\n\n'; + desc += 'h3. 핵심 질문\n' + (process.env.FIELD_QUESTIONS || '-') + '\n'; break; - default: // task - desc += `h3. 연결된 Story/Epic\n${{ steps.parse.outputs.parent }}\n\n`; - desc += `h3. 작업 범위\n${{ steps.parse.outputs.scope }}\n`; + default: + desc += 'h3. 연결된 Story/Epic\n' + (process.env.FIELD_PARENT || '-') + '\n\n'; + desc += 'h3. 작업 범위\n' + (process.env.FIELD_SCOPE || '-') + '\n'; } core.setOutput('content', desc); @@ -289,11 +306,12 @@ jobs: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + DESC_JSON: ${{ steps.parse.outputs.description }} with: script: | const jiraKey = '${{ steps.get-jira-key.outputs.jira_key }}'; const issue = context.payload.issue; - const descContent = ${{ steps.parse.outputs.description }}; + const descContent = JSON.parse(process.env.DESC_JSON); const response = await fetch( `${process.env.JIRA_BASE_URL}/rest/api/3/issue/${jiraKey}`, diff --git a/.github/workflows/github-jira-pr-sync.yml b/.github/workflows/github-jira-pr-sync.yml index f1d3aea..5f7747b 100644 --- a/.github/workflows/github-jira-pr-sync.yml +++ b/.github/workflows/github-jira-pr-sync.yml @@ -204,11 +204,13 @@ jobs: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + DESC_JSON: ${{ steps.parse.outputs.description }} + with: script: | const jiraKey = '${{ steps.get-jira-key.outputs.jira_key }}'; const pr = context.payload.pull_request; - const descContent = ${{ steps.parse.outputs.description }}; + const descContent = JSON.parse(process.env.DESC_JSON); const response = await fetch( `${process.env.JIRA_BASE_URL}/rest/api/3/issue/${jiraKey}`, diff --git a/package-lock.json b/package-lock.json index 0f07aed..f9e95b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,18 @@ "version": "0.0.0", "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^7.0.0", - "@mui/material": "^7.0.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.7", + "@mui/material": "^7.3.7", "@mui/x-date-pickers": "^8.0.0", "@reduxjs/toolkit": "^2.0.0", - "axios": "^1.7.0", + "aws-amplify": "^6.15.9", + "axios": "^1.13.2", "date-fns": "^4.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-redux": "^9.0.0", - "react-router-dom": "^7.0.0" + "react-router-dom": "^7.12.0" }, "devDependencies": { "@eslint/js": "^9.0.0", @@ -33,1533 +34,1831 @@ "vite": "^6.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", + "node_modules/@aws-amplify/analytics": { + "version": "7.0.90", + "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-7.0.90.tgz", + "integrity": "sha512-Z/YA2dtYNu2ybs+MFc+ceQexCIYJmK9yUm5E6mLYPR2H4fCvINaUE9V0my6A4+1zwlmvRMWbnZO6Z1XKx02m4A==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "@aws-sdk/client-firehose": "3.621.0", + "@aws-sdk/client-kinesis": "3.621.0", + "@aws-sdk/client-personalize-events": "3.621.0", + "@smithy/util-utf8": "2.0.0", + "tslib": "^2.5.0" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-amplify/api": { + "version": "6.3.21", + "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-6.3.21.tgz", + "integrity": "sha512-oo3y1gCYlVBEco0d8INtBugPP4p3RRKE3XAAXazC26Xp2Reb0QWpwRjWPWn8qNH1a9LgnpMktwWXWx+fN99KJQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/api-graphql": "4.8.2", + "@aws-amplify/api-rest": "4.6.0", + "@aws-amplify/data-schema": "^1.7.0", + "rxjs": "^7.8.1", + "tslib": "^2.5.0" + }, + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/api-graphql": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-4.8.2.tgz", + "integrity": "sha512-Gpem7cf0+YAhla7s/Qmc0eApIAc9Ufo5LHTqm8Hdlr6G0T1H5fUrCk1PzDroIfbDjSw8gqdeCTTGPZPpFezGyw==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" + "@aws-amplify/api-rest": "4.6.0", + "@aws-amplify/core": "6.15.0", + "@aws-amplify/data-schema": "^1.7.0", + "@aws-sdk/types": "3.387.0", + "graphql": "15.8.0", + "rxjs": "^7.8.1", + "tslib": "^2.5.0", + "uuid": "^11.0.0" + } + }, + "node_modules/@aws-amplify/api-rest": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-4.6.0.tgz", + "integrity": "sha512-BUOgnw6cZ9ufCaG72QDt9KAtGLhJOj2DAFl2WBm7jPu45RnyUVFBEiCNcluVKkaoAJjXAA+ee9DZ5Y/ITZyQlg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" + "node_modules/@aws-amplify/auth": { + "version": "6.17.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-6.17.1.tgz", + "integrity": "sha512-1zDSmX8fzhDFEUroPMyxuqkUFN2lEKPqSFcstl50/stvxVPiymTEpH6FWVTga9mTjK/Hifd7ye4m5gM6gF/Y4A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "5.2.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.5.0" + }, + "peerDependencies": { + "@aws-amplify/core": "^6.1.0", + "@aws-amplify/react-native": "^1.1.10" + }, + "peerDependenciesMeta": { + "@aws-amplify/react-native": { + "optional": true + } + } }, - "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", - "license": "MIT", + "node_modules/@aws-amplify/core": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-6.15.0.tgz", + "integrity": "sha512-D08O7GaF6bDcwhKBkSURus5vJGdaNrTeIEZk9OQiDe3jiR1HOiNq7djIZXUWLc3FuiJ1Yhf2aHTD9/Gj6fqQxw==", + "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/types": "3.398.0", + "@smithy/util-hex-encoding": "2.0.0", + "@types/uuid": "^9.0.0", + "js-cookie": "^3.0.5", + "rxjs": "^7.8.1", + "tslib": "^2.5.0", + "uuid": "^11.0.0" + } + }, + "node_modules/@aws-amplify/core/node_modules/@aws-sdk/types": { + "version": "3.398.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", + "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/core/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-amplify/data-schema": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema/-/data-schema-1.22.2.tgz", + "integrity": "sha512-zzRaT81HZwXs/+Q1VVcwE5ZPo5+k8WckFHIXWZz/Nacn1sVtllOba5GfsGVoXreIRkt7nIh/HYdXlQRWXhHfiQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/data-schema-types": "*", + "@smithy/util-base64": "^3.0.0", + "@types/aws-lambda": "^8.10.134", + "@types/json-schema": "^7.0.15", + "rxjs": "^7.8.1" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "license": "MIT", + "node_modules/@aws-amplify/data-schema-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@aws-amplify/data-schema-types/-/data-schema-types-1.2.1.tgz", + "integrity": "sha512-SuYVcy9Hg8Ox9P0QCXEPwqHxX5zVPgVo2YvNBOm5TpkZr4UK6ir3USame7dELZsk5/9f6KoP70QAYhTvp/j1Og==", + "license": "Apache-2.0", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "graphql": "15.8.0", + "rxjs": "^7.8.1" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/datastore": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-5.1.2.tgz", + "integrity": "sha512-ELIjTLzvw6EP8SCqsaDs0hKtAkiBkN7Z4jHqBiK08z5XY9LhWGL6/s5yZ95TKbwZbXC2t9gdef2uF8jnjHpvFQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" + "@aws-amplify/api": "6.3.21", + "@aws-amplify/api-graphql": "4.8.2", + "buffer": "4.9.2", + "idb": "5.0.6", + "immer": "9.0.6", + "rxjs": "^7.8.1", + "ulid": "^2.3.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, + "node_modules/@aws-amplify/datastore/node_modules/immer": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz", + "integrity": "sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ==", "license": "MIT", - "engines": { - "node": ">=6.9.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-amplify/notifications": { + "version": "2.0.90", + "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-2.0.90.tgz", + "integrity": "sha512-mJGVpR9JbCO+WELIE98QIglndSc5vbMumCDEj/gbNCfXAhqplOu4KQp6lgwnj9XY18nnrQYrSUTfWAdkZUUigg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.398.0", + "lodash": "^4.17.21", + "tslib": "^2.5.0" + }, + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", + "node_modules/@aws-amplify/notifications/node_modules/@aws-sdk/types": { + "version": "3.398.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", + "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/notifications/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.9.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/storage": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-6.11.0.tgz", + "integrity": "sha512-VRc2N+xLrdrajyqxmazRlICBZvly8XElpHfl96CtKKqCnAd3fTlsH0fsUwXCkZcVestn0POGQ1t4ShvRUaDYvQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@aws-sdk/types": "3.398.0", + "@smithy/md5-js": "2.0.7", + "buffer": "4.9.2", + "crc-32": "1.2.2", + "fast-xml-parser": "^4.4.1", + "tslib": "^2.5.0" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@aws-amplify/core": "^6.1.0" } }, - "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", - "license": "MIT", + "node_modules/@aws-amplify/storage/node_modules/@aws-sdk/types": { + "version": "3.398.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", + "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/types": "^7.28.5" - }, - "bin": { - "parser": "bin/babel-parser.js" + "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-amplify/storage/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=14.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=16.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" } }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "license": "MIT", + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", - "debug": "^4.3.1" + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-firehose": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-firehose/-/client-firehose-3.621.0.tgz", + "integrity": "sha512-XAjAkXdb35PDvBYph609Fxn4g00HYH/U6N4+KjF9gLQrdTU+wkjf3D9YD02DZNbApJVcu4eIxWh/8M25YkW02A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", - "license": "MIT", + "node_modules/@aws-sdk/client-firehose/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=16.0.0" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", - "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", - "license": "MIT", + "node_modules/@aws-sdk/client-firehose/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/serialize": "^1.3.3", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/cache": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", - "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", - "license": "MIT", + "node_modules/@aws-sdk/client-kinesis": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-kinesis/-/client-kinesis-3.621.0.tgz", + "integrity": "sha512-53Omt/beFmTQPjQNpMuPMk5nMzYVsXCRiO+MeqygZEKYG1fWw/UGluCWVbi7WjClOHacsW8lQcsqIRvkPDFNag==", + "license": "Apache-2.0", "dependencies": { - "@emotion/memoize": "^0.9.0", - "@emotion/sheet": "^1.4.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "stylis": "4.2.0" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/eventstream-serde-browser": "^3.0.5", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.4", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/hash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", - "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", - "license": "MIT" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", - "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", - "license": "MIT", + "node_modules/@aws-sdk/client-kinesis/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", "dependencies": { - "@emotion/memoize": "^0.9.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT" - }, - "node_modules/@emotion/react": { - "version": "11.14.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", - "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", - "license": "MIT", + "node_modules/@aws-sdk/client-kinesis/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2", - "@emotion/weak-memoize": "^0.4.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/serialize": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", - "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", - "license": "MIT", + "node_modules/@aws-sdk/client-personalize-events": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-personalize-events/-/client-personalize-events-3.621.0.tgz", + "integrity": "sha512-qkVkqYvOe3WVuVNL/gRITGYFfHJCx2ijGFK7H3hNUJH3P4AwskmouAd1pWf+3cbGedRnj2is7iw7E602LeJIHA==", + "license": "Apache-2.0", "dependencies": { - "@emotion/hash": "^0.9.2", - "@emotion/memoize": "^0.9.0", - "@emotion/unitless": "^0.10.0", - "@emotion/utils": "^1.4.2", - "csstype": "^3.0.2" + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/sheet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", - "license": "MIT" - }, - "node_modules/@emotion/styled": { - "version": "11.14.1", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", - "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", - "license": "MIT", + "node_modules/@aws-sdk/client-personalize-events/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.13.5", - "@emotion/is-prop-valid": "^1.3.0", - "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", - "@emotion/utils": "^1.4.2" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "peerDependencies": { - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-personalize-events/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@emotion/unitless": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", - "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", - "license": "MIT" + "node_modules/@aws-sdk/client-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.621.0.tgz", + "integrity": "sha512-xpKfikN4u0BaUYZA9FGUMkkDmfoIP0Q03+A86WjqDWhcOoqNA1DkHsE4kZ+r064ifkPUfcNuUvlkVTEoBZoFjA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", - "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", - "license": "MIT", + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.621.0.tgz", + "integrity": "sha512-mMjk3mFUwV2Y68POf1BQMTF+F6qxt5tPu6daEUCNGC9Cenk3h2YXQQoS4/eSyYzuBiYk3vx49VgleRvdvkg8rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, "peerDependencies": { - "react": ">=16.8.0" + "@aws-sdk/client-sts": "^3.621.0" } }, - "node_modules/@emotion/utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", - "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", - "license": "MIT" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", - "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", - "license": "MIT" + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "node_modules/@aws-sdk/client-sso-oidc/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@aws-sdk/client-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@aws-sdk/client-sso/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "node_modules/@aws-sdk/client-sts": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.621.0.tgz", + "integrity": "sha512-707uiuReSt+nAx6d0c21xLjLm2lxeKc7padxjv92CIrIocnQSlJPxSCM7r5zBhwiahJA6MNQwmTl2xznU67KgA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@aws-sdk/client-sts/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@aws-sdk/client-sts/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@aws-sdk/core": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.621.0.tgz", + "integrity": "sha512-CtOwWmDdEiINkGXD93iGfXjN0WmCp9l45cDWHHGa8lRgEDyhuL7bwd/pH5aSzj0j8SiQBG2k0S7DHbd5RaqvbQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.3.1", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" + "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } ], - "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-env/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.621.0.tgz", + "integrity": "sha512-/jc2tEsdkT1QQAI5Dvoci50DbSxtJrevemwFsm0B73pwCcOQZ5ZwwSdVqGsPutzYzUVx3bcXg3LRL7jLACqRIg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.621.0.tgz", + "integrity": "sha512-0EWVnSc+JQn5HLnF5Xv405M8n4zfdx9gyGdpnCmAmFqEDHA8LmBdxJdpUk1Ovp/I5oPANhjojxabIW5f1uU0RA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-ini/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.621.0.tgz", + "integrity": "sha512-4JqpccUgz5Snanpt2+53hbOBbJQrSFq7E1sAAbgY6BKVQUsW5qyXqnjvSF32kDeKa5JpBl3bBWLZl04IadcPHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-node/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@aws-sdk/credential-provider-process/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.621.0.tgz", + "integrity": "sha512-Kza0jcFeA/GEL6xJlzR2KFf1PfZKMFnxfGzJzl5yN7EjoGdMijl34KaRyVnfRjnCWcsUpBWKNIDk9WZVMY9yiw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "node_modules/@aws-sdk/credential-provider-web-identity/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "node_modules/@aws-sdk/middleware-host-header/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@aws-sdk/middleware-logger/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18" + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/middleware-recursion-detection/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, + "node_modules/@aws-sdk/middleware-user-agent/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0" + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, + "node_modules/@aws-sdk/region-config-resolver/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "license": "Apache-2.0", "dependencies": { - "@types/json-schema": "^7.0.15" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "node_modules/@aws-sdk/token-providers/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", + "node_modules/@aws-sdk/types": { + "version": "3.387.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.387.0.tgz", + "integrity": "sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.1.0", + "tslib": "^2.5.0" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, + "node_modules/@aws-sdk/util-endpoints/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=16.0.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.2", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz", + "integrity": "sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==", "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=18.18.0" + "node": ">=20.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=18.18.0" + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12.22" + "node": ">=16.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, + "node_modules/@aws-sdk/util-user-agent-node/node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, "license": "MIT" }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.6.tgz", - "integrity": "sha512-QaYtTHlr8kDFN5mE1wbvVARRKH7Fdw1ZuOjBJcFdVpfNfRYKF3QLT4rt+WaB6CKJvpqxRsmEo0kpYinhH5GeHg==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@mui/icons-material": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.6.tgz", - "integrity": "sha512-0FfkXEj22ysIq5pa41A2NbcAhJSvmcZQ/vcTIbjDsd6hlslG82k5BEBqqS0ZJprxwIL3B45qpJ+bPHwJPlF7uQ==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@mui/material": "^7.3.6", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@mui/material": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.6.tgz", - "integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/core-downloads-tracker": "^7.3.6", - "@mui/system": "^7.3.6", - "@mui/types": "^7.4.9", - "@mui/utils": "^7.3.6", - "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.12", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1", - "react-is": "^19.2.0", - "react-transition-group": "^4.4.5" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "node": ">=6.9.0" }, "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.6", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@mui/material-pigment-css": { - "optional": true - }, - "@types/react": { - "optional": true - } + "@babel/core": "^7.0.0" } }, - "node_modules/@mui/private-theming": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.6.tgz", - "integrity": "sha512-Ws9wZpqM+FlnbZXaY/7yvyvWQo1+02Tbx50mVdNmzWEi51C51y56KAbaDCYyulOOBL6BJxuaqG8rNNuj7ivVyw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/utils": "^7.3.6", - "prop-types": "^15.8.1" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "bin": { + "parser": "bin/babel-parser.js" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@mui/styled-engine": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.6.tgz", - "integrity": "sha512-+wiYbtvj+zyUkmDB+ysH6zRjuQIJ+CM56w0fEXV+VDNdvOuSywG+/8kpjddvvlfMLsaWdQe5oTuYGBcodmqGzQ==", + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@emotion/cache": "^11.14.0", - "@emotion/serialize": "^1.3.3", - "@emotion/sheet": "^1.4.0", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "node": ">=6.9.0" }, "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@mui/system": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.6.tgz", - "integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==", + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/private-theming": "^7.3.6", - "@mui/styled-engine": "^7.3.6", - "@mui/types": "^7.4.9", - "@mui/utils": "^7.3.6", - "clsx": "^2.1.1", - "csstype": "^3.1.3", - "prop-types": "^15.8.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "node": ">=6.9.0" }, "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } + "@babel/core": "^7.0.0-0" } }, - "node_modules/@mui/types": { - "version": "7.4.9", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.9.tgz", - "integrity": "sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==", + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@mui/utils": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.6.tgz", - "integrity": "sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==", + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/types": "^7.4.9", - "@types/prop-types": "^15.7.15", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-is": "^19.2.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@mui/x-date-pickers": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.23.0.tgz", - "integrity": "sha512-uKtam5wqMEuErmRxZLPEX/7CZZFTMfrl05V9cWNjBkpGTcdDBIs1Kba8z2pfQU93e9lSLrRlxbCMJzCu6iF0Rg==", + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/utils": "^7.3.5", - "@mui/x-internals": "8.23.0", - "@types/react-transition-group": "^4.4.12", - "clsx": "^2.1.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", - "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", - "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", - "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", - "dayjs": "^1.10.7", - "luxon": "^3.0.2", - "moment": "^2.29.4", - "moment-hijri": "^2.1.2 || ^3.0.0", - "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "date-fns": { - "optional": true - }, - "date-fns-jalali": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "luxon": { - "optional": true - }, - "moment": { - "optional": true - }, - "moment-hijri": { - "optional": true - }, - "moment-jalaali": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/@mui/x-internals": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.23.0.tgz", - "integrity": "sha512-FN7wdqwTxqq1tJBYVz8TA/HMcViuaHS0Jphr4pEjT/8Iuf94Yt3P82WbsTbXyYrgOQDQl07UqE7qWcJetRcHcg==", + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4", - "@mui/utils": "^7.3.5", - "reselect": "^5.1.1", - "use-sync-external-store": "^1.6.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" } }, - "node_modules/@reduxjs/toolkit": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", - "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", - "@standard-schema/utils": "^0.3.0", - "immer": "^11.0.0", - "redux": "^5.0.1", - "redux-thunk": "^3.1.0", - "reselect": "^5.1.0" + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", - "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" }, "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { + "@types/react": { "optional": true } } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.53", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", - "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", - "dev": true, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ - "arm" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "android" - ] + "aix" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -1567,13 +1866,16 @@ "license": "MIT", "optional": true, "os": [ - "darwin" - ] + "android" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -1581,13 +1883,16 @@ "license": "MIT", "optional": true, "os": [ - "darwin" - ] + "android" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -1595,41 +1900,67 @@ "license": "MIT", "optional": true, "os": [ - "freebsd" - ] + "darwin" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ - "arm" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -1638,12 +1969,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -1652,26 +1986,32 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ - "arm64" + "ia32" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -1680,26 +2020,32 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ - "loong64" + "mips64el" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -1708,175 +2054,1992 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ - "ppc64" + "riscv64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ - "riscv64" + "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ - "riscv64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ - "s390x" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz", + "integrity": "sha512-8jWwS6FweMkpyRkrJooamUGe1CQfO1yJ+lM43IyUJbrhHW/ObES+6ry4vfGi8EKaldHL3t3BG1bcLcERuJPcjg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.7.tgz", + "integrity": "sha512-3Q+ulAqG+A1+R4ebgoIs7AccaJhIGy+Xi/9OnvX376jQ6wcy+rz4geDGrxQxCGzdjOQr4Z3NgyFSZCz4T999lA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.3.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.7.tgz", + "integrity": "sha512-6bdIxqzeOtBAj2wAsfhWCYyMKPLkRO9u/2o5yexcL0C3APqyy91iGSWgT3H7hg+zR2XgE61+WAu12wXPON8b6A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/core-downloads-tracker": "^7.3.7", + "@mui/system": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.3", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.3.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.7.tgz", + "integrity": "sha512-w7r1+CYhG0syCAQUWAuV5zSaU2/67WA9JXUderdb7DzCIJdp/5RmJv6L85wRjgKCMsxFF0Kfn0kPgPbPgw/jdw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.7", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.7.tgz", + "integrity": "sha512-y/QkNXv6cF6dZ5APztd/dFWfQ6LHKPx3skyYO38YhQD4+Cxd6sFAL3Z38WMSSC8LQz145Mpp3CcLrSCLKPwYAg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.7.tgz", + "integrity": "sha512-DovL3k+FBRKnhmatzUMyO5bKkhMLlQ9L7Qw5qHrre3m8zCZmE+31NDVBFfqrbrA7sq681qaEIHdkWD5nmiAjyQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/private-theming": "^7.3.7", + "@mui/styled-engine": "^7.3.7", + "@mui/types": "^7.4.10", + "@mui/utils": "^7.3.7", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.10.tgz", + "integrity": "sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.7.tgz", + "integrity": "sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/types": "^7.4.10", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.23.0.tgz", + "integrity": "sha512-uKtam5wqMEuErmRxZLPEX/7CZZFTMfrl05V9cWNjBkpGTcdDBIs1Kba8z2pfQU93e9lSLrRlxbCMJzCu6iF0Rg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "@mui/x-internals": "8.23.0", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0", + "@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2 || ^3.0.0", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.23.0.tgz", + "integrity": "sha512-FN7wdqwTxqq1tJBYVz8TA/HMcViuaHS0Jphr4pEjT/8Iuf94Yt3P82WbsTbXyYrgOQDQl07UqE7qWcJetRcHcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "@mui/utils": "^7.3.5", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", + "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", + "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.7.tgz", + "integrity": "sha512-8olpW6mKCa0v+ibCjoCzgZHQx1SQmZuW/WkrdZo73wiTprTH6qhmskT60QLFdT9DRa5mXxjz89kQPZ7ZSsoqqg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-stream": "^3.3.4", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", + "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.10.tgz", + "integrity": "sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-codec/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.14.tgz", + "integrity": "sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.11.tgz", + "integrity": "sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.13.tgz", + "integrity": "sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.13", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.13.tgz", + "integrity": "sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^3.1.10", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", + "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/hash-node/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", + "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-2.0.7.tgz", + "integrity": "sha512-2i2BpXF9pI5D1xekqUsgQ/ohv5+H//G9FlawJrkOJskV18PgJ8LiNbLiskMeYt07yAsSTZR7qtlcAaa/GQLWww==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^2.3.1", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@smithy/md5-js/node_modules/@smithy/types": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.12.0.tgz", + "integrity": "sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", + "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.8.tgz", + "integrity": "sha512-OEJZKVUEhMOqMs3ktrTWp7UvvluMJEvD5XgQwRePSbDg1VvBaL8pX8mwPltFn6wk1GySbcVwwyldL8S+iqnrEQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-serde": "^3.0.11", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "@smithy/url-parser": "^3.0.11", + "@smithy/util-middleware": "^3.0.11", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.34.tgz", + "integrity": "sha512-yVRr/AAtPZlUvwEkrq7S3x7Z8/xCd97m2hLDaqdz6ucP2RKHsBjEqaUA2ebNv2SsZoPEi+ZD0dZbOB1u37tGCA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/protocol-http": "^4.1.8", + "@smithy/service-error-classification": "^3.0.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-retry": "^3.0.11", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", + "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", + "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", + "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/shared-ini-file-loader": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", + "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", + "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", + "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", + "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", + "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", + "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", + "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", + "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.11", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.7.0.tgz", + "integrity": "sha512-9wYrjAZFlqWhgVo3C4y/9kpc68jgiSsKUnsFPzr/MSiRL93+QRDafGTfhhKAb2wsr69Ru87WTiqSfQusSmWipA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.5.7", + "@smithy/middleware-endpoint": "^3.2.8", + "@smithy/middleware-stack": "^3.0.11", + "@smithy/protocol-http": "^4.1.8", + "@smithy/types": "^3.7.2", + "@smithy/util-stream": "^3.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", + "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", + "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-base64/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.34.tgz", + "integrity": "sha512-FumjjF631lR521cX+svMLBj3SwSDh9VdtyynTYDAiBDEf8YPP5xORNXKQ9j0105o5+ARAGnOOP/RqSl40uXddA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.34", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.34.tgz", + "integrity": "sha512-vN6aHfzW9dVVzkI0wcZoUXvfjkl4CSbM9nE//08lmUMyf00S75uuCpTrqF9uD4bD9eldIXlt53colrlwKAT8Gw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^3.0.13", + "@smithy/credential-provider-imds": "^3.2.8", + "@smithy/node-config-provider": "^3.1.12", + "@smithy/property-provider": "^3.1.11", + "@smithy/smithy-client": "^3.7.0", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", + "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.12", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", + "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", + "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.11", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "node_modules/@smithy/util-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.4.tgz", + "integrity": "sha512-SGhGBG/KupieJvJSZp/rfHHka8BFgj56eek9px4pp7lZbOF+fRiVr4U7A3y3zJD8uGhxq32C5D96HxsTC9BckQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^4.1.3", + "@smithy/node-http-handler": "^3.3.3", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.3.tgz", + "integrity": "sha512-6SxNltSncI8s689nvnzZQc/dPXcpHQ34KUj6gR/HBroytKOd/isMG3gJF/zBE1TBmTT18TXyzhg3O3SOOqGEhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.8", + "@smithy/querystring-builder": "^3.0.11", + "@smithy/types": "^3.7.2", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "node_modules/@smithy/util-stream/node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] + "node_modules/@smithy/util-stream/node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "node_modules/@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "node_modules/@smithy/util-utf8/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "node_modules/@smithy/util-utf8/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.2.0.tgz", + "integrity": "sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^3.1.9", + "@smithy/types": "^3.7.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, "node_modules/@standard-schema/spec": { "version": "1.1.0", @@ -1890,6 +4053,12 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, + "node_modules/@types/aws-lambda": { + "version": "8.10.159", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.159.tgz", + "integrity": "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1946,7 +4115,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/parse-json": { @@ -1995,6 +4163,12 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", @@ -2085,6 +4259,22 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, + "node_modules/aws-amplify": { + "version": "6.15.9", + "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-6.15.9.tgz", + "integrity": "sha512-KnRxqIwy1Qgwkv6vMWmji/KXhY1T1ii0Cpy7F9bYZ7IkacywcC1LwsXuq5ySz1HtbCcSG1zZ3DJs3wJpSzNcEA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-amplify/analytics": "7.0.90", + "@aws-amplify/api": "6.3.21", + "@aws-amplify/auth": "6.17.1", + "@aws-amplify/core": "6.15.0", + "@aws-amplify/datastore": "5.1.2", + "@aws-amplify/notifications": "2.0.90", + "@aws-amplify/storage": "6.11.0", + "tslib": "^2.5.0" + } + }, "node_modules/axios": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", @@ -2118,6 +4308,26 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.9.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", @@ -2128,6 +4338,12 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2173,6 +4389,17 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2325,6 +4552,18 @@ "node": ">= 6" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2736,6 +4975,24 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2956,6 +5213,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graphql": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz", + "integrity": "sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==", + "license": "MIT", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3020,6 +5286,32 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/idb": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/idb/-/idb-5.0.6.tgz", + "integrity": "sha512-/PFvOWPzRcEPmlDt5jEvzVZVs0wyd/EvGvkDIcbBpGuMMLQKrTPG0TxvE2UJtgZtCQCmOtM2QD7yQJBVEjKGOw==", + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3110,6 +5402,12 @@ "node": ">=0.10.0" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3117,6 +5415,15 @@ "dev": true, "license": "ISC" }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3234,6 +5541,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3621,9 +5934,9 @@ } }, "node_modules/react-router": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.11.0.tgz", - "integrity": "sha512-uI4JkMmjbWCZc01WVP2cH7ZfSzH91JAZUDd7/nIprDgWxBV1TkkmLToFh7EbMTcMak8URFRa2YoBL/W8GWnCTQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", + "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -3643,12 +5956,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.11.0.tgz", - "integrity": "sha512-e49Ir/kMGRzFOOrYQBdoitq3ULigw4lKbAyKusnvtDu2t4dBX4AGYPrzNvorXmVuOyeakai6FUPW5MmibvVG8g==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz", + "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==", "license": "MIT", "dependencies": { - "react-router": "7.11.0" + "react-router": "7.12.0" }, "engines": { "node": ">=20.0.0" @@ -3769,6 +6082,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -3846,6 +6168,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", @@ -3894,6 +6228,12 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3907,6 +6247,15 @@ "node": ">= 0.8.0" } }, + "node_modules/ulid": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.4.0.tgz", + "integrity": "sha512-fIRiVTJNcSRmXKPZtGzFQv9WRrZ3M9eoptl/teFJvjOzmpU+/K/JH6HZ8deBfb5vMEpicJcLn7JmvdknlMq7Zg==", + "license": "MIT", + "bin": { + "ulid": "bin/cli.js" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -3957,6 +6306,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/vite": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", diff --git a/package.json b/package.json index 0305f7b..8f46707 100644 --- a/package.json +++ b/package.json @@ -11,17 +11,18 @@ }, "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", - "@mui/icons-material": "^7.0.0", - "@mui/material": "^7.0.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^7.3.7", + "@mui/material": "^7.3.7", "@mui/x-date-pickers": "^8.0.0", "@reduxjs/toolkit": "^2.0.0", - "axios": "^1.7.0", + "aws-amplify": "^6.15.9", + "axios": "^1.13.2", "date-fns": "^4.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-redux": "^9.0.0", - "react-router-dom": "^7.0.0" + "react-router-dom": "^7.12.0" }, "devDependencies": { "@eslint/js": "^9.0.0", diff --git a/src/App.jsx b/src/App.jsx index e652c82..2109fd4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ -import {useState} from 'react' -import {Route, Routes, useNavigate} from 'react-router-dom' -import {Box, Button, Card, CardContent, Collapse, Container, Grid, Typography,} from '@mui/material' +import { useState } from 'react' +import { Route, Routes, useNavigate, Navigate } from 'react-router-dom' +import { Box, Button, Card, CardContent, Collapse, Container, Grid, Typography, CircularProgress } from '@mui/material' import { ChevronRight as ChevronRightIcon, Create as WritingCategoryIcon, @@ -24,16 +24,67 @@ import DailyLearning from './domains/vocab/pages/DailyLearning' import TestPage from './domains/vocab/pages/TestPage' import WordListPage from './domains/vocab/pages/WordListPage' import StatsPage from './domains/vocab/pages/StatsPage' -import {WritingPage} from './domains/grammar' -import {BadgeSection} from './domains/badge' -import {useChat} from './contexts/ChatContext' -import {useSettings} from './contexts/SettingsContext' +import { WritingPage } from './domains/grammar' +import { BadgeSection } from './domains/badge' +import { useChat } from './contexts/ChatContext' +import { useSettings } from './contexts/SettingsContext' +import { useAuth } from './contexts/AuthContext' +import LoginPage from './pages/Login' +import SignUpPage from './pages/SignUp' + + +function ProtectedRoute({ children }) { + const { isAuthenticated, isLoading } = useAuth() + + if (isLoading) { + return ( + + + + ) + } + + if (!isAuthenticated) { + return + } + + return children +} + +// 이미 로그인된 경우 대시보드로 +function PublicRoute({ children }) { + const { isAuthenticated, isLoading } = useAuth() + + if (isLoading) { + return ( + + + + ) + } + + if (isAuthenticated) { + return + } + + return children +} // Dashboard Page function Dashboard() { const navigate = useNavigate() const [expandedCard, setExpandedCard] = useState(null) - const {t} = useSettings() + const { t } = useSettings() const learningModes = [ { @@ -131,9 +182,9 @@ function Dashboard() { } return ( - + {/* Header */} - + - + - + {t('dashboard.greeting')} @@ -168,7 +219,7 @@ function Dashboard() { const hasChildren = mode.children && mode.children.length > 0 return ( - + handleCardHover(mode.id)} onMouseLeave={handleCardLeave} @@ -189,8 +240,8 @@ function Dashboard() { minHeight: isExpanded ? 'auto' : 140, }} > - - + + {/* Icon */} - + {/* Text */} - + )} - + {mode.description} @@ -286,14 +337,14 @@ function Dashboard() { boxShadow: '0 2px 8px -2px rgba(0,0,0,0.1)', }} > - + + sx={{ mb: 0.5 }}> {child.title} + sx={{ lineHeight: 1.3 }}> {child.description} @@ -310,12 +361,12 @@ function Dashboard() { {/* Recent Activity */} - - + + {t('dashboard.recentActivity')} - + - + {t('dashboard.noHistory')} - + {t('dashboard.startLearning')} + + + 계정이 없으신가요?{' '} + + 회원가입 + + + + ); +} \ No newline at end of file diff --git a/src/domains/auth/components/LoginForm.jsx b/src/domains/auth/components/LoginForm.jsx new file mode 100644 index 0000000..8a6c5c2 --- /dev/null +++ b/src/domains/auth/components/LoginForm.jsx @@ -0,0 +1,119 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Box, + TextField, + Button, + Typography, + Alert, + InputAdornment, + IconButton, + CircularProgress, + Link, +} from '@mui/material'; +import { + Email as EmailIcon, + Lock as LockIcon, + Visibility, + VisibilityOff, +} from '@mui/icons-material'; +import { useAuth } from '../../../contexts/AuthContext'; + +export default function LoginForm({ onSwitchToSignUp }) { + const navigate = useNavigate(); + const { login } = useAuth(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(''); + + if (!email || !password) { + setError('이메일과 비밀번호를 입력해주세요.'); + return; + } + + setIsLoading(true); + const result = await login(email, password); + + if (result.success) { + navigate('/dashboard'); + } else { + setError(result.message); + } + setIsLoading(false); + }; + + return ( + + + 로그인 + + + AI 언어 학습 시스템에 오신 것을 환영합니다 + + + {error && {error}} + + setEmail(e.target.value)} + disabled={isLoading} + sx={{ mb: 2 }} + InputProps={{ + startAdornment: ( + + ), + }} + /> + + setPassword(e.target.value)} + disabled={isLoading} + sx={{ mb: 3 }} + InputProps={{ + startAdornment: ( + + ), + endAdornment: ( + + setShowPassword(!showPassword)}> + {showPassword ? : } + + + ), + }} + /> + + + + + 계정이 없으신가요?{' '} + + 회원가입 + + + + ); +} \ No newline at end of file diff --git a/src/domains/auth/components/SignupForm.jsx b/src/domains/auth/components/SignupForm.jsx new file mode 100644 index 0000000..8a6c5c2 --- /dev/null +++ b/src/domains/auth/components/SignupForm.jsx @@ -0,0 +1,119 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Box, + TextField, + Button, + Typography, + Alert, + InputAdornment, + IconButton, + CircularProgress, + Link, +} from '@mui/material'; +import { + Email as EmailIcon, + Lock as LockIcon, + Visibility, + VisibilityOff, +} from '@mui/icons-material'; +import { useAuth } from '../../../contexts/AuthContext'; + +export default function LoginForm({ onSwitchToSignUp }) { + const navigate = useNavigate(); + const { login } = useAuth(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(''); + + if (!email || !password) { + setError('이메일과 비밀번호를 입력해주세요.'); + return; + } + + setIsLoading(true); + const result = await login(email, password); + + if (result.success) { + navigate('/dashboard'); + } else { + setError(result.message); + } + setIsLoading(false); + }; + + return ( + + + 로그인 + + + AI 언어 학습 시스템에 오신 것을 환영합니다 + + + {error && {error}} + + setEmail(e.target.value)} + disabled={isLoading} + sx={{ mb: 2 }} + InputProps={{ + startAdornment: ( + + ), + }} + /> + + setPassword(e.target.value)} + disabled={isLoading} + sx={{ mb: 3 }} + InputProps={{ + startAdornment: ( + + ), + endAdornment: ( + + setShowPassword(!showPassword)}> + {showPassword ? : } + + + ), + }} + /> + + + + + 계정이 없으신가요?{' '} + + 회원가입 + + + + ); +} \ No newline at end of file diff --git a/src/domains/badge/components/BadgeGrid.jsx b/src/domains/badge/components/BadgeGrid.jsx index 757b49f..9a06e1d 100644 --- a/src/domains/badge/components/BadgeGrid.jsx +++ b/src/domains/badge/components/BadgeGrid.jsx @@ -1,29 +1,72 @@ +import {useRef, useState} from 'react' import {Box, Skeleton, Typography} from '@mui/material' import BadgeCard from './BadgeCard' import {useSettings} from '../../../contexts/SettingsContext' export default function BadgeGrid({badges = [], loading = false, size = 'medium'}) { const {isKorean} = useSettings() + const scrollRef = useRef(null) + const [isDragging, setIsDragging] = useState(false) + const [startX, setStartX] = useState(0) + const [scrollLeft, setScrollLeft] = useState(0) + + // 드래그 스크롤 핸들러 + const handleMouseDown = (e) => { + setIsDragging(true) + setStartX(e.pageX - scrollRef.current.offsetLeft) + setScrollLeft(scrollRef.current.scrollLeft) + } + + const handleMouseLeave = () => { + setIsDragging(false) + } + + const handleMouseUp = () => { + setIsDragging(false) + } + + const handleMouseMove = (e) => { + if (!isDragging) return + e.preventDefault() + const x = e.pageX - scrollRef.current.offsetLeft + const walk = (x - startX) * 1.5 + scrollRef.current.scrollLeft = scrollLeft - walk + } + + // 터치 이벤트 핸들러 + const handleTouchStart = (e) => { + setIsDragging(true) + setStartX(e.touches[0].pageX - scrollRef.current.offsetLeft) + setScrollLeft(scrollRef.current.scrollLeft) + } + + const handleTouchMove = (e) => { + if (!isDragging) return + const x = e.touches[0].pageX - scrollRef.current.offsetLeft + const walk = (x - startX) * 1.5 + scrollRef.current.scrollLeft = scrollLeft - walk + } + + const handleTouchEnd = () => { + setIsDragging(false) + } if (loading) { return ( - {Array.from({length: 12}).map((_, index) => ( - - - + {Array.from({length: 6}).map((_, index) => ( + + + ))} @@ -32,12 +75,7 @@ export default function BadgeGrid({badges = [], loading = false, size = 'medium' if (badges.length === 0) { return ( - + {isKorean ? '배지가 없습니다' : 'No badges available'} @@ -54,20 +92,45 @@ export default function BadgeGrid({badges = [], loading = false, size = 'medium' return ( {sortedBadges.map((badge) => ( - + + + ))} ) diff --git a/src/domains/badge/services/badgeService.js b/src/domains/badge/services/badgeService.js index e449d89..dc3c77d 100644 --- a/src/domains/badge/services/badgeService.js +++ b/src/domains/badge/services/badgeService.js @@ -1,7 +1,7 @@ import badgeApi from '../../../api/badgeApi' -// Mock 데이터 사용 여부 -const USE_MOCK = true +// Mock 데이터 사용 여부 (환경변수로 제어: VITE_USE_MOCK=true) +const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' // Placeholder 이미지 (실제 S3 이미지가 없을 경우 대비) const PLACEHOLDER_BADGE = 'https://via.placeholder.com/100x100/FFD700/000000?text=Badge' @@ -188,7 +188,10 @@ const withMock = (apiCall, mockData) => { setTimeout(() => resolve(mockData), 500) }) } - return apiCall().catch(() => mockData) + // 실제 API 호출 시 응답의 data 필드 추출 (백엔드 응답: { isSuccess, message, data }) + return apiCall() + .then(response => response.data || response) + .catch(() => mockData) } /** diff --git a/src/domains/chat/services/chatService.js b/src/domains/chat/services/chatService.js index f81918d..21bcab3 100644 --- a/src/domains/chat/services/chatService.js +++ b/src/domains/chat/services/chatService.js @@ -2,8 +2,8 @@ import chatApi from '../../../api/chatApi' const TEMP_USER_ID = import.meta.env.VITE_TEMP_USER_ID || 'user1' -// Mock 데이터 사용 여부 (true: 목 데이터 사용, false: 실제 API 호출) -const USE_MOCK = true +// Mock 데이터 사용 여부 (환경변수로 제어: VITE_USE_MOCK=true) +const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' // ============================================ // Mock 데이터 (백엔드 API 응답 형식과 동일) @@ -200,19 +200,13 @@ const mockMessages = { const withMock = (apiCall, mockData) => { if (USE_MOCK) { - // interceptor가 response.data를 반환하므로 동일한 형식으로 반환 - // 실제 API: { success: true, message: ..., data: {...} } - return Promise.resolve({ - success: true, - message: 'Mock data', - data: mockData - }) + // Mock 모드에서는 직접 데이터 반환 + return Promise.resolve(mockData) } - return apiCall().catch(() => ({ - success: true, - message: 'Fallback mock data', - data: mockData - })) + // 실제 API 호출 시 응답의 data 필드 추출 (백엔드 응답: { isSuccess, message, data }) + return apiCall() + .then(response => response.data || response) + .catch(() => mockData) } /** @@ -235,7 +229,7 @@ export const chatRoomService = { lastMessageAt: new Date().toISOString(), } return withMock( - () => chatApi.post('/rooms', {...data, createdBy: TEMP_USER_ID}), + () => chatApi.post('/chat/rooms', {...data, createdBy: TEMP_USER_ID}), newRoom ) }, @@ -254,7 +248,7 @@ export const chatRoomService = { queryParams.append('userId', TEMP_USER_ID) } if (cursor) queryParams.append('cursor', cursor) - return chatApi.get(`/rooms?${queryParams.toString()}`) + return chatApi.get(`/chat/rooms?${queryParams.toString()}`) }, { rooms: mockChatRooms @@ -273,7 +267,7 @@ export const chatRoomService = { // GET /rooms/{roomId} - 채팅방 상세 조회 getDetail: async (roomId) => { return withMock( - () => chatApi.get(`/rooms/${roomId}`), + () => chatApi.get(`/chat/rooms/${roomId}`), mockChatRooms.find(r => r.roomId === roomId) || mockChatRooms[0] ) }, @@ -282,7 +276,7 @@ export const chatRoomService = { join: async (roomId, password) => { const room = mockChatRooms.find(r => r.roomId === roomId) return withMock( - () => chatApi.post(`/rooms/${roomId}/join`, { + () => chatApi.post(`/chat/rooms/${roomId}/join`, { userId: TEMP_USER_ID, ...(password && {password}), }), @@ -301,7 +295,7 @@ export const chatRoomService = { // POST /rooms/{roomId}/leave - 채팅방 퇴장 leave: async (roomId) => { return withMock( - () => chatApi.post(`/rooms/${roomId}/leave`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/leave`, {userId: TEMP_USER_ID}), {roomId, currentMembers: 2} ) }, @@ -322,7 +316,7 @@ export const messageService = { createdAt: new Date().toISOString(), } return withMock( - () => chatApi.post(`/rooms/${roomId}/messages`, { + () => chatApi.post(`/chat/rooms/${roomId}/messages`, { userId: TEMP_USER_ID, content, messageType, @@ -340,7 +334,7 @@ export const messageService = { const queryParams = new URLSearchParams() queryParams.append('limit', limit) if (cursor) queryParams.append('cursor', cursor) - return chatApi.get(`/rooms/${roomId}/messages?${queryParams.toString()}`) + return chatApi.get(`/chat/rooms/${roomId}/messages?${queryParams.toString()}`) }, { messages: (mockMessages[roomId] || []).slice(0, limit), @@ -358,7 +352,7 @@ export const voiceService = { // POST /voice/synthesize - TTS 변환 synthesize: async (messageId, roomId, voice = 'FEMALE') => { return withMock( - () => chatApi.post('/voice/synthesize', {messageId, roomId, voice}), + () => chatApi.post('/chat/voice/synthesize', {messageId, roomId, voice}), {audioUrl: null, cached: false} ) }, @@ -412,7 +406,7 @@ export const gameService = { } return withMock( - () => chatApi.post(`/rooms/${roomId}/game/start`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/game/start`, {userId: TEMP_USER_ID}), { gameStatus: mockGameState.gameStatus, currentRound: mockGameState.currentRound, @@ -447,7 +441,7 @@ export const gameService = { } return withMock( - () => chatApi.post(`/rooms/${roomId}/game/stop`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/game/stop`, {userId: TEMP_USER_ID}), {message: '게임이 종료되었습니다.', scores: finalScores} ) }, @@ -455,7 +449,7 @@ export const gameService = { // GET /rooms/{roomId}/game/status - 게임 상태 조회 getStatus: async (roomId) => { return withMock( - () => chatApi.get(`/rooms/${roomId}/game/status`), + () => chatApi.get(`/chat/rooms/${roomId}/game/status`), { gameStatus: mockGameState.gameStatus, currentRound: mockGameState.currentRound, @@ -480,7 +474,7 @@ export const gameService = { .map(([userId, score], index) => ({rank: index + 1, userId, score})) return withMock( - () => chatApi.get(`/rooms/${roomId}/game/scores`), + () => chatApi.get(`/chat/rooms/${roomId}/game/scores`), {scores: sortedScores, currentRound: mockGameState.currentRound, totalRounds: mockGameState.totalRounds} ) }, @@ -522,7 +516,7 @@ export const gameService = { mockGameState.hintUsed = true return withMock( - () => chatApi.post(`/rooms/${roomId}/game/hint`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/game/hint`, {userId: TEMP_USER_ID}), {hint, hintUsed: true} ) }, @@ -532,7 +526,7 @@ export const gameService = { if (mockGameState.currentRound >= mockGameState.totalRounds) { mockGameState.gameStatus = 'FINISHED' return withMock( - () => chatApi.post(`/rooms/${roomId}/game/skip`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/game/skip`, {userId: TEMP_USER_ID}), {gameStatus: 'FINISHED', message: '게임이 종료되었습니다.'} ) } @@ -553,7 +547,7 @@ export const gameService = { } return withMock( - () => chatApi.post(`/rooms/${roomId}/game/skip`, {userId: TEMP_USER_ID}), + () => chatApi.post(`/chat/rooms/${roomId}/game/skip`, {userId: TEMP_USER_ID}), { currentRound: mockGameState.currentRound, currentDrawerId: mockGameState.currentDrawerId, diff --git a/src/domains/freetalk/components/ChatRoomModal.jsx b/src/domains/freetalk/components/ChatRoomModal.jsx index bedd277..a6b9e7f 100644 --- a/src/domains/freetalk/components/ChatRoomModal.jsx +++ b/src/domains/freetalk/components/ChatRoomModal.jsx @@ -8,6 +8,8 @@ import { Fade, IconButton, Paper, + Popover, + Slider, Tab, Tabs, TextField, @@ -16,9 +18,11 @@ import { } from '@mui/material' import { Chat as ChatIcon, + Circle as CircleIcon, Close as CloseIcon, ExitToApp as ExitToAppIcon, Minimize as MinimizeIcon, + Opacity as OpacityIcon, OpenInFull as MaximizeIcon, Refresh as RefreshIcon, Send as SendIcon, @@ -31,21 +35,36 @@ import { gameService, MESSAGE_TYPES, messageService, - TEMP_USER_ID, voiceService } from '../../chat/services/chatService' import {useSettings} from '../../../contexts/SettingsContext' +import {useAuth} from '../../../contexts/AuthContext' import {DESIGN_TOKENS, getChatStyles} from '../../../theme/theme' import GameModePanel from './GameModePanel' +import {useChatWebSocket} from '../hooks/useChatWebSocket' const ChatRoomModal = ({open, onClose, room, onLeave}) => { const theme = useTheme() const isDark = theme.palette.mode === 'dark' const {settings} = useSettings() + const {user} = useAuth() + const currentUserId = user?.userId || user?.username || user?.sub const messagesEndRef = useRef(null) const dragRef = useRef(null) - const [messages, setMessages] = useState([]) + // WebSocket 훅 사용 + const { + isConnected, + messages, + gameState: wsGameState, + error: wsError, + connect: wsConnect, + disconnect: wsDisconnect, + sendMessage: wsSendMessage, + clearError: wsClearError, + setMessages, + } = useChatWebSocket(room?.id, currentUserId) + const [newMessage, setNewMessage] = useState('') const [loading, setLoading] = useState(true) const [sendingMessage, setSendingMessage] = useState(false) @@ -58,6 +77,8 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { const [dragOffset, setDragOffset] = useState({x: 0, y: 0}) const [activeTab, setActiveTab] = useState(0) // 0: 채팅, 1: 게임 const [gameStatus, setGameStatus] = useState(GAME_STATUS.NONE) + const [opacity, setOpacity] = useState(100) + const [opacityAnchorEl, setOpacityAnchorEl] = useState(null) // 메시지 목록 조회 const fetchMessages = useCallback(async () => { if (!room?.id) return @@ -71,7 +92,7 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { userId: msg.userId, messageType: msg.messageType, createdAt: new Date(msg.createdAt), - isOwn: msg.userId === TEMP_USER_ID, + isOwn: msg.userId === currentUserId, })) setMessages(transformedMessages.reverse()) } catch (err) { @@ -96,19 +117,49 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { } }, [room?.id]) + // WebSocket 에러 통합 + useEffect(() => { + if (wsError) { + setError(wsError) + } + }, [wsError]) + + // WebSocket 게임 상태 변경 감지 - 탭 자동 전환 + useEffect(() => { + if (wsGameState?.status === 'PLAYING') { + setGameStatus(GAME_STATUS.PLAYING) + setActiveTab(1) + } else if (wsGameState?.status === 'FINISHED') { + setGameStatus(GAME_STATUS.NONE) + setActiveTab(0) + } + }, [wsGameState?.status]) + // 초기 로드 useEffect(() => { - if (open && room?.id) { + if (open && room?.id && currentUserId) { + console.log('[ChatRoomModal] Initializing...', {roomId: room.id, userId: currentUserId}) setLoading(true) - setMessages([]) setMinimized(false) setActiveTab(0) + + // WebSocket 연결 + wsConnect() + + // 기존 메시지 및 게임 상태 로드 Promise.all([ fetchMessages(), fetchGameStatus(), ]).finally(() => setLoading(false)) } - }, [open, room?.id, fetchMessages, fetchGameStatus]) + + return () => { + if (open && room?.id) { + console.log('[ChatRoomModal] Disconnecting WebSocket...') + wsDisconnect() + } + } + }, [open, room?.id, currentUserId]) // eslint-disable-line react-hooks/exhaustive-deps // 게임 메시지 처리 const handleGameMessage = (gameMessage) => { @@ -153,6 +204,13 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { } }, [messages.length]) + // 탭 전환 시 채팅 탭으로 돌아오면 스크롤 맨 아래로 + useEffect(() => { + if (activeTab === 0 && messages.length > 0 && !loading) { + setTimeout(() => scrollToBottom(true), 100) + } + }, [activeTab]) + // 드래그 핸들러 const handleMouseDown = (e) => { // 버튼, 입력창, 슬라이더, 팝오버 클릭 시 드래그 방지 @@ -194,7 +252,7 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { } }, [isDragging, dragOffset]) - // 메시지 전송 + // 메시지 전송 (WebSocket 사용) const handleSendMessage = async () => { if (!newMessage.trim() || sendingMessage) return @@ -202,25 +260,38 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { const messageContent = newMessage.trim() setNewMessage('') - const tempMessage = { - id: `temp-${Date.now()}`, - content: messageContent, - userId: TEMP_USER_ID, - messageType: 'TEXT', - createdAt: new Date(), - isOwn: true, - } - setMessages((prev) => [...prev, tempMessage]) + // /start, /stop 명령어는 서버 응답(WebSocket game_start/game_end)을 기다림 + // 탭 전환은 useEffect의 wsGameState 감지에서 처리 - try { - await messageService.send(room.id, messageContent) - await fetchMessages() - } catch (err) { - console.error('Failed to send message:', err) - setMessages((prev) => prev.filter((m) => m.id !== tempMessage.id)) - setError('메시지 전송에 실패했습니다') - } finally { + // WebSocket으로 전송 + if (isConnected) { + const success = wsSendMessage(messageContent, 'TEXT') + if (!success) { + setError('메시지 전송에 실패했습니다') + } setSendingMessage(false) + } else { + // WebSocket 연결이 안 된 경우 REST API fallback + const tempMessage = { + id: `temp-${Date.now()}`, + content: messageContent, + userId: currentUserId, + messageType: 'TEXT', + createdAt: new Date(), + isOwn: true, + } + setMessages((prev) => [...prev, tempMessage]) + + try { + await messageService.send(room.id, messageContent) + await fetchMessages() + } catch (err) { + console.error('Failed to send message:', err) + setMessages((prev) => prev.filter((m) => m.id !== tempMessage.id)) + setError('메시지 전송에 실패했습니다') + } finally { + setSendingMessage(false) + } } } @@ -278,10 +349,15 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { } const formatTime = (date) => { + // date가 문자열이거나 Date 객체일 수 있음 + const dateObj = date instanceof Date ? date : new Date(date) + if (isNaN(dateObj.getTime())) { + return '' // 유효하지 않은 날짜는 빈 문자열 반환 + } return new Intl.DateTimeFormat('ko-KR', { hour: '2-digit', minute: '2-digit', - }).format(date) + }).format(dateObj) } if (!open) return null @@ -291,6 +367,7 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { { zIndex: 1300, cursor: isDragging ? 'grabbing' : 'default', border: isDark ? '1px solid rgba(255,255,255,0.1)' : 'none', + pointerEvents: opacity < 50 ? 'none' : 'auto', }} > - {/* 헤더 - 드래그 가능 */} + {/* 헤더 - 드래그 가능, 항상 조작 가능 */} { justifyContent: 'space-between', cursor: 'grab', userSelect: 'none', + pointerEvents: 'auto', }} > {room?.name || '채팅방'} + {/* 연결 상태 표시 */} + {room?.level && ( { )} + setOpacityAnchorEl(e.currentTarget)} + sx={{color: 'white'}} + title="투명도" + > + + + setOpacityAnchorEl(null)} + anchorOrigin={{vertical: 'top', horizontal: 'center'}} + transformOrigin={{vertical: 'bottom', horizontal: 'center'}} + slotProps={{ + paper: { + sx: {pointerEvents: 'auto'} + } + }} + > + + + 투명도: {opacity}% + + setOpacity(v)} + min={10} + max={100} + size="small" + /> + + @@ -392,7 +512,11 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { {/* 게임 모드 */} {activeTab === 1 && ( - + )} @@ -484,15 +608,14 @@ const ChatRoomModal = ({open, onClose, room, onLeave}) => { maxWidth: '70%', }} > - {!message.isOwn && ( - - {message.userId} - - )} + + {message.userId} + { ) )} - {/* 입력 영역 */} + {/* 입력 영역 - 항상 조작 가능 */} { borderTop: 1, borderColor: 'divider', bgcolor: 'background.paper', + pointerEvents: 'auto', }} > { +const GameModePanel = ({roomId, onGameMessage, initialGameStatus}) => { const theme = useTheme() const isDark = theme.palette.mode === 'dark' + const {user} = useAuth() + const currentUserId = user?.userId || user?.username const canvasRef = useRef(null) const [gameState, setGameState] = useState({ - gameStatus: GAME_STATUS.NONE, + gameStatus: initialGameStatus || GAME_STATUS.NONE, currentRound: 0, totalRounds: 5, currentDrawerId: null, @@ -35,7 +38,7 @@ const GameModePanel = ({roomId, onGameMessage}) => { const [brushSize, setBrushSize] = useState(3) const [loading, setLoading] = useState(false) - const isDrawer = gameState.currentDrawerId === TEMP_USER_ID + const isDrawer = gameState.currentDrawerId === currentUserId const isGameActive = gameState.gameStatus === GAME_STATUS.PLAYING // 게임 상태 조회 @@ -43,12 +46,33 @@ const GameModePanel = ({roomId, onGameMessage}) => { try { const response = await gameService.getStatus(roomId) const data = response.data || response - setGameState(data) + setGameState(prev => ({ + ...prev, + ...data, + gameStatus: data.gameStatus || GAME_STATUS.NONE, + })) } catch (err) { console.error('Failed to fetch game status:', err) } }, [roomId]) + // 마운트 시 게임 상태 조회 + useEffect(() => { + if (roomId) { + fetchGameStatus() + } + }, [roomId, fetchGameStatus]) + + // 부모 컴포넌트의 게임 상태 변경 반영 + useEffect(() => { + if (initialGameStatus) { + setGameState(prev => ({ + ...prev, + gameStatus: initialGameStatus, + })) + } + }, [initialGameStatus]) + // 타이머 useEffect(() => { if (!isGameActive || !gameState.roundStartTime) return diff --git a/src/domains/freetalk/hooks/useChatWebSocket.js b/src/domains/freetalk/hooks/useChatWebSocket.js new file mode 100644 index 0000000..51b4ae5 --- /dev/null +++ b/src/domains/freetalk/hooks/useChatWebSocket.js @@ -0,0 +1,277 @@ +import {useCallback, useEffect, useRef, useState} from 'react' +import {chatWebSocketService} from '../services/chatWebSocketService' +import {chatRoomService} from '../../chat/services/chatService' + +/** + * Chat WebSocket 훅 + * 실시간 채팅 및 게임을 위한 상태 관리 + * + * 연결 흐름: + * 1. POST /chat/rooms/{roomId}/join 호출 → roomToken 발급 + * 2. roomToken으로 WebSocket 연결 + */ +export function useChatWebSocket(roomId, userId) { + const [isConnected, setIsConnected] = useState(false) + const [messages, setMessages] = useState([]) + const [gameState, setGameState] = useState(null) + const [error, setError] = useState(null) + + const isConnectedRef = useRef(false) + const roomTokenRef = useRef(null) + + /** + * WebSocket 연결 + */ + const connect = useCallback(async () => { + console.log('[useChatWebSocket] Attempting to connect...', {roomId, userId}) + + if (!roomId || !userId) { + console.error('[useChatWebSocket] roomId and userId are required', {roomId, userId}) + return + } + + try { + setError(null) + + // 1. 먼저 REST API로 roomToken 발급받기 + console.log('[useChatWebSocket] Getting roomToken via join API...') + const joinResponse = await chatRoomService.join(roomId) + const roomToken = joinResponse.roomToken || joinResponse.data?.roomToken + console.log('[useChatWebSocket] Got roomToken:', roomToken ? 'exists' : 'missing') + + if (!roomToken) { + throw new Error('roomToken not received from join API') + } + + roomTokenRef.current = roomToken + + // 콜백 설정 + chatWebSocketService.setCallbacks({ + onMessage: (data) => { + const newMessage = { + id: data.messageId || `msg-${Date.now()}`, + content: data.content, + userId: data.userId, + messageType: data.messageType || 'TEXT', + createdAt: data.createdAt || new Date().toISOString(), + isOwn: data.userId === userId, + } + setMessages((prev) => [...prev, newMessage]) + }, + + onGameStart: (data) => { + setGameState({ + status: 'PLAYING', + currentRound: data.currentRound || 1, + totalRounds: data.totalRounds || 5, + currentDrawerId: data.currentDrawerId, + currentWord: data.currentWord, + roundStartTime: Date.now(), + scores: data.scores || {}, + }) + }, + + onGameEnd: (data) => { + setGameState((prev) => ({ + ...prev, + status: 'FINISHED', + finalScores: data.scores, + })) + }, + + onRoundStart: (data) => { + setGameState((prev) => ({ + ...prev, + currentRound: data.currentRound, + currentDrawerId: data.currentDrawerId, + currentWord: data.currentWord, + roundStartTime: Date.now(), + hintUsed: false, + correctGuessers: [], + })) + }, + + onRoundEnd: (data) => { + setGameState((prev) => ({ + ...prev, + scores: data.scores || prev?.scores, + })) + }, + + onCorrectAnswer: (data) => { + setGameState((prev) => ({ + ...prev, + correctGuessers: [...(prev?.correctGuessers || []), data.userId], + scores: data.scores || prev?.scores, + })) + }, + + onScoreUpdate: (data) => { + setGameState((prev) => ({ + ...prev, + scores: data.scores, + })) + }, + + onHint: (data) => { + setGameState((prev) => ({ + ...prev, + hint: data.hint, + hintUsed: true, + })) + }, + + onDrawing: (data) => { + // 캔버스에 그리기 데이터 적용 (별도 핸들러로 처리) + }, + + onUserJoin: (data) => { + const systemMessage = { + id: `system-${Date.now()}`, + content: `${data.userId}님이 입장했습니다.`, + messageType: 'SYSTEM', + createdAt: new Date().toISOString(), + isSystem: true, + } + setMessages((prev) => [...prev, systemMessage]) + }, + + onUserLeave: (data) => { + const systemMessage = { + id: `system-${Date.now()}`, + content: `${data.userId}님이 퇴장했습니다.`, + messageType: 'SYSTEM', + createdAt: new Date().toISOString(), + isSystem: true, + } + setMessages((prev) => [...prev, systemMessage]) + }, + + onError: (data) => { + setError(data.message || '연결 오류가 발생했습니다') + }, + + onClose: () => { + setIsConnected(false) + isConnectedRef.current = false + }, + }) + + // 2. roomToken으로 WebSocket 연결 + await chatWebSocketService.connect(roomToken, roomId, userId) + setIsConnected(true) + isConnectedRef.current = true + } catch (err) { + console.error('[useChatWebSocket] Connection error:', err) + setError('연결에 실패했습니다') + setIsConnected(false) + isConnectedRef.current = false + } + }, [roomId, userId]) + + /** + * 연결 종료 + */ + const disconnect = useCallback(() => { + chatWebSocketService.disconnect() + setIsConnected(false) + isConnectedRef.current = false + }, []) + + /** + * 메시지 전송 + */ + const sendMessage = useCallback((content, messageType = 'TEXT') => { + if (!isConnectedRef.current) { + setError('연결되지 않았습니다') + return false + } + + const success = chatWebSocketService.sendMessage(content, messageType) + + if (success) { + // Optimistic update - 자신의 메시지 즉시 추가 + const optimisticMessage = { + id: `temp-${Date.now()}`, + content, + userId, + messageType, + createdAt: new Date().toISOString(), + isOwn: true, + isPending: true, + } + setMessages((prev) => [...prev, optimisticMessage]) + } + + return success + }, [userId]) + + /** + * 게임 시작 + */ + const startGame = useCallback(() => { + return chatWebSocketService.startGame() + }, []) + + /** + * 게임 종료 + */ + const stopGame = useCallback(() => { + return chatWebSocketService.stopGame() + }, []) + + /** + * 그리기 데이터 전송 + */ + const sendDrawing = useCallback((drawingData) => { + chatWebSocketService.sendDrawing(drawingData) + }, []) + + /** + * 에러 초기화 + */ + const clearError = useCallback(() => { + setError(null) + }, []) + + /** + * 메시지 초기화 + */ + const clearMessages = useCallback(() => { + setMessages([]) + }, []) + + /** + * 컴포넌트 언마운트 시 정리 + */ + useEffect(() => { + return () => { + if (isConnectedRef.current) { + chatWebSocketService.disconnect() + } + } + }, []) + + return { + // 상태 + isConnected, + messages, + gameState, + error, + + // 액션 + connect, + disconnect, + sendMessage, + startGame, + stopGame, + sendDrawing, + clearError, + clearMessages, + + // 유틸 + setMessages, + } +} + +export default useChatWebSocket diff --git a/src/domains/freetalk/pages/ChatRoomPage.jsx b/src/domains/freetalk/pages/ChatRoomPage.jsx index 77f9d75..0e21178 100644 --- a/src/domains/freetalk/pages/ChatRoomPage.jsx +++ b/src/domains/freetalk/pages/ChatRoomPage.jsx @@ -15,12 +15,15 @@ import { } from '@mui/material' import { ArrowBack as ArrowBackIcon, + Circle as CircleIcon, ExitToApp as ExitToAppIcon, Refresh as RefreshIcon, Send as SendIcon, VolumeUp as VolumeUpIcon, } from '@mui/icons-material' -import {chatRoomService, messageService, TEMP_USER_ID, voiceService} from '../../chat/services/chatService' +import {chatRoomService, messageService, voiceService} from '../../chat/services/chatService' +import {useAuth} from '../../../contexts/AuthContext' +import {useChatWebSocket} from '../hooks/useChatWebSocket' const levelColors = { beginner: {bg: '#e8f5e9', color: '#2e7d32', label: '초급'}, @@ -31,17 +34,34 @@ const levelColors = { const ChatRoomPage = () => { const {roomId} = useParams() const navigate = useNavigate() + const {user} = useAuth() + const currentUserId = user?.userId || user?.username || user?.sub + + // 디버깅: 사용자 정보 확인 + console.log('[ChatRoomPage] User info:', {user, currentUserId, roomId}) + const messagesEndRef = useRef(null) const messagesContainerRef = useRef(null) const [room, setRoom] = useState(null) - const [messages, setMessages] = useState([]) const [newMessage, setNewMessage] = useState('') const [loading, setLoading] = useState(true) - const [sendingMessage, setSendingMessage] = useState(false) const [error, setError] = useState(null) const [playingTTS, setPlayingTTS] = useState(null) + // WebSocket 훅 사용 + const { + isConnected, + messages, + gameState, + error: wsError, + connect: wsConnect, + disconnect: wsDisconnect, + sendMessage: wsSendMessage, + clearError: wsClearError, + setMessages, + } = useChatWebSocket(roomId, currentUserId) + // 채팅방 정보 조회 const fetchRoomDetail = useCallback(async () => { try { @@ -60,25 +80,28 @@ const ChatRoomPage = () => { } }, [roomId]) - // 메시지 목록 조회 + // 기존 메시지 목록 조회 (초기 로드용) const fetchMessages = useCallback(async () => { try { const response = await messageService.getList(roomId, {limit: 50}) - const transformedMessages = (response.messages || []).map((msg) => ({ - id: msg.messageId, + const transformedMessages = (response.messages || []).map((msg, index) => ({ + id: msg.messageId || `msg-${index}-${Date.now()}`, content: msg.content, userId: msg.userId, messageType: msg.messageType, createdAt: new Date(msg.createdAt), - isOwn: msg.userId === TEMP_USER_ID, + isOwn: msg.userId === currentUserId, })) - // 오래된 메시지가 위에 오도록 정렬 - setMessages(transformedMessages.reverse()) + // 중복 제거 후 정렬 + const uniqueMessages = transformedMessages.filter( + (msg, index, self) => index === self.findIndex(m => m.id === msg.id) + ) + setMessages(uniqueMessages.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))) } catch (err) { console.error('Failed to fetch messages:', err) setError('메시지를 불러오는데 실패했습니다') } - }, [roomId]) + }, [roomId, currentUserId, setMessages]) // 초기 로드 useEffect(() => { @@ -88,7 +111,20 @@ const ChatRoomPage = () => { setLoading(false) } loadData() - }, [fetchRoomDetail, fetchMessages]) + }, []) // eslint-disable-line react-hooks/exhaustive-deps + + // WebSocket 연결 (별도 effect) + useEffect(() => { + if (currentUserId && roomId) { + console.log('[ChatRoomPage] Connecting WebSocket...', {roomId, currentUserId}) + wsConnect() + } + + return () => { + console.log('[ChatRoomPage] Disconnecting WebSocket...') + wsDisconnect() + } + }, [roomId, currentUserId]) // eslint-disable-line react-hooks/exhaustive-deps // 스크롤 맨 아래로 const scrollToBottom = () => { @@ -99,36 +135,42 @@ const ChatRoomPage = () => { scrollToBottom() }, [messages]) - // 메시지 전송 + // 에러 통합 + useEffect(() => { + if (wsError) { + setError(wsError) + } + }, [wsError]) + + // 메시지 전송 (WebSocket 사용) const handleSendMessage = async () => { - if (!newMessage.trim() || sendingMessage) return + if (!newMessage.trim()) return - setSendingMessage(true) const messageContent = newMessage.trim() setNewMessage('') - // Optimistic update - const tempMessage = { - id: `temp-${Date.now()}`, - content: messageContent, - userId: TEMP_USER_ID, - messageType: 'TEXT', - createdAt: new Date(), - isOwn: true, - } - setMessages((prev) => [...prev, tempMessage]) + // WebSocket으로 전송 + if (isConnected) { + wsSendMessage(messageContent, 'TEXT') + } else { + // WebSocket 연결이 안 된 경우 REST API fallback + const tempMessage = { + id: `temp-${Date.now()}`, + content: messageContent, + userId: currentUserId, + messageType: 'TEXT', + createdAt: new Date(), + isOwn: true, + } + setMessages((prev) => [...prev, tempMessage]) - try { - await messageService.send(roomId, messageContent) - // 전송 성공 시 메시지 새로고침 - await fetchMessages() - } catch (err) { - console.error('Failed to send message:', err) - // 실패 시 임시 메시지 제거 - setMessages((prev) => prev.filter((m) => m.id !== tempMessage.id)) - setError('메시지 전송에 실패했습니다') - } finally { - setSendingMessage(false) + try { + await messageService.send(roomId, messageContent) + } catch (err) { + console.error('Failed to send message:', err) + setMessages((prev) => prev.filter((m) => m.id !== tempMessage.id)) + setError('메시지 전송에 실패했습니다') + } } } @@ -163,6 +205,7 @@ const ChatRoomPage = () => { // 채팅방 퇴장 const handleLeaveRoom = async () => { try { + wsDisconnect() await chatRoomService.leave(roomId) navigate('/freetalk/people') } catch (err) { @@ -176,12 +219,18 @@ const ChatRoomPage = () => { fetchMessages() } + // 에러 닫기 + const handleCloseError = () => { + setError(null) + wsClearError() + } + // 시간 포맷 const formatTime = (date) => { return new Intl.DateTimeFormat('ko-KR', { hour: '2-digit', minute: '2-digit', - }).format(date) + }).format(new Date(date)) } if (loading) { @@ -201,9 +250,18 @@ const ChatRoomPage = () => { - - {room?.name || '채팅방'} - + + + {room?.name || '채팅방'} + + {/* 연결 상태 표시 */} + + {room?.level && ( { {room?.currentMembers}/{room?.maxMembers}명 + + {isConnected ? '실시간' : '오프라인'} + @@ -233,11 +294,19 @@ const ChatRoomPage = () => { {/* 에러 메시지 */} {error && ( - setError(null)} sx={{m: 1}}> + {error} )} + {/* 게임 상태 표시 */} + {gameState?.status === 'PLAYING' && ( + + 🎮 게임 진행 중! 라운드 {gameState.currentRound}/{gameState.totalRounds} + {gameState.currentDrawerId === currentUserId && ` - 제시어: ${gameState.currentWord}`} + + )} + {/* 메시지 영역 */} { 아직 메시지가 없습니다. 첫 메시지를 보내보세요! + + /start - 게임 시작 | /stop - 게임 종료 + ) : ( messages.map((message) => ( @@ -267,71 +339,83 @@ const ChatRoomPage = () => { gap: 1, }} > - {/* 아바타 (상대방만) */} - {!message.isOwn && ( - - {message.userId?.charAt(0)?.toUpperCase() || 'U'} - - )} - - - {/* 사용자 이름 (상대방만) */} - {!message.isOwn && ( - - {message.userId} + {/* 시스템 메시지 */} + {message.isSystem ? ( + + + {message.content} - )} - - {/* 메시지 버블 */} - - {message.isOwn && ( - - {formatTime(message.createdAt)} - + + ) : ( + <> + {/* 아바타 (상대방만) */} + {!message.isOwn && ( + + {message.userId?.charAt(0)?.toUpperCase() || 'U'} + )} - - - {message.content} - - + {/* 사용자 이름 (상대방만) */} + {!message.isOwn && ( + + {message.userId} + + )} - {!message.isOwn && ( - - handlePlayTTS(message.id)} - disabled={playingTTS === message.id} - sx={{p: 0.5}} + {/* 메시지 버블 */} + + {message.isOwn && ( + + {formatTime(message.createdAt)} + + )} + + - {playingTTS === message.id ? ( - - ) : ( - - )} - - - {formatTime(message.createdAt)} - + + {message.content} + + + + {!message.isOwn && ( + + handlePlayTTS(message.id)} + disabled={playingTTS === message.id} + sx={{p: 0.5}} + > + {playingTTS === message.id ? ( + + ) : ( + + )} + + + {formatTime(message.createdAt)} + + + )} - )} - - + + + )} )) )} @@ -351,7 +435,7 @@ const ChatRoomPage = () => { > setNewMessage(e.target.value)} onKeyPress={handleKeyPress} @@ -367,7 +451,7 @@ const ChatRoomPage = () => { { '&:disabled': {bgcolor: 'grey.300'}, }} > - {sendingMessage ? : } + diff --git a/src/domains/freetalk/pages/FreetalkPeoplePage.jsx b/src/domains/freetalk/pages/FreetalkPeoplePage.jsx index 3d8ee47..66ca4f7 100644 --- a/src/domains/freetalk/pages/FreetalkPeoplePage.jsx +++ b/src/domains/freetalk/pages/FreetalkPeoplePage.jsx @@ -97,9 +97,18 @@ const FreetalkPeoplePage = () => { const transformedRooms = (responseData.rooms || []).map(transformRoomData) if (isLoadMore) { - setRooms((prev) => [...prev, ...transformedRooms]) + // 중복 제거 + setRooms((prev) => { + const existingIds = new Set(prev.map(r => r.id)) + const newRooms = transformedRooms.filter(r => !existingIds.has(r.id)) + return [...prev, ...newRooms] + }) } else { - setRooms(transformedRooms) + // 중복 제거 + const uniqueRooms = transformedRooms.filter( + (room, index, self) => index === self.findIndex(r => r.id === room.id) + ) + setRooms(uniqueRooms) } setCursor(responseData.nextCursor || null) @@ -131,7 +140,11 @@ const FreetalkPeoplePage = () => { const response = await chatRoomService.getList(params) const responseData = response.data || response const transformedRooms = (responseData.rooms || []).map(transformRoomData) - setRooms(transformedRooms) + // 중복 제거 + const uniqueRooms = transformedRooms.filter( + (room, index, self) => index === self.findIndex(r => r.id === room.id) + ) + setRooms(uniqueRooms) setCursor(responseData.nextCursor || null) setHasMore(!!responseData.nextCursor) } catch (err) { @@ -177,7 +190,11 @@ const FreetalkPeoplePage = () => { const response = await chatRoomService.getList(params) const responseData = response.data || response const transformedRooms = (responseData.rooms || []).map(transformRoomData) - setRooms(transformedRooms) + // 중복 제거 + const uniqueRooms = transformedRooms.filter( + (room, index, self) => index === self.findIndex(r => r.id === room.id) + ) + setRooms(uniqueRooms) setCursor(responseData.nextCursor || null) setHasMore(!!responseData.nextCursor) } catch (err) { @@ -308,11 +325,8 @@ const FreetalkPeoplePage = () => { {filteredRooms.map((room, index) => ( diff --git a/src/domains/freetalk/services/chatWebSocketService.js b/src/domains/freetalk/services/chatWebSocketService.js new file mode 100644 index 0000000..6624cee --- /dev/null +++ b/src/domains/freetalk/services/chatWebSocketService.js @@ -0,0 +1,366 @@ +/** + * Chat WebSocket Service + * 실시간 채팅 및 게임 명령을 위한 WebSocket 서비스 + * + * 연결 흐름: + * 1. REST API로 채팅방 입장: POST /chat/rooms/{roomId}/join → roomToken 발급 + * 2. roomToken으로 WebSocket 연결: wss://...?roomToken={roomToken} + */ + +const WS_URL = import.meta.env.VITE_WS_URL || + 'wss://t378dif43l.execute-api.ap-northeast-2.amazonaws.com/dev' + +/** + * Chat WebSocket 연결 클래스 + */ +class ChatWebSocketConnection { + constructor() { + this.ws = null + this.callbacks = {} + this.isConnected = false + this.reconnectAttempts = 0 + this.maxReconnectAttempts = 5 + this.reconnectDelay = 1000 + this.roomId = null + this.userId = null + this.roomToken = null + } + + /** + * WebSocket 연결 + * @param {string} roomToken - POST /chat/rooms/{roomId}/join에서 발급받은 roomToken + * @param {string} roomId - 채팅방 ID + * @param {string} userId - 사용자 ID + */ + connect(roomToken, roomId, userId) { + return new Promise((resolve, reject) => { + try { + this.roomId = roomId + this.userId = userId + this.roomToken = roomToken + + // roomToken 파라미터로 연결 (token이 아님!) + const url = roomToken ? `${WS_URL}?roomToken=${roomToken}` : WS_URL + console.log('[ChatWebSocket] Connecting to:', url) + this.ws = new WebSocket(url) + + this.ws.onopen = () => { + this.isConnected = true + this.reconnectAttempts = 0 + console.log('[ChatWebSocket] Connected') + + // 연결 후 방 입장 메시지 전송 + if (roomId) { + this.joinRoom(roomId) + } + + resolve() + } + + this.ws.onclose = (event) => { + this.isConnected = false + console.log('[ChatWebSocket] Disconnected:', event.code) + this.callbacks.onClose?.(event) + + // 비정상 종료 시 재연결 시도 (roomToken 만료 시 재발급 필요할 수 있음) + if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) { + this.attemptReconnect(roomToken, roomId, userId) + } + } + + this.ws.onerror = (error) => { + console.error('[ChatWebSocket] Error:', error) + this.callbacks.onError?.({type: 'error', message: 'WebSocket connection error'}) + reject(error) + } + + this.ws.onmessage = (event) => { + try { + const data = JSON.parse(event.data) + this.handleMessage(data) + } catch (e) { + console.error('[ChatWebSocket] Parse error:', e) + } + } + } catch (error) { + reject(error) + } + }) + } + + /** + * 재연결 시도 + */ + attemptReconnect(roomToken, roomId, userId) { + this.reconnectAttempts++ + console.log(`[ChatWebSocket] Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`) + + setTimeout(() => { + this.connect(roomToken, roomId, userId).catch(() => { + console.error('[ChatWebSocket] Reconnect failed') + }) + }, this.reconnectDelay * this.reconnectAttempts) + } + + /** + * 메시지 핸들링 + */ + handleMessage(data) { + const {type, messageType} = data + + // 메시지 타입에 따라 콜백 호출 + switch (type || messageType) { + case 'message': + case 'TEXT': + this.callbacks.onMessage?.(data) + break + case 'game_start': + this.callbacks.onGameStart?.(data) + break + case 'game_end': + this.callbacks.onGameEnd?.(data) + break + case 'round_start': + this.callbacks.onRoundStart?.(data) + break + case 'round_end': + this.callbacks.onRoundEnd?.(data) + break + case 'drawing': + this.callbacks.onDrawing?.(data) + break + case 'correct_answer': + this.callbacks.onCorrectAnswer?.(data) + break + case 'score_update': + this.callbacks.onScoreUpdate?.(data) + break + case 'hint': + this.callbacks.onHint?.(data) + break + case 'user_join': + this.callbacks.onUserJoin?.(data) + break + case 'user_leave': + this.callbacks.onUserLeave?.(data) + break + case 'system_command': + // 시스템 명령어 응답 (예: /member, /help 등) + this.callbacks.onMessage?.(data) + break + case 'error': + this.callbacks.onError?.(data) + break + default: + console.log('[ChatWebSocket] Unknown message type:', type || messageType, data) + this.callbacks.onMessage?.(data) + } + } + + /** + * 방 입장 + */ + joinRoom(roomId) { + this.send({ + action: 'joinRoom', + roomId, + userId: this.userId, + }) + } + + /** + * 방 퇴장 + */ + leaveRoom(roomId) { + this.send({ + action: 'leaveRoom', + roomId, + userId: this.userId, + }) + } + + /** + * 메시지 전송 + */ + sendMessage(content, messageType = 'TEXT') { + if (!this.isConnected || !this.ws) { + console.error('[ChatWebSocket] Not connected') + return false + } + + this.send({ + action: 'sendMessage', + roomId: this.roomId, + userId: this.userId, + content, + messageType, + }) + return true + } + + /** + * 게임 시작 명령 + */ + startGame() { + return this.sendMessage('/start', 'TEXT') + } + + /** + * 게임 종료 명령 + */ + stopGame() { + return this.sendMessage('/stop', 'TEXT') + } + + /** + * 그리기 데이터 전송 + */ + sendDrawing(drawingData) { + this.send({ + action: 'sendDrawing', + roomId: this.roomId, + userId: this.userId, + drawingData, + }) + } + + /** + * WebSocket 메시지 전송 + */ + send(data) { + if (!this.isConnected || !this.ws) { + console.error('[ChatWebSocket] Not connected') + return + } + + this.ws.send(JSON.stringify(data)) + } + + /** + * 콜백 설정 + */ + setCallbacks(callbacks) { + this.callbacks = callbacks + } + + /** + * 연결 종료 + */ + disconnect() { + if (this.roomId) { + this.leaveRoom(this.roomId) + } + + if (this.ws) { + this.ws.close(1000, 'Client disconnect') + this.ws = null + } + this.isConnected = false + this.roomId = null + this.userId = null + } + + /** + * 연결 상태 확인 + */ + getConnectionState() { + if (!this.ws) return 'disconnected' + + switch (this.ws.readyState) { + case WebSocket.CONNECTING: + return 'connecting' + case WebSocket.OPEN: + return 'connected' + case WebSocket.CLOSING: + return 'closing' + case WebSocket.CLOSED: + return 'disconnected' + default: + return 'unknown' + } + } +} + +// 싱글톤 인스턴스 +let wsInstance = null + +/** + * Chat WebSocket 서비스 + */ +export const chatWebSocketService = { + /** + * 인스턴스 가져오기 (싱글톤) + */ + getInstance() { + if (!wsInstance) { + wsInstance = new ChatWebSocketConnection() + } + return wsInstance + }, + + /** + * 연결 + */ + async connect(token, roomId, userId) { + const instance = this.getInstance() + return instance.connect(token, roomId, userId) + }, + + /** + * 메시지 전송 + */ + sendMessage(content, messageType = 'TEXT') { + const instance = this.getInstance() + return instance.sendMessage(content, messageType) + }, + + /** + * 게임 시작 + */ + startGame() { + const instance = this.getInstance() + return instance.startGame() + }, + + /** + * 게임 종료 + */ + stopGame() { + const instance = this.getInstance() + return instance.stopGame() + }, + + /** + * 그리기 데이터 전송 + */ + sendDrawing(drawingData) { + const instance = this.getInstance() + return instance.sendDrawing(drawingData) + }, + + /** + * 콜백 설정 + */ + setCallbacks(callbacks) { + const instance = this.getInstance() + instance.setCallbacks(callbacks) + }, + + /** + * 연결 종료 + */ + disconnect() { + const instance = this.getInstance() + instance.disconnect() + }, + + /** + * 연결 상태 + */ + getConnectionState() { + const instance = this.getInstance() + return instance.getConnectionState() + }, +} + +export default chatWebSocketService diff --git a/src/domains/grammar/components/ChatMessage.jsx b/src/domains/grammar/components/ChatMessage.jsx index 5703418..aa2524e 100644 --- a/src/domains/grammar/components/ChatMessage.jsx +++ b/src/domains/grammar/components/ChatMessage.jsx @@ -30,6 +30,8 @@ export default function ChatMessage({ correctedContent, grammarScore, errors = [], + feedback, + isCorrect, aiResponse, conversationTip, } = message @@ -176,6 +178,23 @@ export default function ChatMessage({ ))} + + {/* Feedback */} + {feedback && ( + + + 💡 {feedback} + + + )} diff --git a/src/domains/grammar/pages/WritingPage.jsx b/src/domains/grammar/pages/WritingPage.jsx index 1f479ef..a484cea 100644 --- a/src/domains/grammar/pages/WritingPage.jsx +++ b/src/domains/grammar/pages/WritingPage.jsx @@ -124,17 +124,22 @@ export default function WritingPage() { if (response.session) { setLevel(response.session.level || GRAMMAR_LEVELS.BEGINNER) } - // Convert messages to our format - const formattedMessages = (response.messages || []).map((msg) => ({ - id: msg.messageId, - content: msg.content, - correctedContent: msg.correctedContent, - grammarScore: msg.grammarScore, - errors: msg.errorsJson ? JSON.parse(msg.errorsJson) : [], - aiResponse: msg.role === 'ASSISTANT' ? msg.content : null, - isUser: msg.role === 'USER', - createdAt: msg.createdAt, - })) + // Convert messages to our format and sort by createdAt (oldest first) + const formattedMessages = (response.messages || []) + .map((msg) => ({ + id: msg.messageId, + content: msg.content, + correctedContent: msg.correctedContent, + grammarScore: msg.grammarScore, + errors: msg.errors || (msg.errorsJson ? JSON.parse(msg.errorsJson) : []), + feedback: msg.feedback || null, + isCorrect: msg.isCorrect, + aiResponse: msg.role === 'ASSISTANT' ? msg.content : null, + conversationTip: msg.conversationTip || null, + isUser: msg.role === 'USER', + createdAt: msg.createdAt, + })) + .sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)) setMessages(formattedMessages) } catch (err) { console.error('Failed to load session messages:', err) diff --git a/src/domains/grammar/services/grammarService.js b/src/domains/grammar/services/grammarService.js index 30de7fa..cfc1f00 100644 --- a/src/domains/grammar/services/grammarService.js +++ b/src/domains/grammar/services/grammarService.js @@ -1,7 +1,7 @@ import grammarApi from '../../../api/grammarApi' -// Mock 데이터 사용 여부 (true: 목 데이터 사용, false: 실제 API 호출) -const USE_MOCK = true +// Mock 데이터 사용 여부 (환경변수로 제어: VITE_USE_MOCK=true) +const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' // ============================================ // Mock 데이터 @@ -116,7 +116,10 @@ const withMock = (apiCall, mockData) => { setTimeout(() => resolve(mockData), 800) }) } - return apiCall().catch(() => mockData) + // 실제 API 호출 시 응답의 data 필드 추출 (백엔드 응답: { isSuccess, message, data }) + return apiCall() + .then(response => response.data || response) + .catch(() => mockData) } /** diff --git a/src/domains/grammar/services/grammarStreamService.js b/src/domains/grammar/services/grammarStreamService.js index 0d311da..f9f56c8 100644 --- a/src/domains/grammar/services/grammarStreamService.js +++ b/src/domains/grammar/services/grammarStreamService.js @@ -7,8 +7,8 @@ const WS_URL = import.meta.env.VITE_GRAMMAR_WS_URL || 'wss://placeholder.execute-api.ap-northeast-2.amazonaws.com/dev' -// Mock 모드 (WebSocket 서버가 없을 때 테스트용) -const USE_MOCK = true +// Mock 모드 (환경변수로 제어: VITE_USE_MOCK=true) +const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' const MOCK_DELAY = 50 // 토큰 간 딜레이 (ms) /** diff --git a/src/domains/vocab/pages/DailyLearning.jsx b/src/domains/vocab/pages/DailyLearning.jsx index dd2b4d9..311a29a 100644 --- a/src/domains/vocab/pages/DailyLearning.jsx +++ b/src/domains/vocab/pages/DailyLearning.jsx @@ -33,8 +33,115 @@ import FlashCard from '../components/FlashCard' import {dailyService, userWordService, voiceService} from '../services/vocabService' import {LEVEL_LABELS, LEVELS} from '../constants/vocabConstants' import {useTranslation} from '../../../contexts/SettingsContext' +import {useAuth} from '../../../contexts/AuthContext' -const TEMP_USER_ID = import.meta.env.VITE_TEMP_USER_ID || 'user1' +// 카드 셔플 애니메이션 컴포넌트 +function ShuffleAnimation({count, isKorean}) { + return ( + + {/* 카드 셔플 애니메이션 */} + + {[...Array(Math.min(count, 5))].map((_, i) => ( + + + 📚 + + + ))} + + + {/* 텍스트 */} + + {isKorean ? '카드를 섞는 중...' : 'Shuffling cards...'} + + + {isKorean + ? `${count}개의 단어를 다시 학습합니다` + : `Reviewing ${count} words again` + } + + + ) +} // Level Selection Screen function LevelSelect({onSelect, loading, t, isKorean}) { @@ -142,6 +249,8 @@ function LevelSelect({onSelect, loading, t, isKorean}) { export default function DailyLearning() { const navigate = useNavigate() const {t, isKorean} = useTranslation() + const {user} = useAuth() + const userId = user?.userId || user?.username const [phase, setPhase] = useState('loading') const [loading, setLoading] = useState(false) const [error, setError] = useState(null) @@ -154,7 +263,13 @@ export default function DailyLearning() { const [results, setResults] = useState({correct: 0, incorrect: 0}) const [swipeDirection, setSwipeDirection] = useState(null) const [isEntering, setIsEntering] = useState(false) + const [unknownWords, setUnknownWords] = useState([]) // "몰라요" 선택한 단어들 + const [totalWordCount, setTotalWordCount] = useState(0) // 전체 단어 수 (진행률 계산용) + const [isShuffling, setIsShuffling] = useState(false) // 셔플 애니메이션 상태 + const [shuffleCount, setShuffleCount] = useState(0) // 셔플할 단어 수 + // 마운트 시 먼저 level 없이 시도 (기존 학습이 있으면 성공) + // 실패하면 level 선택 화면으로 이동 useEffect(() => { fetchDailyWords() }, []) @@ -164,7 +279,7 @@ export default function DailyLearning() { setLoading(true) setError(null) - const response = await dailyService.getWords(TEMP_USER_ID, level) + const response = await dailyService.getWords(level) const dailyData = response?.data || response const allWords = [ @@ -179,6 +294,7 @@ export default function DailyLearning() { } setWords(allWords) + setTotalWordCount(allWords.length) // 전체 단어 수 저장 const learnedCount = dailyData?.learnedCount || 0 if (learnedCount > 0 && learnedCount < allWords.length) { @@ -212,7 +328,10 @@ export default function DailyLearning() { } const currentWord = words[currentIndex] - const progress = words.length > 0 ? (learnedIds.size / words.length) * 100 : 0 + // 진행률: 학습 완료된 단어 / 전체 단어 수 + const progress = totalWordCount > 0 ? (learnedIds.size / totalWordCount) * 100 : 0 + // 현재 라운드에서 남은 단어 (몰라요 단어 재학습 시 표시용) + const remainingInRound = words.length - currentIndex const playTTS = useCallback(async (word) => { if (!word || isPlayingTTS) return @@ -248,17 +367,19 @@ export default function DailyLearning() { setSwipeDirection(isCorrect ? 'right' : 'left') - try { - await userWordService.update(TEMP_USER_ID, currentWord.wordId, isCorrect) - - setResults(prev => ({ - ...prev, - [isCorrect ? 'correct' : 'incorrect']: prev[isCorrect ? 'correct' : 'incorrect'] + 1 - })) - - setLearnedIds(prev => new Set([...prev, currentWord.wordId])) - } catch (err) { - console.error('Answer update error:', err) + if (isCorrect) { + // "알아요" 선택 - API 호출하고 학습 완료 처리 + try { + await dailyService.markLearned(currentWord.wordId) + setLearnedIds(prev => new Set([...prev, currentWord.wordId])) + setResults(prev => ({...prev, correct: prev.correct + 1})) + } catch (err) { + console.error('Answer update error:', err) + } + } else { + // "몰라요" 선택 - API 호출 X, 나중에 다시 학습하도록 저장 + setUnknownWords(prev => [...prev, currentWord]) + setResults(prev => ({...prev, incorrect: prev.incorrect + 1})) } setTimeout(() => { @@ -269,20 +390,68 @@ export default function DailyLearning() { }, 250) } + // 배열 섞기 함수 + const shuffleArray = (array) => { + const shuffled = [...array] + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]] + } + return shuffled + } + const moveToNext = () => { setIsFlipped(false) if (currentIndex < words.length - 1) { + // 다음 단어로 이동 setCurrentIndex(prev => prev + 1) + } else if (unknownWords.length > 0) { + // 현재 리스트 끝 + "몰라요" 단어 있음 → 셔플 애니메이션 후 다시 학습 + setShuffleCount(unknownWords.length) + setIsShuffling(true) + setTimeout(() => { + const shuffled = shuffleArray(unknownWords) + setWords(shuffled) + setUnknownWords([]) + setCurrentIndex(0) + setIsShuffling(false) + }, 1500) // 1.5초 애니메이션 } else { + // 모든 단어 "알아요" 완료 → 학습 완료 setPhase('complete') } } + // 건너뛰기 - "몰라요"와 동일하게 처리 + const handleSkip = () => { + if (!currentWord || swipeDirection || isShuffling) return + + setIsFlipped(false) + + if (currentIndex < words.length - 1) { + // 다음 단어가 있으면 현재 단어를 unknownWords에 추가하고 다음으로 + setUnknownWords(prev => [...prev, currentWord]) + setCurrentIndex(prev => prev + 1) + } else { + // 마지막 단어 건너뛰기 → 모든 unknown 단어 + 현재 단어로 셔플 + const allUnknown = [...unknownWords, currentWord] + setShuffleCount(allUnknown.length) + setIsShuffling(true) + setTimeout(() => { + const shuffled = shuffleArray(allUnknown) + setWords(shuffled) + setUnknownWords([]) + setCurrentIndex(0) + setIsShuffling(false) + }, 1500) + } + } + const handleToggleBookmark = async () => { if (!currentWord) return try { const newBookmarked = !currentWord.bookmarked - await userWordService.updateTag(TEMP_USER_ID, currentWord.wordId, { + await userWordService.updateTag(userId, currentWord.wordId, { bookmarked: newBookmarked, }) setWords(prev => @@ -296,11 +465,14 @@ export default function DailyLearning() { } const handleRestart = () => { + // 처음부터 다시 시작 - 단어 목록 다시 가져오기 setCurrentIndex(0) setLearnedIds(new Set()) + setUnknownWords([]) setIsFlipped(false) - setPhase('learning') setResults({correct: 0, incorrect: 0}) + setPhase('loading') + fetchDailyWords() // 단어 다시 로드 } // Loading Screen @@ -442,7 +614,16 @@ export default function DailyLearning() { // Learning Screen return ( - + <> + {/* 셔플 애니메이션 오버레이 */} + {isShuffling && ( + + )} + + {/* Header */} { - if (currentIndex < words.length - 1) { - setCurrentIndex(prev => prev + 1) - setIsFlipped(false) - } else { - setPhase('complete') - } - }} + onClick={handleSkip} + disabled={isShuffling} endIcon={} sx={{color: 'text.secondary'}} > - {currentIndex === words.length - 1 ? t('dailyLearning.finish') : t('dailyLearning.skip')} + {t('dailyLearning.skip')} + ) } diff --git a/src/domains/vocab/pages/StatsPage.jsx b/src/domains/vocab/pages/StatsPage.jsx index 0c6ed67..813862f 100644 --- a/src/domains/vocab/pages/StatsPage.jsx +++ b/src/domains/vocab/pages/StatsPage.jsx @@ -13,75 +13,181 @@ import { ListItem, ListItemText, Paper, - Tab, - Tabs, - Tooltip, Typography, } from '@mui/material' import { ArrowBack as BackIcon, - CalendarMonth as CalendarIcon, + CheckCircle as CheckIcon, + EmojiEvents as TrophyIcon, + LocalFireDepartment as FireIcon, + MenuBook as BookIcon, + Quiz as QuizIcon, + School as SchoolIcon, + Timeline as TimelineIcon, TrendingUp as TrendingUpIcon, + VolumeUp as VolumeIcon, Warning as WarningIcon, } from '@mui/icons-material' import {statsService, voiceService} from '../services/vocabService' -import {DIFFICULTY_LABELS, LEVEL_COLORS, LEVEL_LABELS, VOICE_TYPES,} from '../constants/vocabConstants' +import {DIFFICULTY_LABELS, LEVEL_COLORS, LEVEL_LABELS, VOICE_TYPES} from '../constants/vocabConstants' import {useTranslation} from '../../../contexts/SettingsContext' +import {useAuth} from '../../../contexts/AuthContext' import {BadgeSection} from '../../badge' -const TEMP_USER_ID = import.meta.env.VITE_TEMP_USER_ID || 'user1' - -// 학습 캘린더 히트맵 컴포넌트 +// 학습 캘린더 히트맵 컴포넌트 (GitHub 스타일) function LearningCalendar({data}) { + const [hoveredDay, setHoveredDay] = useState(null) + const [tooltipPos, setTooltipPos] = useState({x: 0, y: 0}) + const today = new Date() + const todayStr = today.toISOString().split('T')[0] + + // 12주(84일) 전부터 오늘까지 const startDate = new Date(today) - startDate.setDate(startDate.getDate() - 83) // 12주 전 + startDate.setDate(startDate.getDate() - 83) + + // 시작일을 해당 주의 일요일로 조정 + const startDayOfWeek = startDate.getDay() + startDate.setDate(startDate.getDate() - startDayOfWeek) const weeks = [] + const monthLabels = [] let currentDate = new Date(startDate) + let lastMonth = -1 - // 12주 데이터 생성 - for (let w = 0; w < 12; w++) { + // 주별로 데이터 생성 + while (currentDate <= today || weeks.length < 13) { const week = [] for (let d = 0; d < 7; d++) { const dateStr = currentDate.toISOString().split('T')[0] - const dayData = data?.find(d => d.date === dateStr) + // 백엔드는 "period" 필드를 사용, 폴백으로 "date"도 지원 + const dayData = data?.find(item => (item.period || item.date) === dateStr) + const isFuture = currentDate > today + + // 월 라벨 추가 (각 주의 첫 날이 새 달이면) + if (d === 0 && currentDate.getMonth() !== lastMonth && !isFuture) { + const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'] + monthLabels.push({ + month: monthNames[currentDate.getMonth()], + weekIndex: weeks.length, + }) + lastMonth = currentDate.getMonth() + } + + // 백엔드는 newWordsLearned 사용, 폴백으로 learnedCount도 지원 + const count = dayData?.newWordsLearned || dayData?.learnedCount || 0 + week.push({ date: dateStr, - count: dayData?.learnedCount || 0, - isToday: dateStr === today.toISOString().split('T')[0], + count, + isToday: dateStr === todayStr, + isFuture, + dayOfWeek: d, }) currentDate.setDate(currentDate.getDate() + 1) } weeks.push(week) + if (weeks.length >= 14) break } - const getColor = (count) => { + // GitHub 스타일 색상 (초록 계열) + const getColor = (count, isFuture) => { + if (isFuture) return 'transparent' if (count === 0) return '#ebedf0' - if (count < 20) return '#9be9a8' - if (count < 40) return '#40c463' - if (count < 55) return '#30a14e' + if (count < 5) return '#9be9a8' + if (count < 15) return '#40c463' + if (count < 30) return '#30a14e' return '#216e39' } + const getLevel = (count) => { + if (count === 0) return 0 + if (count < 5) return 1 + if (count < 15) return 2 + if (count < 30) return 3 + return 4 + } + const dayLabels = ['일', '월', '화', '수', '목', '금', '토'] + const formatDate = (dateStr) => { + const date = new Date(dateStr) + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const weekday = dayLabels[date.getDay()] + return `${year}년 ${month}월 ${day}일 (${weekday})` + } + + const handleMouseEnter = (e, day) => { + if (day.isFuture) return + const rect = e.target.getBoundingClientRect() + setTooltipPos({ + x: rect.left + rect.width / 2, + y: rect.top - 8, + }) + setHoveredDay(day) + } + + // 총 학습량 계산 (백엔드는 newWordsLearned 사용) + const totalLearned = data?.reduce((sum, item) => sum + (item.newWordsLearned || item.learnedCount || 0), 0) || 0 + const activeDays = data?.filter(item => (item.newWordsLearned || item.learnedCount || 0) > 0).length || 0 + return ( - - + + {/* 요약 정보 */} + + + 최근 12주간 {totalLearned}개 단어 학습 + + + + + {/* 월 라벨 */} + + {monthLabels.map((label, idx) => ( + + {label.month} + + ))} + + + {/* 캘린더 그리드 */} + {/* 요일 라벨 */} - + {dayLabels.map((label, idx) => ( {idx % 2 === 1 ? label : ''} @@ -89,85 +195,180 @@ function LearningCalendar({data}) { ))} - {/* 히트맵 그리드 */} + {/* 주별 셀 */} {weeks.map((week, wIdx) => ( - + {week.map((day, dIdx) => ( - - - + onMouseEnter={(e) => handleMouseEnter(e, day)} + onMouseLeave={() => setHoveredDay(null)} + sx={{ + width: 12, + height: 12, + backgroundColor: getColor(day.count, day.isFuture), + borderRadius: '2px', + border: day.isToday + ? '2px solid #10b981' + : day.isFuture + ? 'none' + : '1px solid rgba(27, 31, 35, 0.06)', + cursor: day.isFuture ? 'default' : 'pointer', + transition: 'all 0.15s ease', + boxSizing: 'border-box', + '&:hover': day.isFuture ? {} : { + transform: 'scale(1.2)', + borderColor: 'rgba(27, 31, 35, 0.15)', + boxShadow: '0 1px 3px rgba(0,0,0,0.12)', + }, + }} + /> ))} ))} {/* 범례 */} - - 적음 - {[0, 10, 30, 45, 55].map((count, idx) => ( - - ))} - 많음 + + + 오늘 학습하셨나요? + + + Less + {[0, 1, 2, 3, 4].map((level) => ( + + ))} + More + + + {/* 호버 툴팁 */} + {hoveredDay && ( + + + {hoveredDay.count > 0 + ? `${hoveredDay.count}개 단어 학습` + : '학습 기록 없음'} + + + {formatDate(hoveredDay.date)} + + + )} ) } -// 취약 단어 목록 컴포넌트 +// 복습 필요 단어 목록 컴포넌트 function WeakWordsList({words, onPlayTTS, playingWordId}) { if (!words || words.length === 0) { return ( - - 취약 단어가 없습니다 - + + + + 모든 단어를 잘 학습했어요! + + + 복습이 필요한 단어가 없습니다 + + ) } return ( - {words.map((item, index) => ( + {words.slice(0, 5).map((item, index) => ( + + + {index + 1} + + {item.english} - + {item.incorrectCount > 0 && ( + + {item.incorrectCount}회 오답 + + )} } secondary={item.korean} @@ -176,11 +377,12 @@ function WeakWordsList({words, onPlayTTS, playingWordId}) { size="small" onClick={() => onPlayTTS?.(item)} disabled={playingWordId === item.wordId} + sx={{ + backgroundColor: '#f3f4f6', + '&:hover': {backgroundColor: '#e5e7eb'}, + }} > - + ))} @@ -192,29 +394,57 @@ function WeakWordsList({words, onPlayTTS, playingWordId}) { function LevelProgressChart({data}) { if (!data) return null + const levelConfig = { + BEGINNER: {icon: '🌱', color: '#10b981', bgColor: '#ecfdf5'}, + INTERMEDIATE: {icon: '🌿', color: '#f97316', bgColor: '#fff7ed'}, + ADVANCED: {icon: '🌳', color: '#ef4444', bgColor: '#fef2f2'}, + } + return ( - + {Object.entries(LEVEL_LABELS).map(([level, label]) => { const levelData = data[level] || {total: 0, learned: 0} - const progress = levelData.total > 0 - ? (levelData.learned / levelData.total) * 100 - : 0 + const progress = levelData.total > 0 ? (levelData.learned / levelData.total) * 100 : 0 + const config = levelConfig[level] return ( - - - - {label} - - - {levelData.learned}/{levelData.total} + + + + + {config.icon} + + + {label} + + + + {levelData.learned} / {levelData.total} ) @@ -229,83 +459,129 @@ function DifficultyChart({data}) { const total = Object.values(data).reduce((sum, val) => sum + val, 0) - const colors = { - EASY: '#4caf50', - NORMAL: '#2196f3', - HARD: '#ff9800', + const config = { + EASY: {label: '쉬움', color: '#10b981', bgColor: '#ecfdf5', icon: '😊'}, + NORMAL: {label: '보통', color: '#3b82f6', bgColor: '#eff6ff', icon: '🤔'}, + HARD: {label: '어려움', color: '#ef4444', bgColor: '#fef2f2', icon: '😰'}, } return ( - - {/* 막대 그래프 */} - - {Object.entries(DIFFICULTY_LABELS).map(([key, label]) => { - const count = data[key] || 0 - const height = total > 0 ? (count / total) * 100 : 0 - - return ( - - - {count} - - - - {label} - - - ) - })} - + + {Object.entries(DIFFICULTY_LABELS).map(([key]) => { + const count = data[key] || 0 + const percentage = total > 0 ? ((count / total) * 100).toFixed(0) : 0 + const cfg = config[key] + + return ( + + {cfg.icon} + + {count} + + + {cfg.label} ({percentage}%) + + + ) + })} ) } -// 통계 요약 카드 -function StatCard({title, value, subtitle, icon: Icon, color}) { +// 히어로 통계 카드 +function HeroStatCard({icon: Icon, label, value, subValue, color, bgGradient}) { return ( - + - - {title} + + {label} - + {value} - {subtitle && ( - - {subtitle} + {subValue && ( + + {subValue} )} - {Icon && ( - - - - )} + + + ) } +// 미니 통계 카드 +function MiniStatCard({icon: Icon, label, value, color, bgColor}) { + return ( + + + + + + + {value} + + + {label} + + + + ) +} + export default function StatsPage() { const navigate = useNavigate() - const {t} = useTranslation() - const [tab, setTab] = useState(0) // 0: 일간, 1: 주간, 2: 월간 + const {t, isKorean} = useTranslation() + const {user} = useAuth() const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -323,26 +599,29 @@ export default function StatsPage() { fetchAllStats() }, []) - useEffect(() => { - fetchPeriodStats() - }, [tab]) - const fetchAllStats = async () => { try { setLoading(true) setError(null) const [overviewRes, dailyRes, weakRes] = await Promise.all([ - statsService.getOverall(TEMP_USER_ID), - statsService.getDaily(TEMP_USER_ID, {limit: 84}), - statsService.getWeakness(TEMP_USER_ID), + statsService.getOverall(), + statsService.getDaily(null, {limit: 84}), + statsService.getWeakness(), ]) - setOverviewStats(overviewRes?.data) - setCalendarData(dailyRes?.data?.dailyStats || []) - setWeakWords(weakRes?.data?.weakWords || []) - setLevelProgress(overviewRes?.data?.levelProgress) - setDifficultyDist(overviewRes?.data?.difficultyDistribution) + // API 응답 데이터 접근 (data 필드 또는 직접 접근) + const overview = overviewRes?.data || overviewRes + const daily = dailyRes?.data || dailyRes + const weak = weakRes?.data || weakRes + + setOverviewStats(overview) + // 다양한 응답 형식 지원: history, dailyStats, 또는 배열 자체 + const calendarHistory = daily?.history || daily?.dailyStats || (Array.isArray(daily) ? daily : []) + setCalendarData(calendarHistory) + setWeakWords(weak?.frequentMistakes || weak?.weakWords || weak?.weakestWords || []) + setLevelProgress(overview?.levelProgress) + setDifficultyDist(overview?.difficultyDistribution) } catch (err) { console.error('Fetch stats error:', err) setError('통계를 불러오는데 실패했습니다.') @@ -351,19 +630,6 @@ export default function StatsPage() { } } - const fetchPeriodStats = async () => { - // 기간별 통계는 getDaily로 처리 - try { - const limits = [7, 30, 90] // 일간, 주간, 월간 - const response = await statsService.getDaily(TEMP_USER_ID, { - limit: limits[tab], - }) - // 기간별 통계 처리 - } catch (err) { - console.error('Period stats error:', err) - } - } - const handlePlayTTS = async (word) => { if (playingWordId) return @@ -392,117 +658,189 @@ export default function StatsPage() { return ( - + ) } + // 데이터 추출 + const totalLearned = overviewStats?.totalLearned || overviewStats?.newWordsLearned || 0 + const successRate = overviewStats?.successRate || overviewStats?.averageAccuracy || 0 + const currentStreak = overviewStats?.currentStreak || overviewStats?.streakDays || 0 + const longestStreak = overviewStats?.longestStreak || currentStreak + const testsCompleted = overviewStats?.testsCompleted || 0 + const correctAnswers = overviewStats?.correctAnswers || 0 + const incorrectAnswers = overviewStats?.incorrectAnswers || 0 + const wordsReviewed = overviewStats?.wordsReviewed || 0 + return ( - + {/* 헤더 */} - navigate('/vocab')}> + navigate('/vocab')} + sx={{ + backgroundColor: '#f3f4f6', + '&:hover': {backgroundColor: '#e5e7eb'}, + }} + > - - {t('stats.title')} - + + + 학습 통계 + + + {user?.username || '사용자'}님의 학습 현황 + + {error && ( - setError(null)}> + setError(null)}> {error} )} - {/* 기간 탭 */} - setTab(v)} - sx={{mb: 3}} - variant="fullWidth" - > - - - - - - {/* 요약 카드 */} + {/* 히어로 섹션 - 핵심 통계 */} - - + - - + - - + - - + - {/* 학습 캘린더 */} - - - {t('stats.learningHistory')} + {/* 추가 통계 미니 카드 */} + + + 상세 통계 - + + + + + + + + + + + + + + - {/* 레벨별 진행률 */} - - - {t('stats.levelProgress')} - - + {/* 학습 캘린더 */} + + + + + 학습 기록 + + + {/* 난이도 분포 */} - - - {t('stats.difficultyDist')} - - - + {difficultyDist && ( + + + 체감 난이도 분포 + + + + )} - {/* 취약 단어 */} - - - - {t('stats.weakWordsTop10')} + {/* 레벨별 진행률 */} + {levelProgress && ( + + + 레벨별 학습 진행률 - navigate('/vocab/daily?mode=weak')} - /> + + + )} + + {/* 복습이 필요한 단어 */} + + + + + + 복습이 필요한 단어 + + + {weakWords.length > 0 && ( + navigate('/vocab/daily?mode=weak')} + /> + )} { try { - const response = await testService.getResults(TEMP_USER_ID, {limit: 5}) + const response = await testService.getResults(userId, {limit: 5}) setRecentResults(response?.testResults || []) } catch (err) { console.error('Fetch results error:', err) @@ -508,7 +509,7 @@ export default function TestPage() { try { setLoading(true) setError(null) - const response = await testService.start(TEMP_USER_ID, 'DAILY') + const response = await testService.start(userId, 'DAILY') const testData = response?.data || response if (testData?.testId) { @@ -554,7 +555,7 @@ export default function TestPage() { answer: answers[q.wordId] || '', })) - const response = await testService.submit(TEMP_USER_ID, testId, answersArray) + const response = await testService.submit(userId, testId, answersArray) const resultData = response?.data || response if (resultData) { diff --git a/src/domains/vocab/pages/VocabDashboard.jsx b/src/domains/vocab/pages/VocabDashboard.jsx index 7667110..2df84b0 100644 --- a/src/domains/vocab/pages/VocabDashboard.jsx +++ b/src/domains/vocab/pages/VocabDashboard.jsx @@ -16,26 +16,30 @@ import { Typography, } from '@mui/material' import { + ArrowForward as ArrowIcon, CheckCircle as CheckIcon, - EmojiEvents as TrophyIcon, LocalFireDepartment as FireIcon, MenuBook as VocabIcon, PlayArrow as PlayIcon, Quiz as TestIcon, + School as LearnIcon, Star as StarIcon, StarBorder as StarBorderIcon, + Timeline as StatsIcon, TrendingUp as TrendingIcon, VolumeUp as VolumeIcon, + Warning as WarningIcon, } from '@mui/icons-material' import {dailyService, statsService, userWordService, voiceService} from '../services/vocabService' -import {DAILY_GOAL, LEVEL_LABELS,} from '../constants/vocabConstants' +import {DAILY_GOAL} from '../constants/vocabConstants' import {useTranslation} from '../../../contexts/SettingsContext' - -const TEMP_USER_ID = import.meta.env.VITE_TEMP_USER_ID || 'user1' +import {useAuth} from '../../../contexts/AuthContext' export default function VocabDashboard() { const navigate = useNavigate() const {t, isKorean} = useTranslation() + const {user} = useAuth() + const userId = user?.userId || user?.username const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [dailyData, setDailyData] = useState(null) @@ -54,16 +58,20 @@ export default function VocabDashboard() { setError(null) const [daily, stats, weekly, weakness] = await Promise.all([ - dailyService.getWords(TEMP_USER_ID).catch(() => null), - statsService.getOverall(TEMP_USER_ID).catch(() => null), - statsService.getDaily(TEMP_USER_ID, {limit: 7}).catch(() => null), - statsService.getWeakness(TEMP_USER_ID).catch(() => null), + dailyService.getWords().catch(() => null), + statsService.getOverall().catch(() => null), + statsService.getDaily(null, {limit: 7}).catch(() => null), + statsService.getWeakness().catch(() => null), ]) setDailyData(daily) setStatsData(stats) - setWeeklyStats(weekly?.dailyStats || []) - setWeakWords(weakness?.weakestWords?.slice(0, 5) || []) + // API: { data: { history: [...] } } 또는 mock: { history: [...] } + const weeklyData = weekly?.data || weekly + setWeeklyStats(weeklyData?.history || weeklyData?.dailyStats || []) + // API: { data: { frequentMistakes: [...] } } 또는 mock: { frequentMistakes: [...] } + const weaknessData = weakness?.data || weakness + setWeakWords(weaknessData?.frequentMistakes?.slice(0, 5) || weaknessData?.weakestWords?.slice(0, 5) || []) } catch (err) { console.error('Dashboard fetch error:', err) setError('Failed to load data.') @@ -81,6 +89,8 @@ export default function VocabDashboard() { audio.onended = () => setPlayingTTS(null) audio.onerror = () => setPlayingTTS(null) await audio.play() + } else { + setPlayingTTS(null) } } catch (err) { console.error('TTS error:', err) @@ -90,7 +100,7 @@ export default function VocabDashboard() { const handleToggleBookmark = async (word) => { try { - await userWordService.updateTag(TEMP_USER_ID, word.wordId, { + await userWordService.updateTag(userId, word.wordId, { bookmarked: !word.bookmarked, }) setWeakWords((prev) => @@ -113,36 +123,45 @@ export default function VocabDashboard() { ) } - const learnedCount = dailyData?.learnedCount || 0 - const totalWords = dailyData?.totalWords || DAILY_GOAL.TOTAL - const progress = totalWords > 0 ? (learnedCount / totalWords) * 100 : 0 - const newWordsCount = dailyData?.newWords?.length || 0 - const reviewWordsCount = dailyData?.reviewWords?.length || 0 + // API 응답 구조: { status, message, data: { dailyStudy, progress, newWords, reviewWords } } + // 또는 mock: { dailyStudy, progress, newWords, reviewWords } + const daily = dailyData?.data || dailyData + const learnedCount = daily?.progress?.learned || daily?.dailyStudy?.learnedCount || 0 + const totalWords = daily?.progress?.total || daily?.dailyStudy?.totalWords || DAILY_GOAL.TOTAL + const progress = daily?.progress?.percentage ?? (totalWords > 0 ? (learnedCount / totalWords) * 100 : 0) + const isCompleted = daily?.progress?.isCompleted || daily?.dailyStudy?.isCompleted || false + + // 통계 데이터 (API: { status, data: {...} } 또는 mock: {...}) + const stats = statsData?.data || statsData + const currentStreak = stats?.currentStreak || stats?.streakDays || 0 + const longestStreak = stats?.longestStreak || 0 + const wordsLearned = stats?.newWordsLearned || stats?.totalLearned || 0 + const successRate = stats?.successRate || stats?.averageAccuracy || 0 + const testsCompleted = stats?.testsCompleted || stats?.testCount || 0 - // Calculate streak from weekly stats - const streak = weeklyStats.filter(s => s?.isCompleted).length + // 주간 통계에서 완료일 수 계산 + const weeklyCompleted = weeklyStats.filter(s => s?.isCompleted).length return ( {/* Header */} - - + + - + - + {t('vocabDash.title')} @@ -153,504 +172,539 @@ export default function VocabDashboard() { {error && ( - + {error} )} - {/* Hero Progress Card */} - - {/* Decorative Elements */} - - - - - - - + {/* 오늘의 학습 카드 */} + + + + + + + + {isKorean ? '오늘의 학습' : "Today's Learning"} + + + + {Math.round(progress)} + + % + + + {isCompleted && ( + } + label={isKorean ? '완료' : 'Done'} + sx={{ + backgroundColor: 'white', + color: '#059669', + fontWeight: 700, + }} + /> + )} + + + + + + {learnedCount} / {totalWords} {isKorean ? '단어' : 'words'} + + + {totalWords - learnedCount} {isKorean ? '남음' : 'left'} + + + + + + + + + + + {/* 연속 학습 카드 */} + + + + + + {currentStreak} - - {Math.round(progress)}% + + {isKorean ? '일 연속 학습' : 'Day Streak'} - + + + {isKorean ? '최장 기록' : 'Best'}: {longestStreak} {isKorean ? '일' : 'days'} + + + + + + - {streak > 0 && ( + {/* 통계 요약 카드 4개 */} + + + navigate('/vocab/stats')} + > + - - - - {streak} - - - {t('vocabDash.days')} - - + - )} - - - - - - {learnedCount} / {totalWords} {t('vocabDash.wordsLearned')} + + {wordsLearned} - - - - - - - - {isKorean ? '새 단어' : 'New Words'} + + {isKorean ? '학습한 단어' : 'Words Learned'} - - {newWordsCount} / {DAILY_GOAL.NEW_WORDS} - - - - - {isKorean ? '복습' : 'Review'} - - - {reviewWordsCount} / {DAILY_GOAL.REVIEW_WORDS} - - - - - - - + + + - {/* Quick Actions */} - - + navigate('/vocab/stats')} > - + - + - - {t('vocabDash.viewStats')} + + {successRate.toFixed?.(0) || 0}% + + + {isKorean ? '정답률' : 'Accuracy'} - - {statsData?.totalWords || 0} + + + + + + navigate('/vocab/test')} + > + + + + + + {testsCompleted} - - {t('vocabDash.wordsLearned')} + + {isKorean ? '테스트 완료' : 'Tests Done'} - + + + + + navigate('/vocab/words')} + > + + + > + + + + {weeklyCompleted}/7 + + + {isKorean ? '이번주 완료' : 'This Week'} + + - + {/* 빠른 액션 */} + + navigate('/vocab/test')} > - + - + - - {t('vocabDash.takeQuiz')} - - - {statsData?.avgSuccessRate?.toFixed(0) || 0}% - - - {isKorean ? '평균 점수' : 'average score'} - - + + + {isKorean ? '퀴즈 풀기' : 'Take Quiz'} + + + {isKorean ? '실력을 테스트해보세요' : 'Test your knowledge'} + + + - + navigate('/vocab/words')} > - + - + - - {t('vocabDash.viewWordList')} - - - {statsData?.wordStatusCounts?.MASTERED || 0} - - - {isKorean ? '마스터' : 'mastered'} - - + + {isKorean ? '단어장' : 'Word List'} + + + {isKorean ? '학습한 단어 보기' : 'View your words'} + + + + + + + + + navigate('/vocab/stats')} + > + + + > + + + + + {isKorean ? '학습 통계' : 'Statistics'} + + + {isKorean ? '상세 통계 보기' : 'View detailed stats'} + + + - {/* Weekly Progress */} - + {/* 주간 학습 현황 */} + - - {t('vocabDash.weeklyProgress')} - - - {(isKorean ? ['월', '화', '수', '목', '금', '토', '일'] : ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']).map((day, index) => { - const stat = weeklyStats[index] - const isCompleted = stat?.isCompleted - const hasProgress = stat?.learnedCount > 0 - const isToday = index === new Date().getDay() - 1 || (new Date().getDay() === 0 && index === 6) + + + {isKorean ? '이번주 학습 현황' : 'This Week'} + + = 7 ? 'success' : 'default'} + /> + - return ( - - - {day} - + + {(() => { + const days = isKorean ? ['월', '화', '수', '목', '금', '토', '일'] : ['M', 'T', 'W', 'T', 'F', 'S', 'S'] + const today = new Date() + const dayOfWeek = today.getDay() + const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek + + return days.map((day, index) => { + const date = new Date(today) + date.setDate(today.getDate() + mondayOffset + index) + const dateStr = date.toISOString().split('T')[0] + + // 해당 날짜의 통계 찾기 + const stat = weeklyStats.find(s => s?.period === dateStr || s?.date === dateStr) + const isCompleted = stat?.isCompleted + const hasProgress = (stat?.newWordsLearned || stat?.learnedCount || 0) > 0 + const isToday = date.toDateString() === today.toDateString() + const isFuture = date > today + + return ( - {isCompleted ? ( - + + {day} + + + {date.getDate()} + + + {isCompleted ? ( - - ) : hasProgress ? ( - + ) : hasProgress ? ( - {stat?.learnedCount} + {stat?.newWordsLearned || stat?.learnedCount || 0} - - ) : ( - - )} + ) : isFuture ? null : ( + + X + + )} + - - ) - })} + ) + }) + })()} - {/* Weak Words */} + {/* 취약 단어 */} {weakWords.length > 0 && ( - + + - {t('vocabDash.focusWords')} + {isKorean ? '복습이 필요한 단어' : 'Words to Review'} - + - - {isKorean ? '추가 연습이 필요한 단어입니다' : 'These words need extra attention'} - - {weakWords.map((word, index) => ( - - - - + + {weakWords.map((word) => ( + + + {word.english} + + {word.korean} + + + + + + handlePlayTTS(word)} + disabled={playingTTS === word.wordId} + > + + + + + handleToggleBookmark(word)}> + {word.bookmarked ? ( + + ) : ( + + )} + + - - {word.korean} - - - - - - handlePlayTTS(word)} - disabled={playingTTS === word.wordId} - sx={{ - backgroundColor: playingTTS === word.wordId ? 'primary.main' : 'transparent', - '&:hover': {backgroundColor: 'rgba(5, 150, 105, 0.1)'}, - }} - > - - - - - handleToggleBookmark(word)}> - {word.bookmarked ? ( - - ) : ( - - )} - - - - - ))} + ))} + )} diff --git a/src/domains/vocab/pages/WordListPage.jsx b/src/domains/vocab/pages/WordListPage.jsx index e9c82c8..530fd6b 100644 --- a/src/domains/vocab/pages/WordListPage.jsx +++ b/src/domains/vocab/pages/WordListPage.jsx @@ -28,8 +28,7 @@ import WordDetailModal from '../components/WordDetailModal' import {myWordService, voiceService} from '../services/vocabService' import {LEVEL_LABELS, WORD_STATUS_LABELS,} from '../constants/vocabConstants' import {useTranslation} from '../../../contexts/SettingsContext' - -const TEMP_USER_ID = import.meta.env.VITE_TEMP_USER_ID || 'user1' +import {useAuth} from '../../../contexts/AuthContext' const PAGE_SIZE = 20 // 디바운스 훅 @@ -47,6 +46,8 @@ function useDebounce(value, delay) { export default function WordListPage() { const navigate = useNavigate() const {t} = useTranslation() + const {user} = useAuth() + const userId = user?.userId || user?.username const [searchParams] = useSearchParams() const observerRef = useRef(null) const loadMoreRef = useRef(null) @@ -87,7 +88,7 @@ export default function WordListPage() { params.incorrectOnly = true } - const response = await myWordService.getList(TEMP_USER_ID, params) + const response = await myWordService.getList(userId, params) const data = response?.data || response const newWords = data?.userWords || [] @@ -168,7 +169,7 @@ export default function WordListPage() { const newBookmarked = !word.bookmarked try { - await myWordService.toggleBookmark(TEMP_USER_ID, word.wordId, newBookmarked) + await myWordService.toggleBookmark(userId, word.wordId, newBookmarked) setUserWords(prev => prev.map(w => diff --git a/src/domains/vocab/services/vocabService.js b/src/domains/vocab/services/vocabService.js index 613b1d7..32843f5 100644 --- a/src/domains/vocab/services/vocabService.js +++ b/src/domains/vocab/services/vocabService.js @@ -1,7 +1,7 @@ import vocabApi from '../../../api/vocabApi' -// Mock 데이터 사용 여부 (true: 목 데이터 사용, false: 실제 API 호출) -const USE_MOCK = true +// Mock 데이터 사용 여부 (환경변수로 제어: VITE_USE_MOCK=true) +const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true' // ============================================ // Mock 데이터 @@ -235,7 +235,10 @@ const withMock = (apiCall, mockData) => { // interceptor가 response.data를 반환하므로 mockData를 직접 반환 return Promise.resolve(mockData) } - return apiCall().catch(() => mockData) + // 실제 API 호출 시 응답의 data 필드 추출 (백엔드 응답: { isSuccess, message, data }) + return apiCall() + .then(response => response.data || response) + .catch(() => mockData) } /** @@ -245,7 +248,7 @@ export const wordService = { // GET /words - 단어 목록 조회 getList: ({level, category, limit = 20, cursor} = {}) => withMock( - () => vocabApi.get('/words', {params: {level, category, limit, cursor}}), + () => vocabApi.get('/vocab/words', {params: {level, category, limit, cursor}}), { words: mockWords.filter(w => (!level || w.level === level) && (!category || w.category === category)).slice(0, limit), hasMore: false, @@ -256,7 +259,7 @@ export const wordService = { // GET /words - 단어 목록 조회 (별칭) getWords: (params) => withMock( - () => vocabApi.get('/words', {params}), + () => vocabApi.get('/vocab/words', {params}), {words: mockWords, hasMore: false} ), @@ -284,14 +287,14 @@ export const wordService = { // POST /words/batch - 배치 단어 생성 createBatch: (words) => withMock( - () => vocabApi.post('/words/batch', {words}), + () => vocabApi.post('/vocab/words/batch', {words}), {successCount: words.length, failCount: 0, totalRequested: words.length} ), // POST /words/batch/get - 배치 단어 조회 getBatch: (wordIds) => withMock( - () => vocabApi.post('/words/batch/get', {wordIds}), + () => vocabApi.post('/vocab/words/batch/get', {wordIds}), { words: mockWords.filter(w => wordIds.includes(w.wordId)), requestedCount: wordIds.length, @@ -301,31 +304,45 @@ export const wordService = { } /** - * 일일 학습 API - Backend: POST /daily-study/record, GET /user-words/review + * 일일 학습 API - Backend: GET /vocab/daily?level={level}, POST /vocab/daily/words/{wordId}/learned + * userId는 토큰에서 추출됨 */ export const dailyService = { - // 일일 학습용 단어 조회 (새 단어 + 복습 단어) - getWords: (userId, level) => + // GET /vocab/daily?level={level} - 오늘의 학습 단어 조회 + // 첫 호출 시 자동으로 생성됨 + getWords: (level) => withMock( - () => vocabApi.get('/user-words/review', {params: {userId, ...(level ? {level} : {})}}), + () => vocabApi.get('/vocab/daily', {params: {level: level?.toUpperCase()}}), { - newWords: mockWords.filter(w => !level || w.level === level).slice(0, 10), + dailyStudy: { + date: new Date().toISOString().split('T')[0], + totalWords: 55, + learnedCount: 0, + isCompleted: false, + }, + newWords: mockWords.filter(w => !level || w.level === level.toUpperCase()).slice(0, 50), reviewWords: mockUserWords.filter(w => w.status === 'REVIEWING').slice(0, 5), - learnedCount: 0, - isCompleted: false, + progress: { + total: 55, + learned: 0, + remaining: 55, + percentage: 0, + isCompleted: false, + }, } ), - // POST /daily-study/record - 일일 학습 기록 - markLearned: (userId, wordId, isCorrect, studyType = 'REVIEW') => + // POST /vocab/daily/words/{wordId}/learned - 단어 학습 완료 표시 + // body 필요 없음 (userId는 토큰에서 추출) + markLearned: (wordId) => withMock( - () => vocabApi.post('/daily-study/record', {userId, wordId, isCorrect, studyType}), + () => vocabApi.post(`/vocab/daily/words/${wordId}/learned`), { - userId, - date: new Date().toISOString().split('T')[0], - wordsStudied: 1, - correctCount: isCorrect ? 1 : 0, - incorrectCount: isCorrect ? 0 : 1, + total: 55, + learned: 1, + remaining: 54, + percentage: 1.82, + isCompleted: false, } ), } @@ -337,7 +354,7 @@ export const userWordService = { // GET /user-words/review - 복습 예정 단어 조회 getList: (userId, {status, limit = 20, cursor, date} = {}) => withMock( - () => vocabApi.get('/user-words/review', {params: {userId, status, limit, cursor, date}}), + () => vocabApi.get('/vocab/user-words', {params: {userId, status, limit, cursor, date}}), { userWords: mockUserWords.filter(w => !status || w.status === status).slice(0, limit), hasMore: false, @@ -348,14 +365,14 @@ export const userWordService = { // GET /user-words/review - 사용자 단어 조회 (별칭) getUserWords: (userId, params) => withMock( - () => vocabApi.get('/user-words/review', {params: {userId, ...params}}), + () => vocabApi.get('/vocab/user-words', {params: {userId, ...params}}), {words: mockUserWords, hasMore: false} ), - // POST /user-words/{wordId}/review - 사용자 단어 학습 업데이트 + // PUT /user-words/{wordId} - 사용자 단어 학습 업데이트 update: (userId, wordId, isCorrect) => withMock( - () => vocabApi.post(`/user-words/${wordId}/review`, {userId, isCorrect}), + () => vocabApi.put(`/vocab/user-words/${wordId}`, {userId, isCorrect}), { userId, wordId, @@ -371,16 +388,18 @@ export const userWordService = { ), // PATCH /user-words/{wordId}/tag - 사용자 단어 태그 업데이트 + // userId는 토큰에서 추출되므로 body에 포함하지 않음 updateTag: (userId, wordId, {bookmarked, favorite, difficulty}) => withMock( - () => vocabApi.patch(`/user-words/${wordId}/tag`, {userId, bookmarked, favorite, difficulty}), + () => vocabApi.patch(`/vocab/user-words/${wordId}/tag`, {bookmarked, favorite, difficulty}), {success: true, userId, wordId, bookmarked, favorite, difficulty} ), // PATCH /user-words/{wordId}/tag - 사용자 단어 업데이트 (별칭) + // userId는 토큰에서 추출되므로 body에 포함하지 않음 updateUserWord: (userId, wordId, data) => withMock( - () => vocabApi.patch(`/user-words/${wordId}/tag`, {userId, ...data}), + () => vocabApi.patch(`/vocab/user-words/${wordId}/tag`, data), {success: true, ...data} ), } @@ -392,7 +411,7 @@ export const myWordService = { // GET /user-words/review - 나의 단어 목록 (필터링) getList: (userId, {bookmarked, incorrectOnly, limit = 20, cursor} = {}) => withMock( - () => vocabApi.get('/user-words/review', { + () => vocabApi.get('/vocab/user-words', { params: {userId, bookmarked, incorrectOnly, limit, cursor} }), { @@ -406,21 +425,22 @@ export const myWordService = { // 북마크된 단어 조회 getBookmarked: (userId, {limit = 20, cursor} = {}) => withMock( - () => vocabApi.get('/user-words/review', {params: {userId, bookmarked: true, limit, cursor}}), + () => vocabApi.get('/vocab/user-words', {params: {userId, bookmarked: true, limit, cursor}}), {userWords: mockUserWords.filter(w => w.bookmarked).slice(0, limit), hasMore: false} ), // 오답 단어 조회 getIncorrect: (userId, {limit = 20, cursor} = {}) => withMock( - () => vocabApi.get('/user-words/review', {params: {userId, incorrectOnly: true, limit, cursor}}), + () => vocabApi.get('/vocab/user-words', {params: {userId, incorrectOnly: true, limit, cursor}}), {userWords: mockUserWords.filter(w => w.incorrectCount > 0).slice(0, limit), hasMore: false} ), // PATCH /user-words/{wordId}/tag - 북마크 토글 + // userId는 토큰에서 추출되므로 body에 포함하지 않음 toggleBookmark: (userId, wordId, bookmarked) => withMock( - () => vocabApi.patch(`/user-words/${wordId}/tag`, {userId, bookmarked}), + () => vocabApi.patch(`/vocab/user-words/${wordId}/tag`, {bookmarked}), {success: true, wordId, bookmarked} ), } @@ -432,7 +452,7 @@ export const testService = { // POST /tests/start - 시험 시작 start: (userId, testType = 'DAILY', wordCount = 20, level) => withMock( - () => vocabApi.post('/tests/start', {userId, testType, wordCount, level}), + () => vocabApi.post('/vocab/test/start', {userId, testType, wordCount, level}), { testId: `test-${Date.now()}`, testType, @@ -445,10 +465,10 @@ export const testService = { } ), - // POST /tests/{testId}/submit - 시험 제출 + // POST /vocab/test/submit - 시험 제출 submit: (userId, testId, answers) => withMock( - () => vocabApi.post(`/tests/${testId}/submit`, {userId, answers}), + () => vocabApi.post('/vocab/test/submit', {userId, testId, answers}), { testId, totalQuestions: answers.length, @@ -463,63 +483,95 @@ export const testService = { // 시험 결과 조회 (프론트엔드 전용 - 백엔드에서 미구현) getResults: (userId, {limit = 20, cursor} = {}) => withMock( - () => vocabApi.get('/tests/results', {params: {userId, limit, cursor}}), + () => vocabApi.get('/vocab/test/results', {params: {userId, limit, cursor}}), {testResults: mockTestResults.slice(0, limit), hasMore: false} ), } /** - * 통계 API - Backend: GET /statistics + * 통계 API - Backend: GET /stats/total, GET /stats/history, GET /vocab/stats/weakness + * userId는 토큰에서 추출되므로 파라미터로 전달하지 않음 */ export const statsService = { - // GET /statistics - 학습 통계 조회 - getOverall: (userId, period = 'ALL') => + // GET /stats/total - 전체 통계 조회 + getOverall: () => withMock( - () => vocabApi.get('/statistics', {params: {userId, period}}), + () => vocabApi.get('/stats/total'), { - totalWords: mockWords.length, - totalLearned: 15, - masteredWords: 5, - learningWords: 8, - newWords: 7, - averageSuccessRate: 78.5, - averageAccuracy: 78.5, - studyStreak: 7, + periodType: 'TOTAL', + period: 'ALL', + testsCompleted: 4, + questionsAnswered: 100, + correctAnswers: 78, + incorrectAnswers: 22, + successRate: 78.0, + newWordsLearned: 50, + wordsReviewed: 20, + currentStreak: 7, + longestStreak: 15, + lastStudyDate: new Date().toISOString().split('T')[0], + // 프론트엔드 호환용 필드 + totalLearned: 50, + averageSuccessRate: 78.0, + averageAccuracy: 78.0, streakDays: 7, - dailyStats: generateDailyStats().slice(0, 7), - levelProgress: { - BEGINNER: {total: 8, learned: 6}, - INTERMEDIATE: {total: 7, learned: 5}, - ADVANCED: {total: 5, learned: 2}, - }, - difficultyDistribution: { - EASY: 6, - NORMAL: 9, - HARD: 5, - }, } ), - // GET /statistics - 기간별 통계 (프론트엔드 래핑) - getDaily: (userId, {limit = 30, period = 'MONTH'} = {}) => + // GET /stats/history - 히스토리 조회 (히트맵/차트용) + getDaily: (userId, {limit = 7} = {}) => withMock( - () => vocabApi.get('/statistics', {params: {userId, period}}), - {dailyStats: generateDailyStats().slice(0, limit)} + () => vocabApi.get('/stats/history', {params: {limit}}), + { + history: generateDailyStats().slice(0, limit).map(s => ({ + period: s.date, + testsCompleted: Math.floor(Math.random() * 3), + questionsAnswered: s.wordsStudied, + correctAnswers: s.correctCount, + successRate: s.successRate, + newWordsLearned: s.learnedCount, + wordsReviewed: Math.floor(Math.random() * 10), + // 프론트엔드 호환용 + date: s.date, + learnedCount: s.learnedCount, + isCompleted: s.learnedCount >= 55, + })), + dailyStats: generateDailyStats().slice(0, limit), + hasMore: false, + } ), - // 취약 단어 조회 (프론트엔드 전용 - 백엔드에서 미구현) - getWeakness: (userId) => + // GET /vocab/stats/weakness - 취약점 분석 + getWeakness: () => withMock( - () => vocabApi.get('/statistics', {params: {userId, includeWeak: true}}), + () => vocabApi.get('/vocab/stats/weakness'), { + weakCategories: [ + {category: 'BUSINESS', incorrectRate: 35.5, totalAnswered: 100, incorrectCount: 35}, + {category: 'ACADEMIC', incorrectRate: 28.0, totalAnswered: 50, incorrectCount: 14}, + ], + frequentMistakes: mockUserWords + .filter(w => w.incorrectCount > 0) + .sort((a, b) => b.incorrectCount - a.incorrectCount) + .slice(0, 10) + .map(w => ({ + wordId: w.wordId, + english: w.english, + korean: w.korean, + incorrectCount: w.incorrectCount, + accuracy: Math.round((w.correctCount / (w.correctCount + w.incorrectCount)) * 100), + })), weakWords: mockUserWords .filter(w => w.incorrectCount > 0) - .sort((a, b) => (a.correctCount / (a.correctCount + a.incorrectCount)) - (b.correctCount / (b.correctCount + b.incorrectCount))) .slice(0, 10) .map(w => ({ ...w, accuracy: Math.round((w.correctCount / (w.correctCount + w.incorrectCount)) * 100), })), + weakestWords: mockUserWords + .filter(w => w.incorrectCount > 0) + .slice(0, 5), + recommendedReview: 15, } ), } @@ -531,7 +583,7 @@ export const voiceService = { // POST /voice/synthesize - 음성 합성 synthesize: (wordId, text, voice = 'female', type = 'word') => withMock( - () => vocabApi.post('/voice/synthesize', {wordId, text, voice, type}), + () => vocabApi.post('/vocab/voice/synthesize', {wordId, text, voice, type}), { audioUrl: null, // Mock에서는 실제 오디오 없음 cached: false, diff --git a/src/layouts/AuthLayout/index.jsx b/src/layouts/AuthLayout/index.jsx new file mode 100644 index 0000000..cc67932 --- /dev/null +++ b/src/layouts/AuthLayout/index.jsx @@ -0,0 +1,50 @@ +import { Box, Paper, Typography } from '@mui/material'; + +export default function AuthLayout({ children }) { + return ( + + + + + + AI + + + + AI 언어 학습 + + + {children} + + + ); +} \ No newline at end of file diff --git a/src/layouts/MainLayout/Header/index.jsx b/src/layouts/MainLayout/Header/index.jsx index e468f15..b31fb98 100644 --- a/src/layouts/MainLayout/Header/index.jsx +++ b/src/layouts/MainLayout/Header/index.jsx @@ -1,5 +1,5 @@ -import {useState} from 'react' -import {useNavigate} from 'react-router-dom' +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' import { AppBar, Avatar, @@ -25,21 +25,23 @@ import { Settings as SettingsIcon, Translate as TranslateIcon, } from '@mui/icons-material' -import {useThemeMode} from '../../../contexts/ThemeContext' -import {useSettings, useTranslation} from '../../../contexts/SettingsContext' -import {LANGUAGE_LABELS, LANGUAGES} from '../../../i18n/translations' +import { useThemeMode } from '../../../contexts/ThemeContext' +import { useSettings, useTranslation } from '../../../contexts/SettingsContext' +import { LANGUAGE_LABELS, LANGUAGES } from '../../../i18n/translations' +import { useAuth } from '../../../contexts/AuthContext' -const Header = ({onMenuClick, sidebarOpen}) => { +const Header = ({ onMenuClick, sidebarOpen }) => { const theme = useTheme() const navigate = useNavigate() const isMobile = useMediaQuery(theme.breakpoints.down('md')) - const {mode, toggleTheme} = useThemeMode() - const {setLanguage, language} = useSettings() - const {t} = useTranslation() + const { mode, toggleTheme } = useThemeMode() + const { setLanguage, language } = useSettings() + const { t } = useTranslation() const [anchorEl, setAnchorEl] = useState(null) const [notificationAnchor, setNotificationAnchor] = useState(null) const [langAnchor, setLangAnchor] = useState(null) + const { logout } = useAuth() const handleProfileMenuOpen = (event) => { setAnchorEl(event.currentTarget) @@ -70,8 +72,9 @@ const Header = ({onMenuClick, sidebarOpen}) => { handleLangClose() } - const handleLogout = () => { + const handleLogout = async () => { handleProfileMenuClose() + await logout() // Cognito 로그아웃 (토큰 삭제) navigate('/login') } @@ -89,7 +92,7 @@ const Header = ({onMenuClick, sidebarOpen}) => { borderColor: mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)', }} > - + {/* Hamburger menu (mobile) */} {isMobile && ( { }, }} > - + )} @@ -141,7 +144,7 @@ const Header = ({onMenuClick, sidebarOpen}) => { L - + { - + {/* Right side icons */} - + {/* Language selector */} { }, }} > - + {/* Dark mode toggle */} @@ -188,7 +191,7 @@ const Header = ({onMenuClick, sidebarOpen}) => { }, }} > - {mode === 'dark' ? : } + {mode === 'dark' ? : } {/* Notifications */} @@ -214,14 +217,14 @@ const Header = ({onMenuClick, sidebarOpen}) => { }, }} > - + {/* Profile */} { minWidth: 160, }, }} - transformOrigin={{horizontal: 'right', vertical: 'top'}} - anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} > - + {t('settings.language')} - + {Object.entries(LANGUAGES).map(([key, value]) => ( { boxShadow: '0 10px 40px -10px rgba(0,0,0,0.2)', }, }} - transformOrigin={{horizontal: 'right', vertical: 'top'}} - anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} > - + {t('nav.notifications')} @@ -327,7 +330,7 @@ const Header = ({onMenuClick, sidebarOpen}) => { }} /> - + {[ { text: language === 'ko' ? '면접 연습 세션이 완료되었습니다.' : 'Interview session completed.', @@ -354,7 +357,7 @@ const Header = ({onMenuClick, sidebarOpen}) => { }} > - + {item.text} @@ -378,10 +381,10 @@ const Header = ({onMenuClick, sidebarOpen}) => { boxShadow: '0 10px 40px -10px rgba(0,0,0,0.2)', }, }} - transformOrigin={{horizontal: 'right', vertical: 'top'}} - anchorOrigin={{horizontal: 'right', vertical: 'bottom'}} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} > - + {t('header.user')} @@ -389,15 +392,15 @@ const Header = ({onMenuClick, sidebarOpen}) => { user@example.com - + { handleProfileMenuClose(); navigate('/profile'); }} - sx={{py: 1.5, px: 2.5}} + sx={{ py: 1.5, px: 2.5 }} > - + {t('nav.profile')} { handleProfileMenuClose(); navigate('/settings'); }} - sx={{py: 1.5, px: 2.5}} + sx={{ py: 1.5, px: 2.5 }} > - + {t('nav.settings')} - + - + {t('nav.logout')} diff --git a/src/main.jsx b/src/main.jsx index 9cabb0e..7e54abe 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,13 +1,18 @@ -import {StrictMode} from 'react' -import {createRoot} from 'react-dom/client' -import {Provider} from 'react-redux' -import {BrowserRouter} from 'react-router-dom' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { Provider } from 'react-redux' +import { BrowserRouter } from 'react-router-dom' import App from './App.jsx' -import {store} from './store' -import {ThemeProvider} from './contexts/ThemeContext' -import {ChatProvider} from './contexts/ChatContext' -import {SettingsProvider} from './contexts/SettingsContext' +import { store } from './store' +import { ThemeProvider } from './contexts/ThemeContext' +import { ChatProvider } from './contexts/ChatContext' +import { SettingsProvider } from './contexts/SettingsContext' import './index.css' +import { Amplify } from 'aws-amplify' +import { AuthProvider } from './contexts/AuthContext.jsx' +import awsConfig from './aws-config' + +Amplify.configure(awsConfig) createRoot(document.getElementById('root')).render( @@ -16,7 +21,9 @@ createRoot(document.getElementById('root')).render( - + + + diff --git a/src/pages/Dashboard/index.jsx b/src/pages/Dashboard/index.jsx new file mode 100644 index 0000000..f50cc72 --- /dev/null +++ b/src/pages/Dashboard/index.jsx @@ -0,0 +1,26 @@ +import { Box, Typography, Button, Card, CardContent } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '../../contexts/AuthContext'; + +export default function DashboardPage() { + const navigate = useNavigate(); + const { user, logout } = useAuth(); + + const handleLogout = async () => { + await logout(); + navigate('/login'); + }; + + return ( + + 🎉 로그인 성공! + + + 이메일: {user?.email} + JWT 토큰이 자동으로 관리되고 있습니다. + + + + + ); +} \ No newline at end of file diff --git a/src/pages/Login/index.jsx b/src/pages/Login/index.jsx new file mode 100644 index 0000000..76ce27f --- /dev/null +++ b/src/pages/Login/index.jsx @@ -0,0 +1,12 @@ +import { useNavigate } from 'react-router-dom'; +import AuthLayout from '../../layouts/AuthLayout'; +import LoginForm from '../../domains/auth/components/LoginForm'; + +export default function LoginPage() { + const navigate = useNavigate(); + return ( + + navigate('/signup')} /> + + ); +} \ No newline at end of file diff --git a/src/pages/SignUp/index.jsx b/src/pages/SignUp/index.jsx new file mode 100644 index 0000000..b04ab2a --- /dev/null +++ b/src/pages/SignUp/index.jsx @@ -0,0 +1,12 @@ +import { useNavigate } from 'react-router-dom'; +import AuthLayout from '../../layouts/AuthLayout'; +import SignupForm from '../../domains/auth/components/SignupForm'; + +export default function SignUpPage() { + const navigate = useNavigate(); + return ( + + navigate('/login')} /> + + ); +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 4c5fcda..9833a92 100644 --- a/vite.config.js +++ b/vite.config.js @@ -5,7 +5,15 @@ export default defineConfig({ plugins: [react()], server: { port: 3000, - open: true + open: true, + proxy: { + '/api': { + target: 'https://gc8l9ijhzc.execute-api.ap-northeast-2.amazonaws.com/dev', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), + secure: true, + } + } }, build: { outDir: 'dist',