From f3906a52ac471d1a15eccdffbd8e35112ea0005b Mon Sep 17 00:00:00 2001 From: Seongbin Kim Date: Sun, 12 Jul 2026 15:09:59 +0900 Subject: [PATCH] =?UTF-8?q?feat(self-host):=20Prisma=20=EA=B8=B0=EB=B0=98?= =?UTF-8?q?=20=EB=8B=A8=EC=9D=BC=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EA=B4=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AWS serverless 구성을 제거하고 NestJS, Prisma, MySQL, Caddy 기반 Docker 배포로 전환한다. 로그 원문을 revision으로 보존하고 stale 저장은 current로 승격하지 않는다. --- .dockerignore | 10 + .env.development.example | 15 - .env.example | 11 +- .env.production.example | 15 - .github/workflows/deploy.yml | 110 - .gitignore | 2 +- Caddyfile | 11 + Dockerfile.api | 29 + Dockerfile.web | 16 + README.md | 297 +- apps/api/docker-compose.yml | 34 - .../{eslint.config.js => eslint.config.mjs} | 4 +- apps/api/package.json | 44 +- .../20260712000000_init/migration.sql | 62 + .../api/prisma/migrations/migration_lock.toml | 1 + apps/api/prisma/schema.prisma | 70 + apps/api/scripts/create-local-tables.ts | 111 - apps/api/scripts/delete-local-tables.ts | 44 - apps/api/scripts/with-root-env.sh | 23 - apps/api/serverless.yml | 161 - apps/api/src/app.module.ts | 31 + apps/api/src/auth/auth.controller.ts | 23 + apps/api/src/auth/auth.guard.ts | 40 + apps/api/src/auth/auth.service.ts | 177 + apps/api/src/db.ts | 39 - apps/api/src/db/prisma.service.ts | 16 + apps/api/src/index.ts | 373 - apps/api/src/logs.ts | 163 - apps/api/src/logs/hash.ts | 9 + apps/api/src/logs/logs.controller.ts | 85 + apps/api/src/logs/logs.repository.ts | 246 + apps/api/src/logs/logs.service.test.ts | 147 + apps/api/src/logs/logs.service.ts | 123 + apps/api/src/main.ts | 44 + apps/api/src/settings/settings.controller.ts | 30 + apps/api/src/settings/settings.repository.ts | 74 + apps/api/src/settings/settings.service.ts | 20 + apps/api/src/users.ts | 101 - apps/api/src/users/users.repository.ts | 38 + apps/api/tsconfig.json | 4 +- apps/web/package.json | 7 +- apps/web/serverless.yml | 288 - .../dataManagement/backupService.test.ts | 74 +- .../features/dataManagement/backupService.ts | 50 +- apps/web/src/services/LogService.ts | 5 + .../store/RawLogStorageSyncMiddleware.test.ts | 167 + .../src/store/RawLogStorageSyncMiddleware.ts | 20 +- docker-compose.yml | 25 + docs/API_GATEWAY_HTTPS_SETUP.md | 90 - docs/CLOUDFORMATION_GUIDE.md | 100 - docs/DEPLOYMENT.md | 215 - docs/EDITOR_SYNC_REDESIGN.md | 22 +- docs/LOCAL_ENV_TECH_COMPARISON.md | 64 - docs/SYNC_POLICY_COMPARISON.md | 667 ++ .../001-api-framework-selection.md | 2 + .../003-conflict-detection-system.md | 2 + .../CONFLICT_DETECTION.md | 18 +- ...-self-hosted-nestjs-mysql-revision-sync.md | 85 + docs/apps/api/README.md | 228 +- ...6-06-21-self-hosted-mysql-revision-sync.md | 192 + package.json | 21 +- pnpm-lock.yaml | 6564 ++--------------- scripts/check-porkbun-dns.sh | 274 - scripts/create-aws-acm.sh | 293 - scripts/deploy.sh | 63 - scripts/dns-providers/porkbun.mjs | 240 - scripts/dns-sync.mjs | 58 - scripts/lib/deploy-common.sh | 174 - scripts/reset-aws.sh | 362 - scripts/sync-dns-after-deploy.sh | 90 - scripts/sync-dns.sh | 106 - 71 files changed, 3608 insertions(+), 9811 deletions(-) create mode 100644 .dockerignore delete mode 100644 .env.development.example delete mode 100644 .env.production.example delete mode 100644 .github/workflows/deploy.yml create mode 100644 Caddyfile create mode 100644 Dockerfile.api create mode 100644 Dockerfile.web delete mode 100644 apps/api/docker-compose.yml rename apps/api/{eslint.config.js => eslint.config.mjs} (91%) create mode 100644 apps/api/prisma/migrations/20260712000000_init/migration.sql create mode 100644 apps/api/prisma/migrations/migration_lock.toml create mode 100644 apps/api/prisma/schema.prisma delete mode 100644 apps/api/scripts/create-local-tables.ts delete mode 100644 apps/api/scripts/delete-local-tables.ts delete mode 100644 apps/api/scripts/with-root-env.sh delete mode 100644 apps/api/serverless.yml create mode 100644 apps/api/src/app.module.ts create mode 100644 apps/api/src/auth/auth.controller.ts create mode 100644 apps/api/src/auth/auth.guard.ts create mode 100644 apps/api/src/auth/auth.service.ts delete mode 100644 apps/api/src/db.ts create mode 100644 apps/api/src/db/prisma.service.ts delete mode 100644 apps/api/src/index.ts delete mode 100644 apps/api/src/logs.ts create mode 100644 apps/api/src/logs/hash.ts create mode 100644 apps/api/src/logs/logs.controller.ts create mode 100644 apps/api/src/logs/logs.repository.ts create mode 100644 apps/api/src/logs/logs.service.test.ts create mode 100644 apps/api/src/logs/logs.service.ts create mode 100644 apps/api/src/main.ts create mode 100644 apps/api/src/settings/settings.controller.ts create mode 100644 apps/api/src/settings/settings.repository.ts create mode 100644 apps/api/src/settings/settings.service.ts delete mode 100644 apps/api/src/users.ts create mode 100644 apps/api/src/users/users.repository.ts delete mode 100644 apps/web/serverless.yml create mode 100644 apps/web/src/store/RawLogStorageSyncMiddleware.test.ts create mode 100644 docker-compose.yml delete mode 100644 docs/API_GATEWAY_HTTPS_SETUP.md delete mode 100644 docs/CLOUDFORMATION_GUIDE.md delete mode 100644 docs/DEPLOYMENT.md delete mode 100644 docs/LOCAL_ENV_TECH_COMPARISON.md create mode 100644 docs/SYNC_POLICY_COMPARISON.md create mode 100644 docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md create mode 100644 docs/superpowers/plans/2026-06-21-self-hosted-mysql-revision-sync.md delete mode 100644 scripts/check-porkbun-dns.sh delete mode 100644 scripts/create-aws-acm.sh delete mode 100644 scripts/deploy.sh delete mode 100644 scripts/dns-providers/porkbun.mjs delete mode 100644 scripts/dns-sync.mjs delete mode 100644 scripts/lib/deploy-common.sh delete mode 100644 scripts/reset-aws.sh delete mode 100644 scripts/sync-dns-after-deploy.sh delete mode 100644 scripts/sync-dns.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..330fac3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +node_modules +apps/*/node_modules +apps/*/dist +apps/web/dist +.git +.turbo +.serverless +.dynamodb-data +coverage +*.log diff --git a/.env.development.example b/.env.development.example deleted file mode 100644 index 6abd190..0000000 --- a/.env.development.example +++ /dev/null @@ -1,15 +0,0 @@ -JWT_SECRET=your-fixed-secret - -WEB_DOMAIN_NAME=dev.my-commit.com -ACM_CERTIFICATE_ARN=arn:aws:acm:us-east-1:ACCOUNT_ID:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -# AWS_PROFILE=default - -# Optional: DNS sync after deploy -# UPDATE_DNS_RECORD=true -# DNS_PROVIDER=porkbun -# PORKBUN_API_KEY=pk1_xxxxxxxxxxxxx -# PORKBUN_SECRET_API_KEY=sk1_xxxxxxxxxxxxx -# PORKBUN_ROOT_DOMAIN=my-commit.com -# PORKBUN_RECORD_TYPE=CNAME -# PORKBUN_TTL=600 -# PORKBUN_DRY_RUN=false diff --git a/.env.example b/.env.example index 9b6d03c..3d52169 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,17 @@ # Local common settings # This project uses root-level env files only. -AWS_REGION=ap-northeast-2 JWT_SECRET=your-fixed-secret # Web local dev -> API endpoint VITE_API_URL=http://localhost:3000 + +# Prisma CLI and local API -> host MySQL +DATABASE_URL=mysql://my_commit:my_commit@127.0.0.1:3306/my_commit + +# Docker API -> host MySQL +DOCKER_DATABASE_URL=mysql://my_commit:my_commit@host.docker.internal:3306/my_commit + +# Self-host web port exposed by docker-compose. +WEB_PORT=8080 +ALLOWED_ORIGINS=http://localhost,http://localhost:8080,http://localhost:4000 diff --git a/.env.production.example b/.env.production.example deleted file mode 100644 index 35e7d27..0000000 --- a/.env.production.example +++ /dev/null @@ -1,15 +0,0 @@ -JWT_SECRET=your-fixed-secret - -WEB_DOMAIN_NAME=my-commit.com -ACM_CERTIFICATE_ARN=arn:aws:acm:us-east-1:ACCOUNT_ID:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -# AWS_PROFILE=default - -# Optional: DNS sync after deploy -# UPDATE_DNS_RECORD=true -# DNS_PROVIDER=porkbun -# PORKBUN_API_KEY=pk1_xxxxxxxxxxxxx -# PORKBUN_SECRET_API_KEY=sk1_xxxxxxxxxxxxx -# PORKBUN_ROOT_DOMAIN=my-commit.com -# PORKBUN_RECORD_TYPE=ALIAS -# PORKBUN_TTL=600 -# PORKBUN_DRY_RUN=false diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 90f67ab..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Deploy Production - -on: - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: deploy-production - cancel-in-progress: true - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - with: - version: 9.0.0 - - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: "pnpm" - - - run: pnpm install --frozen-lockfile - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ap-northeast-2 - - - name: Resolve web origin for API CORS - id: web-origin - shell: bash - env: - WEB_DOMAIN_NAME: ${{ secrets.WEB_DOMAIN_NAME }} - ACM_CERTIFICATE_ARN: ${{ secrets.ACM_CERTIFICATE_ARN }} - run: | - set -euo pipefail - if [[ -z "${WEB_DOMAIN_NAME:-}" || -z "${ACM_CERTIFICATE_ARN:-}" ]]; then - echo "Custom domain is required for CI deployment." >&2 - echo "Set both WEB_DOMAIN_NAME and ACM_CERTIFICATE_ARN secrets." >&2 - exit 1 - fi - - WEB_ORIGIN="https://${WEB_DOMAIN_NAME}" - echo "web_origin=${WEB_ORIGIN}" >> "$GITHUB_OUTPUT" - echo "web_healthcheck_url=https://${WEB_DOMAIN_NAME}" >> "$GITHUB_OUTPUT" - - - name: Deploy (prod) - env: - JWT_SECRET: ${{ secrets.JWT_SECRET }} - WEB_DOMAIN_NAME: ${{ secrets.WEB_DOMAIN_NAME }} - ACM_CERTIFICATE_ARN: ${{ secrets.ACM_CERTIFICATE_ARN }} - AWS_REGION: ap-northeast-2 - run: pnpm run deploy:prod - - - name: Resolve API URL - id: api-url - shell: bash - run: | - set -euo pipefail - API_STACK_NAME="prod-my-commit-api" - - API_URL="$(aws cloudformation describe-stacks \ - --stack-name "${API_STACK_NAME}" \ - --region ap-northeast-2 \ - --query 'Stacks[0].Outputs[?OutputKey==`HttpApiUrl`].OutputValue' \ - --output text)" - - if [[ -z "${API_URL}" || "${API_URL}" == "None" ]]; then - echo "Failed to resolve HttpApiUrl from ${API_STACK_NAME}" >&2 - exit 1 - fi - - echo "api_url=${API_URL}" >> "$GITHUB_OUTPUT" - - - name: Smoke test API - shell: bash - run: | - set -euo pipefail - API_URL="${{ steps.api-url.outputs.api_url }}" - WEB_ORIGIN="${{ steps.web-origin.outputs.web_origin }}" - - BODY="$(curl -fsS "${API_URL}/")" - if [[ "${BODY}" != "Hello from Hono!" ]]; then - echo "Unexpected API root response: ${BODY}" >&2 - exit 1 - fi - - PREFLIGHT="$(curl -sS -i -X OPTIONS "${API_URL}/auth/login" \ - -H "Origin: ${WEB_ORIGIN}" \ - -H "Access-Control-Request-Method: POST" \ - -H "Access-Control-Request-Headers: content-type,authorization")" - - echo "${PREFLIGHT}" | grep -i "access-control-allow-origin: ${WEB_ORIGIN}" >/dev/null - - - name: Smoke test Web - shell: bash - run: | - set -euo pipefail - WEB_HEALTHCHECK_URL="${{ steps.web-origin.outputs.web_healthcheck_url }}" - curl -fsS "${WEB_HEALTHCHECK_URL}" >/dev/null diff --git a/.gitignore b/.gitignore index 86bb336..127756a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ out .next # Logs -logs +/logs/ *.log npm-debug.log* yarn-debug.log* diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..bef4f3a --- /dev/null +++ b/Caddyfile @@ -0,0 +1,11 @@ +:80 { + handle_path /api/* { + reverse_proxy api:3000 + } + + handle { + root * /usr/share/caddy + try_files {path} /index.html + file_server + } +} diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..11748f3 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,29 @@ +FROM node:22-alpine AS deps +WORKDIR /app +RUN corepack enable +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY apps/api/package.json apps/api/package.json +COPY apps/api/prisma apps/api/prisma +COPY packages/eslint-config/package.json packages/eslint-config/package.json +RUN pnpm install --frozen-lockfile --filter my-commit-api... + +FROM node:22-alpine AS build +WORKDIR /app +RUN corepack enable +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/apps/api/node_modules ./apps/api/node_modules +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY packages ./packages +COPY apps/api ./apps/api +RUN pnpm --filter my-commit-api build + +FROM node:22-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +RUN corepack enable +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/apps/api/node_modules ./apps/api/node_modules +COPY --from=build /app/apps/api/dist ./apps/api/dist +WORKDIR /app/apps/api +EXPOSE 3000 +CMD ["node", "dist/main.js"] diff --git a/Dockerfile.web b/Dockerfile.web new file mode 100644 index 0000000..b792097 --- /dev/null +++ b/Dockerfile.web @@ -0,0 +1,16 @@ +FROM node:22-alpine AS build +WORKDIR /app +RUN corepack enable +ARG VITE_API_URL=/api +ENV VITE_API_URL=$VITE_API_URL +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/eslint-config/package.json packages/eslint-config/package.json +RUN pnpm install --frozen-lockfile --filter my-commit-client... +COPY packages ./packages +COPY apps/web ./apps/web +RUN pnpm --filter my-commit-client build + +FROM caddy:2-alpine +COPY --from=build /app/apps/web/dist /usr/share/caddy +COPY Caddyfile /etc/caddy/Caddyfile diff --git a/README.md b/README.md index f1ce9ed..b10c126 100644 --- a/README.md +++ b/README.md @@ -1,250 +1,189 @@ -# My Commit ⏰ +# My Commit -시간 관리 및 기록 애플리케이션 +시간 관리 및 기록 애플리케이션입니다. -## 🏗️ 프로젝트 구조 +## 구조 -``` +```text my-commit/ ├── apps/ -│ ├── web/ # React + Vite 프론트엔드 -│ └── api/ # Hono + Lambda 백엔드 +│ ├── web/ # React + Vite frontend +│ └── api/ # NestJS API ├── packages/ │ └── eslint-config/ -└── scripts/ # 배포 및 유틸리티 스크립트 +├── Dockerfile.api +├── Dockerfile.web +├── Caddyfile +└── docker-compose.yml ``` -## 🚀 빠른 시작 (Getting Started) +## Runtime Policy -### 1) 로컬 실행 (권장: API + Web) +- AWS infrastructure dependency 없음. +- API/Web은 Docker 기반으로 실행. +- DB는 Docker Compose에 포함하지 않고 host local MySQL을 사용. +- API는 NestJS + MySQL. +- 로그는 append-only revision + current pointer 모델로 저장. +- stale 저장은 서버에 revision으로 보존하되 current를 덮어쓰지 않음. -```bash -# 1. 환경 변수 파일 -cp .env.example .env.local +관련 문서: -# 2. 의존성 설치 -pnpm install -``` +- [동기화 정책 비교](./docs/SYNC_POLICY_COMPARISON.md) +- [ADR 004](./docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md) -터미널 1: +## 사전 준비 -```bash -# DynamoDB Local 시작 + 테이블 생성 + API 실행 -pnpm --filter my-time-api local:start -``` +- Node.js 22 권장 +- pnpm 9 +- Docker / Docker Compose +- Host local MySQL -터미널 2: +MySQL 예시: -```bash -# Web 실행 -pnpm dev:web +```sql +CREATE DATABASE my_commit CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE USER 'my_commit'@'%' IDENTIFIED BY 'my_commit'; +CREATE USER 'my_commit'@'localhost' IDENTIFIED BY 'my_commit'; +GRANT ALL PRIVILEGES ON my_commit.* TO 'my_commit'@'%'; +GRANT ALL PRIVILEGES ON my_commit.* TO 'my_commit'@'localhost'; +FLUSH PRIVILEGES; ``` -문제 없이 시작되면: - -- Web: http://localhost:5173 -- API: http://localhost:3000 -- DynamoDB Local: http://localhost:8000 - -`pnpm dev`는 전체 앱을 동시에 띄우지만, DynamoDB 테이블은 자동 생성하지 않습니다. -처음 실행하거나 `.dynamodb-data`를 삭제한 경우 아래를 먼저 실행하세요. +## 환경 변수 ```bash -pnpm --filter my-time-api db:start -pnpm --filter my-time-api db:create-tables +cp .env.example .env.local ``` -### 2) 빌드 +주요 값: ```bash -pnpm build -``` - -### 3) 프로덕션 자동 배포 (GitHub Actions) - -1. GitHub Secrets 설정: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `JWT_SECRET` -2. `us-east-1` ACM 인증서 발급 + DNS CNAME 검증 후 `WEB_DOMAIN_NAME`, `ACM_CERTIFICATE_ARN` 설정 -3. `main` 브랜치에 push 하면 자동 배포 +JWT_SECRET=your-fixed-secret +VITE_API_URL=http://localhost:3000 -### 4) 프로덕션 수동 배포 +DATABASE_URL=mysql://my_commit:my_commit@127.0.0.1:3306/my_commit +DOCKER_DATABASE_URL=mysql://my_commit:my_commit@host.docker.internal:3306/my_commit -```bash -# 1) 루트 배포 env 파일 생성/수정 -cp .env.production.example .env.production -# dev면 -# cp .env.development.example .env.development - -# 2) 최초 배포(권장): ACM 생성 + DNS 검증 자동화 + 배포 -pnpm run create:aws:acm:prod:porkbun -# ACM DNS 검증/발급 대기 시간은 보통 약 1분 30초 내외(환경에 따라 더 길 수 있음) -# 출력된 박스의 `Copy line` 값을 `.env.production`의 `ACM_CERTIFICATE_ARN=`에 그대로 반영 -pnpm run deploy:prod:porkbun - -# 3) 후속 배포(일반): 코드 배포만 -pnpm run deploy:prod +WEB_PORT=8080 +ALLOWED_ORIGINS=http://localhost,http://localhost:8080,http://localhost:4000 ``` -배포는 저장소 루트에서만 허용됩니다. +Prisma CLI와 로컬 API는 `DATABASE_URL`을 사용합니다. Docker API는 +`DOCKER_DATABASE_URL`로 host MySQL에 접속합니다. -`deploy`는 루트 `.env` 파일을 자동 로드하고, -`WEB_ORIGIN`/`VITE_API_URL`를 자동 계산해 배포합니다. +## 로컬 개발 -## 📦 배포 - -Full AWS 스택으로 배포됩니다 (S3 + CloudFront + Lambda + DynamoDB) +```bash +pnpm install +pnpm db:migrate +``` -원격 DynamoDB 테이블은 배포 시 `apps/api/serverless.yml`의 `resources` 정의로 자동 생성됩니다. -처음 배포 때 별도 수동 생성은 필요하지 않습니다. +터미널 1: -상세한 배포 가이드는 **[docs/DEPLOYMENT.md](./docs/DEPLOYMENT.md)** 참고 +```bash +pnpm dev:api +``` -기본 운영 도메인: +터미널 2: -- Web: 커스텀 도메인 -- API: API Gateway `execute-api` 도메인 +```bash +pnpm dev:web +``` -### CD (GitHub Actions) +접속: -`main` push 시 `.github/workflows/deploy.yml`로 프로덕션 자동 배포가 실행됩니다. +- Web dev: http://localhost:4000 +- API: http://localhost:3000 -필수 GitHub Secrets: +## Docker 실행 -- `AWS_ACCESS_KEY_ID` -- `AWS_SECRET_ACCESS_KEY` -- `JWT_SECRET` -- `WEB_DOMAIN_NAME` -- `ACM_CERTIFICATE_ARN` +Host MySQL이 먼저 실행 중이어야 합니다. `selfhost:up`은 먼저 Prisma +migration을 적용한 뒤 Docker Compose를 시작합니다. -## 🛠️ 기술 스택 +```bash +pnpm selfhost:up +``` -### 프론트엔드 +접속: -- React 18 -- TypeScript -- Vite -- Redux Toolkit -- TailwindCSS + DaisyUI -- React Router +- Web: http://localhost:8080 +- API proxy: http://localhost:8080/api -### 백엔드 +종료: -- Hono (Web Framework) -- AWS Lambda -- DynamoDB -- JWT 인증 -- Serverless Framework +```bash +pnpm selfhost:down +``` -### 인프라 +로그: -- AWS S3 (정적 호스팅) -- AWS CloudFront (CDN) -- AWS Lambda (서버리스 컴퓨팅) -- AWS DynamoDB (NoSQL 데이터베이스) -- AWS API Gateway (API 관리) +```bash +pnpm selfhost:logs +``` -## 📝 주요 명령어 +## 명령어 ```bash # 개발 -pnpm dev # 전체 개발 서버 -pnpm dev:web # 프론트엔드 개발 서버 -pnpm dev:api # 백엔드 개발 서버 +pnpm dev +pnpm dev:web +pnpm dev:api + +# DB +pnpm db:migrate # 빌드 -pnpm build # 전체 빌드 -pnpm build:web # 프론트엔드 빌드 -pnpm build:api # 백엔드 빌드 +pnpm build +pnpm build:web +pnpm build:api # 테스트 -pnpm test # 전체 테스트 -pnpm test:web # 프론트엔드 테스트 +pnpm test +pnpm test:web +pnpm --filter my-commit-api test # 린트 -pnpm lint # 전체 린트 -pnpm lint:fix # 린트 자동 수정 - -# 배포 -pnpm run deploy:prod # 전체 배포 (prod) -pnpm run deploy:dev # 전체 배포 (dev) -pnpm run deploy:prod:porkbun # 배포 후 DNS 자동 동기화 (Porkbun) -pnpm run dns:sync:porkbun -- prod # DNS만 재동기화(CloudFront 주소 자동 조회) -pnpm run create:aws:acm:prod:porkbun # ACM 생성 + DNS 검증 레코드 자동 반영 -pnpm run undeploy:prod # prod 스택 + ACM 제거 (기본) -# ACM 유지 시: pnpm run undeploy -- prod --keep-acm -``` - -## 🔐 환경 변수 +pnpm lint +pnpm lint:fix -### 배포용 (루트 `.env.production`) - -```bash -JWT_SECRET=your-fixed-secret -WEB_DOMAIN_NAME=my-commit.com -ACM_CERTIFICATE_ARN=arn:aws:acm:us-east-1:ACCOUNT_ID:certificate/xxxx - -# Optional: DNS provider integration (after web deploy) -# AWS_PROFILE=my-aws-profile -# UPDATE_DNS_RECORD=true -# DNS_PROVIDER=porkbun -# PORKBUN_API_KEY=pk1_xxxxxxxxxxxxx -# PORKBUN_SECRET_API_KEY=sk1_xxxxxxxxxxxxx -# PORKBUN_ROOT_DOMAIN=my-commit.com -# PORKBUN_RECORD_TYPE=ALIAS -# PORKBUN_TTL=600 -# PORKBUN_DRY_RUN=false +# self-host +pnpm selfhost:up +pnpm selfhost:down +pnpm selfhost:logs ``` -Porkbun 연동 시 주의: -- 대상 도메인(`my-commit.com`)의 `API Access`를 Porkbun 콘솔에서 먼저 활성화해야 합니다. - -`dev` 배포 시에는 `.env.development`가 동일한 방식으로 사용됩니다. +## API -### 로컬 공통 (루트 `.env.local`) +인증: -```bash -JWT_SECRET=your-fixed-secret -VITE_API_URL=http://localhost:3000 +```text +POST /auth/signup +POST /auth/login ``` -앱 하위(`apps/web`, `apps/api`)에는 별도 `.env` 파일을 두지 않습니다. -로컬/배포 모두 루트 `.env*` 파일만 사용합니다. - -프로덕션 배포에서는 `VITE_API_URL`을 수동 입력하지 않습니다. -배포 시 API Gateway endpoint를 자동 조회해 주입합니다. -자세한 내용은 `docs/DEPLOYMENT.md` 참고 - -## 🧯 로컬 로그인 트러블슈팅 - -- 에러: `ResourceNotFoundException: Cannot do operations on a non-existent table` - - 원인: 로컬 DynamoDB 테이블 미생성 - - 해결: +설정: -```bash -pnpm --filter my-time-api db:create-tables +```text +GET /user-settings +PUT /user-settings ``` -- 에러: `ECONNREFUSED ::1:8000` 또는 `ECONNREFUSED 127.0.0.1:8000` - - 원인: DynamoDB Local 컨테이너 미실행 - - 해결: +로그: -```bash -pnpm --filter my-time-api db:start +```text +GET /raw-logs +POST /raw-logs +POST /raw-logs/bulk +GET /raw-logs/:date +GET /raw-logs/:date/backups ``` -## 💰 AWS 프리티어 - -모든 서비스가 AWS 프리티어로 운영 가능합니다: - -- S3: 5GB 저장 + 20,000 GET 요청 -- CloudFront: 1TB 전송 + 10M 요청 -- Lambda: 100만 요청 -- DynamoDB: 25GB 저장 -- API Gateway: 100만 요청 +## 백업 -## 📄 라이선스 +운영 백업은 host MySQL 기준으로 수행합니다. -MIT - -## 🤝 기여 +```bash +mysqldump -u my_commit -p my_commit > my_commit_backup.sql +``` -이슈와 PR은 언제나 환영합니다! +앱의 JSON export는 사용자 단위 current data 이동용으로 유지합니다. 전체 버전 보존의 source of truth는 MySQL `log_revisions`입니다. diff --git a/apps/api/docker-compose.yml b/apps/api/docker-compose.yml deleted file mode 100644 index 0eaac7f..0000000 --- a/apps/api/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -services: - dynamodb-local: - image: amazon/dynamodb-local:latest - container_name: my-commit-dynamodb-local - ports: - - "8000:8000" - command: - [ - "-jar", - "DynamoDBLocal.jar", - "-sharedDb", - "-dbPath", - "/home/dynamodblocal/data", - ] - volumes: - - ./.dynamodb-data:/home/dynamodblocal/data - healthcheck: - test: ["CMD-SHELL", "curl -s http://localhost:8000 || exit 1"] - interval: 10s - timeout: 5s - retries: 3 - - dynamodb-admin: - image: aaronshaf/dynamodb-admin:latest - container_name: my-commit-dynamodb-admin - ports: - - "8001:8001" - environment: - - DYNAMO_ENDPOINT=http://dynamodb-local:8000 - - AWS_REGION=ap-northeast-2 - - AWS_ACCESS_KEY_ID=local - - AWS_SECRET_ACCESS_KEY=local - depends_on: - - dynamodb-local diff --git a/apps/api/eslint.config.js b/apps/api/eslint.config.mjs similarity index 91% rename from apps/api/eslint.config.js rename to apps/api/eslint.config.mjs index 340a824..2e9b720 100644 --- a/apps/api/eslint.config.js +++ b/apps/api/eslint.config.mjs @@ -7,11 +7,11 @@ export default [ ignores: [ "dist/**", "node_modules/**", - ".serverless/**", "coverage/**", - "eslint.config.js", + "eslint.config.mjs", "tsup.config.ts", "scripts/**", + "**/*.test.ts", "jest.config.js", ], }, diff --git a/apps/api/package.json b/apps/api/package.json index 250945e..28d1016 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -1,43 +1,37 @@ { "name": "my-commit-api", "version": "0.0.1", - "type": "module", + "type": "commonjs", "private": true, "scripts": { - "dev": "sh scripts/with-root-env.sh sh -c 'IS_OFFLINE=true SLS_STAGE=local tsx watch src/index.ts'", - "dev:local": "sh scripts/with-root-env.sh sh -c 'IS_OFFLINE=true SLS_STAGE=local tsx watch src/index.ts'", - "build": "tsup --config tsup.config.ts", + "dev": "tsx watch src/main.ts", + "dev:local": "tsx watch src/main.ts", + "build": "pnpm prisma:generate && tsc -p tsconfig.json", + "start": "node dist/main.js", + "test": "vitest run", "lint": "eslint .", "lint:fix": "eslint . --fix", - "db:start": "docker-compose up -d", - "db:stop": "docker-compose down", - "db:create-tables": "tsx scripts/create-local-tables.ts", - "db:delete-tables": "tsx scripts/delete-local-tables.ts", - "db:reset": "pnpm db:delete-tables && pnpm db:create-tables", - "local:start": "pnpm db:start && sleep 2 && pnpm db:create-tables && pnpm dev:local", - "sls:offline": "sh scripts/with-root-env.sh sh -c 'pnpm build && IS_OFFLINE=true SLS_STAGE=local serverless offline --stage local'", - "sls:deploy": "if [ \"${MY_COMMIT_ROOT_DEPLOY:-}\" != \"1\" ]; then echo \"Use repository-root deploy command: pnpm run deploy:prod or pnpm run deploy:dev\"; exit 1; fi; pnpm build && serverless deploy", - "sls:remove": "serverless remove" + "prisma:generate": "prisma generate", + "db:migrate": "prisma migrate deploy", + "db:migrate:dev": "prisma migrate dev", + "local:start": "pnpm db:migrate && pnpm dev:local" }, "dependencies": { - "@aws-sdk/client-dynamodb": "^3.500.0", - "@aws-sdk/lib-dynamodb": "^3.500.0", - "@hono/node-server": "^1.8.0", - "aws-lambda": "^1.0.7", - "hono": "^4.0.0", - "jose": "^6.1.3" + "@nestjs/common": "^10.4.0", + "@nestjs/core": "^10.4.0", + "@nestjs/platform-express": "^10.4.0", + "@prisma/client": "6", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1" }, "devDependencies": { "@my-commit/eslint-config": "workspace:*", - "@types/aws-lambda": "^8.10.133", "@types/node": "^20.11.17", "eslint": "^9.0.0", "globals": "^15.0.0", - "serverless": "^3.38.0", - "serverless-dynamodb": "^0.2.54", - "serverless-offline": "^13.3.3", - "tsup": "^8.0.2", + "prisma": "6", "tsx": "^4.7.1", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "vitest": "^0.31.4" } } diff --git a/apps/api/prisma/migrations/20260712000000_init/migration.sql b/apps/api/prisma/migrations/20260712000000_init/migration.sql new file mode 100644 index 0000000..15cd3fa --- /dev/null +++ b/apps/api/prisma/migrations/20260712000000_init/migration.sql @@ -0,0 +1,62 @@ +CREATE TABLE `users` ( + `username` VARCHAR(191) NOT NULL, + `password_hash` VARCHAR(255) NOT NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + PRIMARY KEY (`username`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE `user_settings` ( + `username` VARCHAR(191) NOT NULL, + `app_theme` VARCHAR(191) NULL, + `app_theme_by_scheme` TEXT NULL, + `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + PRIMARY KEY (`username`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE `log_revisions` ( + `revision_id` CHAR(36) NOT NULL, + `user_id` VARCHAR(191) NOT NULL, + `log_date` CHAR(10) NOT NULL, + `content` LONGTEXT NOT NULL, + `content_hash` VARCHAR(128) NOT NULL, + `base_revision_id` CHAR(36) NULL, + `base_content_hash` VARCHAR(128) NULL, + `source` ENUM('edit', 'restore', 'import') NOT NULL DEFAULT 'edit', + `promoted` BOOLEAN NOT NULL DEFAULT false, + `client_mutation_id` VARCHAR(191) NULL, + `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + INDEX `idx_log_revisions_user_date_created` (`user_id`, `log_date`, `created_at`), + INDEX `idx_log_revisions_user_date_promoted` (`user_id`, `log_date`, `promoted`, `created_at`), + INDEX `idx_log_revisions_client_mutation` (`user_id`, `client_mutation_id`), + PRIMARY KEY (`revision_id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +CREATE TABLE `current_logs` ( + `user_id` VARCHAR(191) NOT NULL, + `log_date` CHAR(10) NOT NULL, + `current_revision_id` CHAR(36) NOT NULL, + `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + INDEX `idx_current_logs_revision` (`current_revision_id`), + PRIMARY KEY (`user_id`, `log_date`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE `user_settings` + ADD CONSTRAINT `fk_user_settings_user` + FOREIGN KEY (`username`) REFERENCES `users`(`username`) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE `log_revisions` + ADD CONSTRAINT `fk_log_revisions_user` + FOREIGN KEY (`user_id`) REFERENCES `users`(`username`) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE `current_logs` + ADD CONSTRAINT `fk_current_logs_user` + FOREIGN KEY (`user_id`) REFERENCES `users`(`username`) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE `current_logs` + ADD CONSTRAINT `fk_current_logs_revision` + FOREIGN KEY (`current_revision_id`) REFERENCES `log_revisions`(`revision_id`) + ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/apps/api/prisma/migrations/migration_lock.toml b/apps/api/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..7dec767 --- /dev/null +++ b/apps/api/prisma/migrations/migration_lock.toml @@ -0,0 +1 @@ +provider = "mysql" diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma new file mode 100644 index 0000000..11132d1 --- /dev/null +++ b/apps/api/prisma/schema.prisma @@ -0,0 +1,70 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") +} + +model User { + username String @id @db.VarChar(191) + passwordHash String @map("password_hash") @db.VarChar(255) + createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) + updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) + settings UserSettings? + revisions LogRevision[] + currentLogs CurrentLog[] + + @@map("users") +} + +model UserSettings { + username String @id @db.VarChar(191) + appTheme String? @map("app_theme") @db.VarChar(191) + appThemeByScheme String? @map("app_theme_by_scheme") @db.Text + updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) + user User @relation(fields: [username], references: [username], onDelete: Cascade) + + @@map("user_settings") +} + +model LogRevision { + revisionId String @id @default(uuid()) @map("revision_id") @db.Char(36) + userId String @map("user_id") @db.VarChar(191) + logDate String @map("log_date") @db.Char(10) + content String @db.LongText + contentHash String @map("content_hash") @db.VarChar(128) + baseRevisionId String? @map("base_revision_id") @db.Char(36) + baseContentHash String? @map("base_content_hash") @db.VarChar(128) + source RevisionSource @default(edit) + promoted Boolean @default(false) + clientMutationId String? @map("client_mutation_id") @db.VarChar(191) + createdAt DateTime @default(now()) @map("created_at") @db.DateTime(3) + user User @relation(fields: [userId], references: [username], onDelete: Cascade) + currentLogs CurrentLog[] + + @@index([userId, logDate, createdAt], map: "idx_log_revisions_user_date_created") + @@index([userId, logDate, promoted, createdAt], map: "idx_log_revisions_user_date_promoted") + @@index([userId, clientMutationId], map: "idx_log_revisions_client_mutation") + @@map("log_revisions") +} + +model CurrentLog { + userId String @map("user_id") @db.VarChar(191) + logDate String @map("log_date") @db.Char(10) + currentRevisionId String @map("current_revision_id") @db.Char(36) + updatedAt DateTime @updatedAt @map("updated_at") @db.DateTime(3) + user User @relation(fields: [userId], references: [username], onDelete: Cascade) + revision LogRevision @relation(fields: [currentRevisionId], references: [revisionId], onDelete: Restrict) + + @@id([userId, logDate]) + @@index([currentRevisionId], map: "idx_current_logs_revision") + @@map("current_logs") +} + +enum RevisionSource { + edit + restore + import +} diff --git a/apps/api/scripts/create-local-tables.ts b/apps/api/scripts/create-local-tables.ts deleted file mode 100644 index 17cf7e6..0000000 --- a/apps/api/scripts/create-local-tables.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * 로컬 DynamoDB 테이블 생성 스크립트 - * Usage: npx tsx scripts/create-local-tables.ts - */ - -import { - CreateTableCommand, - DynamoDBClient, - ListTablesCommand, -} from "@aws-sdk/client-dynamodb"; - -const STAGE = process.env.SLS_STAGE || "local"; -const MAX_RETRIES = 10; -const RETRY_DELAY = 2000; // 2초 - -const client = new DynamoDBClient({ - region: "ap-northeast-2", - endpoint: "http://localhost:8000", - credentials: { - accessKeyId: "local", - secretAccessKey: "local", - }, - requestHandler: { - connectionTimeout: 3000, - requestTimeout: 5000, - }, -}); - -const tables = [ - { - TableName: `${STAGE}-my-commit-users`, - KeySchema: [{ AttributeName: "username", KeyType: "HASH" as const }], - AttributeDefinitions: [ - { AttributeName: "username", AttributeType: "S" as const }, - ], - BillingMode: "PAY_PER_REQUEST" as const, - }, - { - TableName: `${STAGE}-my-commit-logs`, - KeySchema: [ - { AttributeName: "userId", KeyType: "HASH" as const }, - { AttributeName: "date", KeyType: "RANGE" as const }, - ], - AttributeDefinitions: [ - { AttributeName: "userId", AttributeType: "S" as const }, - { AttributeName: "date", AttributeType: "S" as const }, - ], - BillingMode: "PAY_PER_REQUEST" as const, - }, - { - TableName: `${STAGE}-my-commit-log-backups`, - KeySchema: [ - { AttributeName: "userId", KeyType: "HASH" as const }, - { AttributeName: "backupId", KeyType: "RANGE" as const }, - ], - AttributeDefinitions: [ - { AttributeName: "userId", AttributeType: "S" as const }, - { AttributeName: "backupId", AttributeType: "S" as const }, - ], - BillingMode: "PAY_PER_REQUEST" as const, - }, -]; - -async function waitForDynamoDB() { - console.log("⏳ DynamoDB Local 연결 대기 중..."); - - for (let i = 0; i < MAX_RETRIES; i++) { - try { - await client.send(new ListTablesCommand({})); - console.log("✅ DynamoDB Local 연결 성공!"); - return; - } catch (error) { - if (i === MAX_RETRIES - 1) { - throw new Error( - `DynamoDB Local에 연결할 수 없습니다. Docker 컨테이너가 실행 중인지 확인하세요.\n` + - `명령어: docker ps | grep dynamodb` - ); - } - console.log(`⏳ 재시도 중... (${i + 1}/${MAX_RETRIES})`); - await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY)); - } - } -} - -async function createTables() { - await waitForDynamoDB(); - - console.log("🔍 기존 테이블 확인 중..."); - - const { TableNames = [] } = await client.send(new ListTablesCommand({})); - - for (const table of tables) { - if (TableNames.includes(table.TableName)) { - console.log(`⏭️ 테이블 "${table.TableName}" 이미 존재`); - continue; - } - - console.log(`📦 테이블 "${table.TableName}" 생성 중...`); - try { - await client.send(new CreateTableCommand(table)); - console.log(`✅ 테이블 "${table.TableName}" 생성 완료`); - } catch (error) { - console.error(`❌ 테이블 "${table.TableName}" 생성 실패:`, error); - } - } - - console.log("\n🎉 로컬 DynamoDB 테이블 설정 완료!"); - console.log("📊 DynamoDB Admin UI: http://localhost:8001"); -} - -createTables().catch(console.error); diff --git a/apps/api/scripts/delete-local-tables.ts b/apps/api/scripts/delete-local-tables.ts deleted file mode 100644 index f0908b3..0000000 --- a/apps/api/scripts/delete-local-tables.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * 로컬 DynamoDB 테이블 삭제 스크립트 - * Usage: npx tsx scripts/delete-local-tables.ts - */ - -import { DeleteTableCommand, DynamoDBClient, ListTablesCommand } from '@aws-sdk/client-dynamodb'; - -const STAGE = process.env.SLS_STAGE || 'local'; - -const client = new DynamoDBClient({ - region: 'ap-northeast-2', - endpoint: 'http://localhost:8000', - credentials: { - accessKeyId: 'local', - secretAccessKey: 'local', - }, -}); - -const tableNames = [`${STAGE}-my-commit-users`, `${STAGE}-my-commit-logs`]; - -async function deleteTables() { - console.log('🔍 기존 테이블 확인 중...'); - - const { TableNames = [] } = await client.send(new ListTablesCommand({})); - - for (const tableName of tableNames) { - if (!TableNames.includes(tableName)) { - console.log(`⏭️ 테이블 "${tableName}" 없음 - 건너뜀`); - continue; - } - - console.log(`🗑️ 테이블 "${tableName}" 삭제 중...`); - try { - await client.send(new DeleteTableCommand({ TableName: tableName })); - console.log(`✅ 테이블 "${tableName}" 삭제 완료`); - } catch (error) { - console.error(`❌ 테이블 "${tableName}" 삭제 실패:`, error); - } - } - - console.log('\n🎉 로컬 DynamoDB 테이블 삭제 완료!'); -} - -deleteTables().catch(console.error); diff --git a/apps/api/scripts/with-root-env.sh b/apps/api/scripts/with-root-env.sh deleted file mode 100644 index 34c4af6..0000000 --- a/apps/api/scripts/with-root-env.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env sh -set -eu - -SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" -ROOT_DIR="$(CDPATH= cd -- "${SCRIPT_DIR}/../../.." && pwd)" - -load_env_file() { - file="$1" - if [ -f "$file" ]; then - set -a - # shellcheck disable=SC1090 - . "$file" - set +a - fi -} - -# Later files override earlier values. -load_env_file "${ROOT_DIR}/.env" -load_env_file "${ROOT_DIR}/.env.local" -load_env_file "${ROOT_DIR}/.env.development" -load_env_file "${ROOT_DIR}/.env.development.local" - -exec "$@" diff --git a/apps/api/serverless.yml b/apps/api/serverless.yml deleted file mode 100644 index 85bfa59..0000000 --- a/apps/api/serverless.yml +++ /dev/null @@ -1,161 +0,0 @@ -service: my-commit-api - -frameworkVersion: "3" -useDotenv: true - -provider: - name: aws - runtime: nodejs18.x - region: ${opt:region, 'ap-northeast-2'} - stage: ${opt:stage, 'dev'} - stackName: ${self:provider.stage}-${self:service} - memorySize: 256 - timeout: 10 - - httpApi: - cors: - allowedOrigins: - - ${self:custom.webOrigin.${self:provider.stage}, self:custom.webOrigin.dev} - - http://localhost:3000 - - https://localhost:3000 - - http://localhost:5173 - - https://localhost:5173 - - http://localhost:5174 - - https://localhost:5174 - - http://localhost:6006 - - https://localhost:6006 - - http://localhost:4000 - - https://localhost:4000 - allowedHeaders: - - Content-Type - - Authorization - allowedMethods: - - GET - - POST - - PUT - - DELETE - - OPTIONS - allowCredentials: false - maxAge: 86400 - - environment: - SLS_STAGE: ${self:provider.stage} - JWT_SECRET: ${env:JWT_SECRET} - ALLOWED_ORIGINS: ${self:custom.webOrigin.${self:provider.stage}, self:custom.webOrigin.dev},http://localhost:3000,https://localhost:3000,http://localhost:5173,https://localhost:5173,http://localhost:5174,https://localhost:5174,http://localhost:6006,https://localhost:6006,http://localhost:4000,https://localhost:4000 - IS_OFFLINE: ${self:custom.isOffline.${self:provider.stage}, 'false'} - - iam: - role: - statements: - - Effect: Allow - Action: - - dynamodb:Query - - dynamodb:Scan - - dynamodb:GetItem - - dynamodb:PutItem - - dynamodb:UpdateItem - - dynamodb:DeleteItem - Resource: - - !GetAtt UsersTable.Arn - - !GetAtt LogsTable.Arn - - !GetAtt LogBackupsTable.Arn - -custom: - usersTableName: ${self:provider.stage}-my-commit-users - logsTableName: ${self:provider.stage}-my-commit-logs - logBackupsTableName: ${self:provider.stage}-my-commit-log-backups - webOrigin: - local: http://localhost:5173 - dev: ${env:WEB_ORIGIN} - prod: ${env:WEB_ORIGIN} - - isOffline: - local: "true" - dev: "false" - prod: "false" - - serverless-offline: - httpPort: 3000 - noPrependStageInUrl: true - reloadHandler: true - - dynamodb: - stages: - - local - start: - port: 8000 - inMemory: true - migrate: true - seed: true - seed: - domain: - sources: - - table: ${self:custom.usersTableName} - sources: [./seed/users.json] - - table: ${self:custom.logsTableName} - sources: [./seed/logs.json] - -functions: - api: - handler: dist/index.handler - events: - - httpApi: "*" - -plugins: - - serverless-dynamodb - - serverless-offline - -resources: - Resources: - UsersTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: ${self:custom.usersTableName} - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: username - AttributeType: S - KeySchema: - - AttributeName: username - KeyType: HASH - Tags: - - Key: Environment - Value: ${self:provider.stage} - - LogsTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: ${self:custom.logsTableName} - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: userId - AttributeType: S - - AttributeName: date - AttributeType: S - KeySchema: - - AttributeName: userId - KeyType: HASH - - AttributeName: date - KeyType: RANGE - Tags: - - Key: Environment - Value: ${self:provider.stage} - - LogBackupsTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: ${self:custom.logBackupsTableName} - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: userId - AttributeType: S - - AttributeName: backupId - AttributeType: S - KeySchema: - - AttributeName: userId - KeyType: HASH - - AttributeName: backupId - KeyType: RANGE - Tags: - - Key: Environment - Value: ${self:provider.stage} diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts new file mode 100644 index 0000000..e7e41f9 --- /dev/null +++ b/apps/api/src/app.module.ts @@ -0,0 +1,31 @@ +import { Module } from "@nestjs/common"; + +import { AuthController } from "./auth/auth.controller"; +import { AuthGuard } from "./auth/auth.guard"; +import { AuthService } from "./auth/auth.service"; +import { PrismaService } from "./db/prisma.service"; +import { LogsController } from "./logs/logs.controller"; +import { LOGS_REPOSITORY, PrismaLogsRepository } from "./logs/logs.repository"; +import { LogsService } from "./logs/logs.service"; +import { SettingsController } from "./settings/settings.controller"; +import { SettingsRepository } from "./settings/settings.repository"; +import { SettingsService } from "./settings/settings.service"; +import { UsersRepository } from "./users/users.repository"; + +@Module({ + controllers: [AuthController, SettingsController, LogsController], + providers: [ + PrismaService, + UsersRepository, + AuthService, + AuthGuard, + SettingsRepository, + SettingsService, + LogsService, + { + provide: LOGS_REPOSITORY, + useClass: PrismaLogsRepository, + }, + ], +}) +export class AppModule {} diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts new file mode 100644 index 0000000..d3fb71b --- /dev/null +++ b/apps/api/src/auth/auth.controller.ts @@ -0,0 +1,23 @@ +import { Body, Controller, Post } from "@nestjs/common"; + +import { AuthService } from "./auth.service"; + +type CredentialsBody = { + username?: string; + password?: string; +}; + +@Controller("auth") +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Post("signup") + signup(@Body() body: CredentialsBody) { + return this.authService.signup(body.username ?? "", body.password ?? ""); + } + + @Post("login") + login(@Body() body: CredentialsBody) { + return this.authService.login(body.username ?? "", body.password ?? ""); + } +} diff --git a/apps/api/src/auth/auth.guard.ts b/apps/api/src/auth/auth.guard.ts new file mode 100644 index 0000000..9dc46f5 --- /dev/null +++ b/apps/api/src/auth/auth.guard.ts @@ -0,0 +1,40 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; + +import { AuthService, JwtUserPayload } from "./auth.service"; + +export type AuthenticatedRequest = { + headers: { + authorization?: string; + }; + user?: JwtUserPayload; +}; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor(private readonly authService: AuthService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractBearerToken(request.headers.authorization); + request.user = await this.authService.verifyToken(token); + return true; + } + + private extractBearerToken(value: string | undefined): string { + if (!value) { + throw new UnauthorizedException("Unauthorized"); + } + + const [type, token] = value.split(" "); + if (type !== "Bearer" || !token) { + throw new UnauthorizedException("Unauthorized"); + } + + return token; + } +} diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts new file mode 100644 index 0000000..dc5e6dc --- /dev/null +++ b/apps/api/src/auth/auth.service.ts @@ -0,0 +1,177 @@ +import { + createHmac, + randomBytes, + scryptSync, + timingSafeEqual, +} from "node:crypto"; + +import { + BadRequestException, + ConflictException, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; + +import { UsersRepository } from "../users/users.repository"; + +export interface JwtUserPayload { + username: string; + sub: string; +} + +interface SignedJwtPayload extends JwtUserPayload { + iat: number; + exp: number; +} + +@Injectable() +export class AuthService { + private readonly jwtSecret: string; + + constructor(private readonly usersRepository: UsersRepository) { + const secret = process.env.JWT_SECRET?.trim(); + if (!secret) { + throw new Error("JWT_SECRET must be set and fixed across deployments"); + } + this.jwtSecret = secret; + } + + async signup(username: string, password: string) { + this.assertCredentials(username, password); + + const existing = await this.usersRepository.findUser(username); + if (existing) { + throw new ConflictException("Username already exists"); + } + + await this.usersRepository.createUser( + username, + this.hashPassword(password) + ); + return this.createLoginResponse(username); + } + + async login(username: string, password: string) { + this.assertCredentials(username, password); + + const user = await this.usersRepository.findUser(username); + if (!user || !this.verifyPassword(password, user.passwordHash)) { + throw new UnauthorizedException("Invalid credentials"); + } + + return this.createLoginResponse(username); + } + + async verifyToken(token: string): Promise { + const payload = this.verifyJwt(token); + const username = payload.username || payload.sub; + if (typeof username !== "string" || username.length === 0) { + throw new UnauthorizedException("Invalid token"); + } + + return { + username, + sub: username, + }; + } + + private async createLoginResponse(username: string) { + const now = Math.floor(Date.now() / 1000); + const accessToken = this.signJwt({ + username, + sub: username, + iat: now, + exp: now + 60 * 60, + }); + + return { access_token: accessToken }; + } + + private signJwt(payload: SignedJwtPayload): string { + const header = this.encodeJson({ alg: "HS256", typ: "JWT" }); + const body = this.encodeJson(payload); + const signature = this.sign(`${header}.${body}`); + + return `${header}.${body}.${signature}`; + } + + private verifyJwt(token: string): SignedJwtPayload { + const [headerPart, payloadPart, signaturePart] = token.split("."); + if (!headerPart || !payloadPart || !signaturePart) { + throw new UnauthorizedException("Invalid token"); + } + + const expectedSignature = this.sign(`${headerPart}.${payloadPart}`); + if (!this.safeEqual(signaturePart, expectedSignature)) { + throw new UnauthorizedException("Invalid token"); + } + + const header = this.decodeJson(headerPart); + if (header.alg !== "HS256") { + throw new UnauthorizedException("Invalid token"); + } + + const payload = this.decodeJson(payloadPart) as Partial; + if ( + typeof payload.username !== "string" || + typeof payload.sub !== "string" || + typeof payload.exp !== "number" + ) { + throw new UnauthorizedException("Invalid token"); + } + + if (payload.exp <= Math.floor(Date.now() / 1000)) { + throw new UnauthorizedException("Token expired"); + } + + return payload as SignedJwtPayload; + } + + private encodeJson(value: object): string { + return Buffer.from(JSON.stringify(value)).toString("base64url"); + } + + private decodeJson(value: string): Record { + try { + return JSON.parse(Buffer.from(value, "base64url").toString("utf8")); + } catch { + throw new UnauthorizedException("Invalid token"); + } + } + + private sign(value: string): string { + return createHmac("sha256", this.jwtSecret) + .update(value) + .digest("base64url"); + } + + private safeEqual(left: string, right: string): boolean { + const leftBuffer = Buffer.from(left); + const rightBuffer = Buffer.from(right); + if (leftBuffer.length !== rightBuffer.length) { + return false; + } + + return timingSafeEqual(leftBuffer, rightBuffer); + } + + private assertCredentials(username: string, password: string): void { + if (!username || !password) { + throw new BadRequestException("Username and password are required"); + } + } + + private hashPassword(password: string): string { + const salt = randomBytes(16).toString("hex"); + const passwordHash = scryptSync(password, salt, 64).toString("hex"); + return `${salt}:${passwordHash}`; + } + + private verifyPassword(password: string, storedHash: string): boolean { + const [salt, expectedHash] = storedHash.split(":"); + if (!salt || !expectedHash) return false; + const expected = Buffer.from(expectedHash, "hex"); + const actual = scryptSync(password, salt, expected.length); + return timingSafeEqual(expected, actual); + } +} diff --git a/apps/api/src/db.ts b/apps/api/src/db.ts deleted file mode 100644 index f81a0ae..0000000 --- a/apps/api/src/db.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; - -let docClient: DynamoDBDocumentClient | null = null; - -// 로컬 DynamoDB 엔드포인트 결정 -const getLocalEndpoint = () => { - // DynamoDB Local 사용 시 - if (process.env.IS_OFFLINE === "true") { - return "http://localhost:8000"; - } - return undefined; -}; - -export const getDynamoDb = (): DynamoDBDocumentClient => { - if (docClient) return docClient; - - const endpoint = getLocalEndpoint(); - - const client = new DynamoDBClient({ - region: process.env.AWS_REGION || "ap-northeast-2", - ...(endpoint && { - endpoint, - credentials: { - accessKeyId: "local", - secretAccessKey: "local", - }, - }), - }); - - docClient = DynamoDBDocumentClient.from(client); - return docClient; -}; - -const stage = process.env.SLS_STAGE || "local"; - -export const TABLE_NAME = `${stage}-my-commit-users`; -export const LOGS_TABLE_NAME = `${stage}-my-commit-logs`; -export const LOG_BACKUPS_TABLE_NAME = `${stage}-my-commit-log-backups`; diff --git a/apps/api/src/db/prisma.service.ts b/apps/api/src/db/prisma.service.ts new file mode 100644 index 0000000..d43730f --- /dev/null +++ b/apps/api/src/db/prisma.service.ts @@ -0,0 +1,16 @@ +import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common"; +import { PrismaClient } from "@prisma/client"; + +@Injectable() +export class PrismaService + extends PrismaClient + implements OnModuleInit, OnModuleDestroy +{ + async onModuleInit(): Promise { + await this.$connect(); + } + + async onModuleDestroy(): Promise { + await this.$disconnect(); + } +} diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts deleted file mode 100644 index 30573e8..0000000 --- a/apps/api/src/index.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto"; -import { fileURLToPath } from "node:url"; - -import { serve } from "@hono/node-server"; -import { type Context, Hono } from "hono"; -import { handle } from "hono/aws-lambda"; -import { cors } from "hono/cors"; -import { jwt } from "hono/jwt"; -import { logger } from "hono/logger"; -import { SignJWT } from "jose"; - -import { - bulkSaveLogs, - getAllLogs, - getLog, - getLogBackups, - saveLog, -} from "./logs"; -import { - createUser, - findUser, - getUserSettings, - saveUserSettings, -} from "./users"; - -const app = new Hono(); -const allowedOrigins = ( - process.env.ALLOWED_ORIGINS ?? - [ - "http://localhost:3000", - "https://localhost:3000", - "http://localhost:5173", - "https://localhost:5173", - "http://localhost:5174", - "https://localhost:5174", - "http://localhost:6006", - "https://localhost:6006", - "http://localhost:4000", - "https://localhost:4000", - ].join(",") -) - .split(",") - .map((origin) => origin.trim()) - .filter(Boolean); - -// Middleware -app.use("*", logger()); -app.use( - "*", - cors({ - origin: (origin) => { - if (!origin) return undefined; - return allowedOrigins.includes(origin) ? origin : undefined; - }, - credentials: false, - }) -); - -const JWT_SECRET = process.env.JWT_SECRET?.trim(); -if (!JWT_SECRET) { - throw new Error("JWT_SECRET must be set and fixed across deployments"); -} -const jwtSigningKey = new TextEncoder().encode(JWT_SECRET); - -const createAccessToken = async (username: string) => { - return new SignJWT({ username, sub: username }) - .setProtectedHeader({ alg: "HS256" }) - .setIssuedAt() - .setExpirationTime("1h") - .sign(jwtSigningKey); -}; - -const hashPassword = (password: string) => { - const salt = randomBytes(16).toString("hex"); - const passwordHash = scryptSync(password, salt, 64).toString("hex"); - return `${salt}:${passwordHash}`; -}; - -const verifyPassword = (password: string, storedHash: string) => { - const [salt, expectedHash] = storedHash.split(":"); - if (!salt || !expectedHash) return false; - const expected = Buffer.from(expectedHash, "hex"); - const actual = scryptSync(password, salt, expected.length); - return timingSafeEqual(expected, actual); -}; - -type LambdaEnvLike = { - event?: { - requestContext?: { - requestId?: string; - }; - }; - requestContext?: { - requestId?: string; - }; - lambdaContext?: { - awsRequestId?: string; - }; -}; - -const getRequestId = (c: Context): string | undefined => { - const env = c.env as LambdaEnvLike | undefined; - return ( - env?.requestContext?.requestId || - env?.event?.requestContext?.requestId || - env?.lambdaContext?.awsRequestId - ); -}; - -const getErrorCode = (error: unknown): string => { - if ( - typeof error === "object" && - error && - "name" in error && - typeof error.name === "string" && - error.name - ) { - return error.name; - } - return "UnknownError"; -}; - -const logRouteError = (c: Context, message: string, error: unknown) => { - const requestId = getRequestId(c); - if (requestId) { - console.error(`${message} [requestId=${requestId}]`, error); - return; - } - console.error(message, error); -}; - -const internalServerError = (c: Context, error: unknown) => { - const requestId = getRequestId(c); - if (requestId) { - c.header("x-request-id", requestId); - } - - return c.json( - { - message: "Internal Server Error", - code: getErrorCode(error), - ...(requestId ? { requestId } : {}), - }, - 500 - ); -}; - -// Protected Routes Middleware -// Hono route pattern "/raw-logs/*" does not include "/raw-logs" itself. -app.use("/raw-logs", jwt({ secret: JWT_SECRET })); -app.use("/raw-logs/*", jwt({ secret: JWT_SECRET })); -app.use("/user-settings", jwt({ secret: JWT_SECRET })); -app.use("/user-settings/*", jwt({ secret: JWT_SECRET })); - -// Routes -app.get("/", (c) => c.text("Hello from Hono!")); - -app.post("/auth/signup", async (c) => { - try { - const { username, password } = await c.req.json(); - - if (!username || !password) { - return c.json({ message: "Username and password are required" }, 400); - } - - const existingUser = await findUser(username); - if (existingUser) { - return c.json({ message: "Username already exists" }, 409); - } - - const passwordHash = hashPassword(password); - - await createUser(username, passwordHash); - - const token = await createAccessToken(username); - - return c.json({ access_token: token }); - } catch (error) { - logRouteError(c, "Signup error:", error); - return internalServerError(c, error); - } -}); - -app.post("/auth/login", async (c) => { - try { - const { username, password } = await c.req.json(); - - if (!username || !password) { - return c.json({ message: "Username and password are required" }, 400); - } - - const user = await findUser(username); - if (!user) { - return c.json({ message: "Invalid credentials" }, 401); - } - - const isValid = verifyPassword(password, user.passwordHash); - if (!isValid) { - return c.json({ message: "Invalid credentials" }, 401); - } - - const token = await createAccessToken(username); - - return c.json({ access_token: token }); - } catch (error) { - logRouteError(c, "Login error:", error); - return internalServerError(c, error); - } -}); - -app.get("/user-settings", async (c) => { - try { - const payload = c.get("jwtPayload"); - const username = payload?.username || payload?.sub; - if (!username) { - return c.json({ message: "Unauthorized" }, 401); - } - - const settings = await getUserSettings(username); - return c.json({ success: true, data: settings }); - } catch (error) { - logRouteError(c, "Get user settings error:", error); - return internalServerError(c, error); - } -}); - -app.put("/user-settings", async (c) => { - try { - const payload = c.get("jwtPayload"); - const username = payload?.username || payload?.sub; - if (!username) { - return c.json({ message: "Unauthorized" }, 401); - } - - const body = (await c.req.json()) as { settings?: unknown }; - if ( - !body || - typeof body !== "object" || - !("settings" in body) || - typeof body.settings !== "object" || - body.settings === null || - Array.isArray(body.settings) - ) { - return c.json({ message: "settings object is required" }, 400); - } - - const savedSettings = await saveUserSettings(username, body.settings); - return c.json({ success: true, data: savedSettings }); - } catch (error) { - logRouteError(c, "Save user settings error:", error); - return internalServerError(c, error); - } -}); - -// Log Routes -app.get("/raw-logs", async (c) => { - try { - const payload = c.get("jwtPayload"); - const userId = payload?.username || payload?.sub; - if (!userId) { - return c.json({ message: "Unauthorized" }, 401); - } - const logs = await getAllLogs(userId); - return c.json({ data: logs }); - } catch (error) { - logRouteError(c, "Get all logs error:", error); - return internalServerError(c, error); - } -}); - -app.post("/raw-logs", async (c) => { - try { - const payload = c.get("jwtPayload"); - const userId = payload?.username || payload?.sub; // Hono JWT puts payload in 'jwtPayload' context - if (!userId) { - return c.json({ message: "Unauthorized" }, 401); - } - const { date, content, contentHash, parentHash } = await c.req.json(); - - if (!date) { - return c.json({ message: "Date is required" }, 400); - } - - if (!contentHash) { - return c.json({ message: "contentHash is required" }, 400); - } - - const savedLog = await saveLog( - userId, - date, - content || "", - contentHash, - parentHash ?? null - ); - return c.json({ success: true, data: savedLog }); - } catch (error) { - logRouteError(c, "Save log error:", error); - return internalServerError(c, error); - } -}); - -app.post("/raw-logs/bulk", async (c) => { - try { - const payload = c.get("jwtPayload"); - const userId = payload?.username || payload?.sub; - if (!userId) { - return c.json({ message: "Unauthorized" }, 401); - } - const { logs } = await c.req.json(); - - if (!logs || !Array.isArray(logs)) { - return c.json({ message: "logs array is required" }, 400); - } - - const savedLogs = await bulkSaveLogs(userId, logs); - return c.json({ success: true, data: savedLogs }); - } catch (error) { - logRouteError(c, "Bulk save logs error:", error); - return internalServerError(c, error); - } -}); - -app.get("/raw-logs/:date", async (c) => { - try { - const payload = c.get("jwtPayload"); - const userId = payload?.username || payload?.sub; - if (!userId) { - return c.json({ message: "Unauthorized" }, 401); - } - const date = c.req.param("date"); - - const log = await getLog(userId, date); - return c.json(log || { date, content: "" }); - } catch (error) { - logRouteError(c, "Get log error:", error); - return internalServerError(c, error); - } -}); - -app.get("/raw-logs/:date/backups", async (c) => { - try { - const payload = c.get("jwtPayload"); - const userId = payload?.username || payload?.sub; - if (!userId) { - return c.json({ message: "Unauthorized" }, 401); - } - const date = c.req.param("date"); - - const backups = await getLogBackups(userId, date); - return c.json({ data: backups }); - } catch (error) { - logRouteError(c, "Get log backups error:", error); - return internalServerError(c, error); - } -}); - -// Export for Lambda -export const handler = handle(app); - -// For local development with `tsx`, we need to serve it manually using @hono/node-server -const isMain = - process.argv[1] && - (process.argv[1] === fileURLToPath(import.meta.url) || - process.argv[1].endsWith("index.ts")); - -if (isMain) { - const port = Number(process.env.PORT) || 3000; - console.log(`Server is running on port ${port}`); - serve({ - fetch: app.fetch, - port, - }); -} diff --git a/apps/api/src/logs.ts b/apps/api/src/logs.ts deleted file mode 100644 index 493b6c7..0000000 --- a/apps/api/src/logs.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { GetCommand, PutCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"; - -import { getDynamoDb, LOG_BACKUPS_TABLE_NAME, LOGS_TABLE_NAME } from "./db"; - -export interface LogItem { - userId: string; - date: string; - content: string; - contentHash: string; - parentHash: string | null; - updatedAt?: string; - version?: number; -} - -export interface BackupItem { - userId: string; - backupId: string; // date#timestamp - date: string; - content: string; - originalUpdatedAt?: string; - originalVersion?: number; - backedUpAt: string; -} - -export const getAllLogs = async (userId: string): Promise => { - const result = await getDynamoDb().send( - new QueryCommand({ - TableName: LOGS_TABLE_NAME, - KeyConditionExpression: "userId = :userId", - ExpressionAttributeValues: { - ":userId": userId, - }, - }) - ); - return (result.Items as LogItem[]) || []; -}; - -export const saveLog = async ( - userId: string, - date: string, - content: string, - contentHash: string, - parentHash: string | null -) => { - const db = getDynamoDb(); - - // 1. Get current log to see if we need to backup - const currentLog = await getLog(userId, date); - - const now = new Date().toISOString(); - let nextVersion = 1; - - if (currentLog) { - // Check conflicts if necessary, but for now we prioritize saving. - // If clientUpdatedAt is provided and is older than currentLog.updatedAt, - // it means client is overwriting a newer server version. - // However, since we are backing up, this is "safe" from a data loss perspective. - - // Create Backup - const backupId = `${date}#${now}`; // or use UUID, but timestamp is sorts well - - await db.send( - new PutCommand({ - TableName: LOG_BACKUPS_TABLE_NAME, - Item: { - userId, - backupId, - date, - content: currentLog.content, - originalUpdatedAt: currentLog.updatedAt, - originalVersion: currentLog.version, - backedUpAt: now, - }, - }) - ); - - nextVersion = (currentLog.version || 0) + 1; - } - - // 2. Save new log - const newLog: LogItem = { - userId, - date, - content, - contentHash, - parentHash, - updatedAt: now, - version: nextVersion, - }; - - await db.send( - new PutCommand({ - TableName: LOGS_TABLE_NAME, - Item: newLog, - }) - ); - - return newLog; -}; - -export const getLog = async ( - userId: string, - date: string -): Promise => { - const result = await getDynamoDb().send( - new GetCommand({ - TableName: LOGS_TABLE_NAME, - Key: { - userId, - date, - }, - }) - ); - return result.Item as LogItem; -}; - -export const getLogBackups = async (userId: string, date: string) => { - // Query backups where userId = :userId AND backupId begins_with :date - const result = await getDynamoDb().send( - new QueryCommand({ - TableName: LOG_BACKUPS_TABLE_NAME, - KeyConditionExpression: - "userId = :userId AND begins_with(backupId, :date)", - ExpressionAttributeValues: { - ":userId": userId, - ":date": date, - }, - // ScanIndexForward: false, // Show newest backups first? - }) - ); - - return result.Items as BackupItem[]; -}; -export interface BulkLogInput { - date: string; - content: string; - contentHash: string; - parentHash: string | null; -} - -/** - * 여러 로그를 한 번에 저장합니다. - * 각 날짜별로 기존 데이터가 있으면 백업 후 새 데이터를 저장합니다. - */ -export const bulkSaveLogs = async ( - userId: string, - logs: BulkLogInput[] -): Promise => { - const savedLogs: LogItem[] = []; - - for (const log of logs) { - const saved = await saveLog( - userId, - log.date, - log.content, - log.contentHash, - log.parentHash - ); - savedLogs.push(saved); - } - - return savedLogs; -}; diff --git a/apps/api/src/logs/hash.ts b/apps/api/src/logs/hash.ts new file mode 100644 index 0000000..74ce104 --- /dev/null +++ b/apps/api/src/logs/hash.ts @@ -0,0 +1,9 @@ +export function calculateHashSync(content: string): string { + let hash = 2166136261; + for (let i = 0; i < content.length; i++) { + hash ^= content.charCodeAt(i); + hash += + (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24); + } + return (hash >>> 0).toString(16); +} diff --git a/apps/api/src/logs/logs.controller.ts b/apps/api/src/logs/logs.controller.ts new file mode 100644 index 0000000..ab981fa --- /dev/null +++ b/apps/api/src/logs/logs.controller.ts @@ -0,0 +1,85 @@ +import { + BadRequestException, + Body, + Controller, + Get, + Param, + Post, + Req, + UseGuards, +} from "@nestjs/common"; + +import { AuthenticatedRequest, AuthGuard } from "../auth/auth.guard"; +import { LogsService, SaveLogRequest } from "./logs.service"; + +@Controller("raw-logs") +@UseGuards(AuthGuard) +export class LogsController { + constructor(private readonly logsService: LogsService) {} + + @Get() + async getAllLogs(@Req() request: AuthenticatedRequest) { + const logs = await this.logsService.getAllLogs( + request.user?.username ?? "" + ); + return { data: logs }; + } + + @Post() + async saveLog( + @Req() request: AuthenticatedRequest, + @Body() body: SaveLogRequest + ) { + this.assertLogBody(body); + return this.logsService.saveLog(request.user?.username ?? "", body); + } + + @Post("bulk") + async bulkSaveLogs( + @Req() request: AuthenticatedRequest, + @Body() body: { logs?: SaveLogRequest[] } + ) { + if (!Array.isArray(body.logs)) { + throw new BadRequestException("logs array is required"); + } + + for (const log of body.logs) { + this.assertLogBody(log); + } + + return this.logsService.bulkSaveLogs( + request.user?.username ?? "", + body.logs + ); + } + + @Get(":date") + async getLog( + @Req() request: AuthenticatedRequest, + @Param("date") date: string + ) { + const log = await this.logsService.getLog( + request.user?.username ?? "", + date + ); + return log ?? { date, content: "" }; + } + + @Get(":date/backups") + async getLogBackups( + @Req() request: AuthenticatedRequest, + @Param("date") date: string + ) { + const backups = await this.logsService.getLogBackups( + request.user?.username ?? "", + date + ); + return { data: backups }; + } + + private assertLogBody(body: SaveLogRequest): void { + if (!body?.date) { + throw new BadRequestException("Date is required"); + } + } +} diff --git a/apps/api/src/logs/logs.repository.ts b/apps/api/src/logs/logs.repository.ts new file mode 100644 index 0000000..f30f9b7 --- /dev/null +++ b/apps/api/src/logs/logs.repository.ts @@ -0,0 +1,246 @@ +import { randomUUID } from "node:crypto"; + +import { Injectable } from "@nestjs/common"; +import { Prisma } from "@prisma/client"; + +import { PrismaService } from "../db/prisma.service"; + +export const LOGS_REPOSITORY = Symbol("LOGS_REPOSITORY"); + +export type RevisionSource = "edit" | "restore" | "import"; + +export interface CurrentLog { + userId: string; + date: string; + content: string; + contentHash: string; + parentHash: string | null; + currentRevisionId: string; + updatedAt: string; + version: number; +} + +export interface LogRevision { + revisionId: string; + userId: string; + date: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + baseContentHash?: string | null; + source: RevisionSource; + promoted: boolean; + createdAt: string; +} + +export interface SaveRevisionInput { + userId: string; + date: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + baseContentHash: string | null; + source: RevisionSource; + clientMutationId: string | null; +} + +export interface SaveRevisionResult { + revision: LogRevision; + current: CurrentLog | null; + promoted: boolean; + reason?: "STALE_BASE"; +} + +export interface LogsRepository { + listCurrentLogs(userId: string): Promise; + getCurrentLog(userId: string, date: string): Promise; + saveRevision(input: SaveRevisionInput): Promise; + listRevisions(userId: string, date: string): Promise; +} + +type PrismaClientLike = PrismaService | Prisma.TransactionClient; + +type RevisionRecord = { + revisionId: string; + userId: string; + logDate: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + baseContentHash: string | null; + source: RevisionSource; + promoted: boolean; + createdAt: Date; +}; + +function mapRevision(revision: RevisionRecord): LogRevision { + return { + revisionId: revision.revisionId, + userId: revision.userId, + date: revision.logDate, + content: revision.content, + contentHash: revision.contentHash, + baseRevisionId: revision.baseRevisionId, + baseContentHash: revision.baseContentHash, + source: revision.source, + promoted: revision.promoted, + createdAt: revision.createdAt.toISOString(), + }; +} + +@Injectable() +export class PrismaLogsRepository implements LogsRepository { + constructor(private readonly prisma: PrismaService) {} + + async listCurrentLogs(userId: string): Promise { + const rows = await this.prisma.currentLog.findMany({ + where: { userId }, + include: { revision: true }, + orderBy: { logDate: "asc" }, + }); + + return Promise.all( + rows.map(async (row) => ({ + userId: row.userId, + date: row.logDate, + content: row.revision.content, + contentHash: row.revision.contentHash, + parentHash: row.revision.baseContentHash, + currentRevisionId: row.currentRevisionId, + updatedAt: row.updatedAt.toISOString(), + version: await this.prisma.logRevision.count({ + where: { userId: row.userId, logDate: row.logDate, promoted: true }, + }), + })) + ); + } + + getCurrentLog(userId: string, date: string): Promise { + return this.getCurrentLogWithClient(this.prisma, userId, date); + } + + async saveRevision(input: SaveRevisionInput): Promise { + return this.runSerializableTransaction(async (transaction) => { + await transaction.$queryRaw` + SELECT current_revision_id + FROM current_logs + WHERE user_id = ${input.userId} AND log_date = ${input.date} + FOR UPDATE + `; + + const current = await this.getCurrentLogWithClient( + transaction, + input.userId, + input.date + ); + const shouldPromote = + !current || + input.source === "import" || + input.source === "restore" || + input.baseRevisionId === current.currentRevisionId || + input.baseContentHash === current.contentHash; + + const revisionRecord = await transaction.logRevision.create({ + data: { + revisionId: randomUUID(), + userId: input.userId, + logDate: input.date, + content: input.content, + contentHash: input.contentHash, + baseRevisionId: input.baseRevisionId, + baseContentHash: input.baseContentHash, + source: input.source, + promoted: shouldPromote, + clientMutationId: input.clientMutationId, + }, + }); + + if (shouldPromote) { + await transaction.currentLog.upsert({ + where: { + userId_logDate: { userId: input.userId, logDate: input.date }, + }, + create: { + userId: input.userId, + logDate: input.date, + currentRevisionId: revisionRecord.revisionId, + }, + update: { currentRevisionId: revisionRecord.revisionId }, + }); + } + + const currentAfterSave = shouldPromote + ? await this.getCurrentLogWithClient( + transaction, + input.userId, + input.date + ) + : current; + + return { + revision: mapRevision(revisionRecord), + current: currentAfterSave, + promoted: shouldPromote, + reason: shouldPromote ? undefined : "STALE_BASE", + }; + }); + } + + async listRevisions(userId: string, date: string): Promise { + const revisions = await this.prisma.logRevision.findMany({ + where: { userId, logDate: date }, + orderBy: { createdAt: "desc" }, + }); + + return revisions.map(mapRevision); + } + + private async getCurrentLogWithClient( + client: PrismaClientLike, + userId: string, + date: string + ): Promise { + const row = await client.currentLog.findUnique({ + where: { userId_logDate: { userId, logDate: date } }, + include: { revision: true }, + }); + if (!row) return null; + + const version = await client.logRevision.count({ + where: { userId, logDate: date, promoted: true }, + }); + + return { + userId: row.userId, + date: row.logDate, + content: row.revision.content, + contentHash: row.revision.contentHash, + parentHash: row.revision.baseContentHash, + currentRevisionId: row.currentRevisionId, + updatedAt: row.updatedAt.toISOString(), + version, + }; + } + + private async runSerializableTransaction( + callback: (transaction: Prisma.TransactionClient) => Promise + ): Promise { + const maxAttempts = 3; + + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + try { + return await this.prisma.$transaction(callback, { + isolationLevel: Prisma.TransactionIsolationLevel.Serializable, + }); + } catch (error) { + const shouldRetry = + error instanceof Prisma.PrismaClientKnownRequestError && + error.code === "P2034" && + attempt < maxAttempts; + if (!shouldRetry) throw error; + } + } + + throw new Error("Serializable transaction retry exhausted"); + } +} diff --git a/apps/api/src/logs/logs.service.test.ts b/apps/api/src/logs/logs.service.test.ts new file mode 100644 index 0000000..afadd0e --- /dev/null +++ b/apps/api/src/logs/logs.service.test.ts @@ -0,0 +1,147 @@ +import { beforeEach, describe, expect, it } from "vitest"; + +import type { LogRevision, LogsRepository } from "./logs.repository"; +import { LogsService } from "./logs.service"; + +class InMemoryLogsRepository implements LogsRepository { + currentRevisionId: string | null = null; + revisions = new Map(); + + async listCurrentLogs(userId: string) { + const rows = Array.from(this.revisions.values()) + .filter( + (revision) => + revision.userId === userId && + revision.revisionId === this.currentRevisionId + ) + .map((revision) => ({ + userId: revision.userId, + date: revision.date, + content: revision.content, + contentHash: revision.contentHash, + parentHash: revision.baseContentHash ?? null, + currentRevisionId: revision.revisionId, + updatedAt: revision.createdAt, + version: 1, + })); + + return rows; + } + + async getCurrentLog(userId: string, date: string) { + if (!this.currentRevisionId) return null; + const revision = this.revisions.get(this.currentRevisionId); + if (!revision || revision.userId !== userId || revision.date !== date) { + return null; + } + + return { + userId, + date, + content: revision.content, + contentHash: revision.contentHash, + parentHash: revision.baseContentHash ?? null, + currentRevisionId: revision.revisionId, + updatedAt: revision.createdAt, + version: 1, + }; + } + + async saveRevision(input: { + userId: string; + date: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + baseContentHash: string | null; + source: "edit" | "restore" | "import"; + clientMutationId: string | null; + }) { + const current = await this.getCurrentLog(input.userId, input.date); + const shouldPromote = + (!current && !input.baseRevisionId && !input.baseContentHash) || + (!!current && + (input.baseRevisionId === current.currentRevisionId || + input.baseContentHash === current.contentHash)); + + const revision: LogRevision = { + revisionId: `revision-${this.revisions.size + 1}`, + userId: input.userId, + date: input.date, + content: input.content, + contentHash: input.contentHash, + baseRevisionId: input.baseRevisionId, + source: input.source, + promoted: shouldPromote, + createdAt: `2026-06-21T00:00:0${this.revisions.size}.000Z`, + }; + + this.revisions.set(revision.revisionId, revision); + if (shouldPromote) { + this.currentRevisionId = revision.revisionId; + } + + const currentAfterSave = await this.getCurrentLog(input.userId, input.date); + + return { + revision, + current: currentAfterSave, + promoted: shouldPromote, + reason: shouldPromote ? undefined : ("STALE_BASE" as const), + }; + } + + async listRevisions(userId: string, date: string) { + return Array.from(this.revisions.values()).filter( + (revision) => revision.userId === userId && revision.date === date + ); + } +} + +describe("LogsService", () => { + let repository: InMemoryLogsRepository; + let service: LogsService; + + beforeEach(() => { + repository = new InMemoryLogsRepository(); + service = new LogsService(repository); + }); + + it("promotes the first revision when there is no current log", async () => { + const result = await service.saveLog("u1", { + date: "2026-06-21", + content: "first", + contentHash: "hash-first", + parentHash: null, + }); + + expect(result.promoted).toBe(true); + expect(result.data.content).toBe("first"); + expect(result.data.currentRevisionId).toBe("revision-1"); + }); + + it("preserves a stale save as an unpromoted revision without changing current", async () => { + await service.saveLog("u1", { + date: "2026-06-21", + content: "current", + contentHash: "hash-current", + parentHash: null, + }); + + const result = await service.saveLog("u1", { + date: "2026-06-21", + content: "stale local edit", + contentHash: "hash-stale", + baseRevisionId: "old-revision", + parentHash: "old-hash", + }); + + expect(result.promoted).toBe(false); + expect(result.reason).toBe("STALE_BASE"); + expect(result.data.content).toBe("current"); + expect(repository.revisions.get("revision-2")?.content).toBe( + "stale local edit" + ); + expect(repository.currentRevisionId).toBe("revision-1"); + }); +}); diff --git a/apps/api/src/logs/logs.service.ts b/apps/api/src/logs/logs.service.ts new file mode 100644 index 0000000..8723672 --- /dev/null +++ b/apps/api/src/logs/logs.service.ts @@ -0,0 +1,123 @@ +import { Inject, Injectable } from "@nestjs/common"; + +import { calculateHashSync } from "./hash"; +import { + CurrentLog, + LogRevision, + LOGS_REPOSITORY, + LogsRepository, + RevisionSource, +} from "./logs.repository"; + +export interface SaveLogRequest { + date: string; + content?: string; + contentHash?: string; + parentHash?: string | null; + baseRevisionId?: string | null; + baseContentHash?: string | null; + source?: RevisionSource; + clientMutationId?: string | null; +} + +export interface SaveLogResponse { + success: true; + data: CurrentLog & { + revisionId: string; + promoted: boolean; + reason?: "STALE_BASE"; + }; + revision: LogRevision; + promoted: boolean; + reason?: "STALE_BASE"; +} + +@Injectable() +export class LogsService { + constructor( + @Inject(LOGS_REPOSITORY) private readonly repository: LogsRepository + ) {} + + async getAllLogs(userId: string): Promise { + return this.repository.listCurrentLogs(userId); + } + + async getLog(userId: string, date: string): Promise { + return this.repository.getCurrentLog(userId, date); + } + + async saveLog( + userId: string, + request: SaveLogRequest + ): Promise { + const content = request.content ?? ""; + const contentHash = request.contentHash ?? calculateHashSync(content); + const baseRevisionId = request.baseRevisionId ?? null; + const baseContentHash = + request.baseContentHash ?? request.parentHash ?? null; + + const result = await this.repository.saveRevision({ + userId, + date: request.date, + content, + contentHash, + baseRevisionId, + baseContentHash, + source: request.source ?? "edit", + clientMutationId: request.clientMutationId ?? null, + }); + + const data = result.current ?? { + userId, + date: request.date, + content, + contentHash, + parentHash: baseContentHash, + currentRevisionId: result.revision.revisionId, + updatedAt: result.revision.createdAt, + version: 0, + }; + + return { + success: true, + data: { + ...data, + revisionId: result.revision.revisionId, + promoted: result.promoted, + reason: result.reason, + }, + revision: result.revision, + promoted: result.promoted, + reason: result.reason, + }; + } + + async bulkSaveLogs(userId: string, logs: SaveLogRequest[]) { + const saved = []; + for (const log of logs) { + saved.push(await this.saveLog(userId, log)); + } + + return { + success: true, + data: saved.map((result) => result.data), + }; + } + + async getLogBackups(userId: string, date: string) { + const current = await this.repository.getCurrentLog(userId, date); + const revisions = await this.repository.listRevisions(userId, date); + + return revisions + .filter((revision) => revision.revisionId !== current?.currentRevisionId) + .map((revision, index) => ({ + userId, + backupId: revision.revisionId, + date, + content: revision.content, + originalUpdatedAt: revision.createdAt, + originalVersion: revisions.length - index, + backedUpAt: revision.createdAt, + })); + } +} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts new file mode 100644 index 0000000..d5bb9f2 --- /dev/null +++ b/apps/api/src/main.ts @@ -0,0 +1,44 @@ +import "reflect-metadata"; + +import { NestFactory } from "@nestjs/core"; + +import { AppModule } from "./app.module"; + +const getAllowedOrigins = (): string[] => + ( + process.env.ALLOWED_ORIGINS ?? + [ + "http://localhost:3000", + "https://localhost:3000", + "http://localhost:5173", + "https://localhost:5173", + "http://localhost:5174", + "https://localhost:5174", + "http://localhost:4000", + "https://localhost:4000", + ].join(",") + ) + .split(",") + .map((origin) => origin.trim()) + .filter(Boolean); + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + const allowedOrigins = getAllowedOrigins(); + + app.enableCors({ + origin(origin, callback) { + if (!origin || allowedOrigins.includes(origin)) { + callback(null, true); + return; + } + callback(new Error(`Origin ${origin} is not allowed by CORS`), false); + }, + }); + + const port = Number(process.env.PORT || 3000); + await app.listen(port); + console.log(`API server is running on port ${port}`); +} + +void bootstrap(); diff --git a/apps/api/src/settings/settings.controller.ts b/apps/api/src/settings/settings.controller.ts new file mode 100644 index 0000000..6482752 --- /dev/null +++ b/apps/api/src/settings/settings.controller.ts @@ -0,0 +1,30 @@ +import { Body, Controller, Get, Put, Req, UseGuards } from "@nestjs/common"; + +import { AuthenticatedRequest, AuthGuard } from "../auth/auth.guard"; +import { SettingsService } from "./settings.service"; + +@Controller("user-settings") +@UseGuards(AuthGuard) +export class SettingsController { + constructor(private readonly settingsService: SettingsService) {} + + @Get() + async getSettings(@Req() request: AuthenticatedRequest) { + const settings = await this.settingsService.getUserSettings( + request.user?.username ?? "" + ); + return { success: true, data: settings }; + } + + @Put() + async saveSettings( + @Req() request: AuthenticatedRequest, + @Body() body: { settings?: unknown } + ) { + const settings = await this.settingsService.saveUserSettings( + request.user?.username ?? "", + body.settings + ); + return { success: true, data: settings }; + } +} diff --git a/apps/api/src/settings/settings.repository.ts b/apps/api/src/settings/settings.repository.ts new file mode 100644 index 0000000..ee30db7 --- /dev/null +++ b/apps/api/src/settings/settings.repository.ts @@ -0,0 +1,74 @@ +import { Injectable } from "@nestjs/common"; + +import { PrismaService } from "../db/prisma.service"; + +export type UserSettings = Partial< + Record<"app-theme" | "app-theme-by-scheme", string> +>; + +const USER_SETTING_KEYS = ["app-theme", "app-theme-by-scheme"] as const; + +export const sanitizeUserSettings = (value: unknown): UserSettings => { + if (!value || typeof value !== "object" || Array.isArray(value)) { + return {}; + } + + const settings = value as Record; + const sanitized: UserSettings = {}; + + for (const key of USER_SETTING_KEYS) { + const rawValue = settings[key]; + if (typeof rawValue === "string") { + sanitized[key] = rawValue; + } + } + + return sanitized; +}; + +@Injectable() +export class SettingsRepository { + constructor(private readonly prisma: PrismaService) {} + + async getUserSettings(username: string): Promise { + const row = await this.prisma.userSettings.findUnique({ + where: { username }, + select: { appTheme: true, appThemeByScheme: true }, + }); + if (!row) return {}; + + return { + ...(row.appTheme ? { "app-theme": row.appTheme } : {}), + ...(row.appThemeByScheme + ? { "app-theme-by-scheme": row.appThemeByScheme } + : {}), + }; + } + + async saveUserSettings( + username: string, + settings: unknown + ): Promise { + const sanitizedInput = sanitizeUserSettings(settings); + const currentSettings = await this.getUserSettings(username); + const mergedSettings = { + ...currentSettings, + ...sanitizedInput, + }; + + await this.prisma.userSettings.upsert({ + where: { username }, + create: { + username, + appTheme: mergedSettings["app-theme"] ?? null, + appThemeByScheme: mergedSettings["app-theme-by-scheme"] ?? null, + }, + update: { + appTheme: mergedSettings["app-theme"] ?? null, + appThemeByScheme: mergedSettings["app-theme-by-scheme"] ?? null, + }, + }); + + return mergedSettings; + } +} diff --git a/apps/api/src/settings/settings.service.ts b/apps/api/src/settings/settings.service.ts new file mode 100644 index 0000000..6ef32b2 --- /dev/null +++ b/apps/api/src/settings/settings.service.ts @@ -0,0 +1,20 @@ +import { BadRequestException, Injectable } from "@nestjs/common"; + +import { SettingsRepository, UserSettings } from "./settings.repository"; + +@Injectable() +export class SettingsService { + constructor(private readonly settingsRepository: SettingsRepository) {} + + getUserSettings(username: string): Promise { + return this.settingsRepository.getUserSettings(username); + } + + saveUserSettings(username: string, settings: unknown): Promise { + if (!settings || typeof settings !== "object" || Array.isArray(settings)) { + throw new BadRequestException("settings object is required"); + } + + return this.settingsRepository.saveUserSettings(username, settings); + } +} diff --git a/apps/api/src/users.ts b/apps/api/src/users.ts deleted file mode 100644 index 4e42a38..0000000 --- a/apps/api/src/users.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { GetCommand, PutCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb"; - -import { getDynamoDb, TABLE_NAME } from "./db"; - -export interface User { - username: string; - passwordHash: string; - settings?: Record; -} - -export type UserSettings = Partial< - Record<"app-theme" | "app-theme-by-scheme", string> ->; - -const USER_SETTING_KEYS = ["app-theme", "app-theme-by-scheme"] as const; - -const sanitizeUserSettings = (value: unknown): UserSettings => { - if (!value || typeof value !== "object" || Array.isArray(value)) { - return {}; - } - - const settings = value as Record; - const sanitized: UserSettings = {}; - - for (const key of USER_SETTING_KEYS) { - const rawValue = settings[key]; - if (typeof rawValue === "string") { - sanitized[key] = rawValue; - } - } - - return sanitized; -}; - -export const findUser = async (username: string): Promise => { - const result = await getDynamoDb().send( - new GetCommand({ - TableName: TABLE_NAME, - Key: { username }, - }) - ); - return result.Item as User; -}; - -export const createUser = async ( - username: string, - passwordHash: string -): Promise => { - const user: User = { - username, - passwordHash, - }; - - await getDynamoDb().send( - new PutCommand({ - TableName: TABLE_NAME, - Item: user, - ConditionExpression: "attribute_not_exists(username)", - }) - ); - - return user; -}; - -export const getUserSettings = async ( - username: string -): Promise => { - const user = await findUser(username); - return sanitizeUserSettings(user?.settings ?? null); -}; - -export const saveUserSettings = async ( - username: string, - settings: unknown -): Promise => { - const sanitizedInput = sanitizeUserSettings(settings); - const currentSettings = await getUserSettings(username); - const mergedSettings = { - ...currentSettings, - ...sanitizedInput, - }; - - await getDynamoDb().send( - new UpdateCommand({ - TableName: TABLE_NAME, - Key: { username }, - UpdateExpression: "SET #settings = :settings, #updatedAt = :updatedAt", - ConditionExpression: "attribute_exists(username)", - ExpressionAttributeNames: { - "#settings": "settings", - "#updatedAt": "settingsUpdatedAt", - }, - ExpressionAttributeValues: { - ":settings": mergedSettings, - ":updatedAt": new Date().toISOString(), - }, - }) - ); - - return mergedSettings; -}; diff --git a/apps/api/src/users/users.repository.ts b/apps/api/src/users/users.repository.ts new file mode 100644 index 0000000..b2f57eb --- /dev/null +++ b/apps/api/src/users/users.repository.ts @@ -0,0 +1,38 @@ +import { ConflictException, Injectable } from "@nestjs/common"; +import { Prisma } from "@prisma/client"; + +import { PrismaService } from "../db/prisma.service"; + +export interface User { + username: string; + passwordHash: string; +} + +@Injectable() +export class UsersRepository { + constructor(private readonly prisma: PrismaService) {} + + async findUser(username: string): Promise { + return this.prisma.user.findUnique({ + where: { username }, + select: { username: true, passwordHash: true }, + }); + } + + async createUser(username: string, passwordHash: string): Promise { + try { + return await this.prisma.user.create({ + data: { username, passwordHash }, + select: { username: true, passwordHash: true }, + }); + } catch (error) { + if ( + error instanceof Prisma.PrismaClientKnownRequestError && + error.code === "P2002" + ) { + throw new ConflictException("Username already exists"); + } + throw error; + } + } +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 9b4808a..09b771d 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -4,6 +4,8 @@ "module": "CommonJS", "moduleResolution": "node", "strict": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, @@ -11,5 +13,5 @@ "rootDir": "./src" }, "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist", "**/*.test.ts"] } diff --git a/apps/web/package.json b/apps/web/package.json index 63b2ec4..3ebed56 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -10,9 +10,7 @@ "build": "tsc && vite build", "lint": "eslint . --max-warnings 0", "lint:fix": "eslint . --max-warnings 0 --fix", - "preview": "vite preview", - "deploy": "if [ \"${MY_COMMIT_ROOT_DEPLOY:-}\" != \"1\" ]; then echo \"Use repository-root deploy command: pnpm run deploy:prod or pnpm run deploy:dev\"; exit 1; fi; pnpm build && serverless deploy", - "remove": "serverless remove" + "preview": "vite preview" }, "dependencies": { "@reduxjs/toolkit": "^2.11.2", @@ -42,9 +40,6 @@ "postcss": "^8.4.28", "prettier": "^3.7.4", "prettier-plugin-tailwindcss": "^0.7.2", - "serverless": "^3.38.0", - "serverless-cloudfront-invalidate": "^1.12.0", - "serverless-s3-sync": "^3.2.0", "tailwindcss": "^3.3.3", "typescript": "^5.0.2", "vite": "^4.3.2", diff --git a/apps/web/serverless.yml b/apps/web/serverless.yml deleted file mode 100644 index 4bbe0d7..0000000 --- a/apps/web/serverless.yml +++ /dev/null @@ -1,288 +0,0 @@ -service: my-commit-web - -frameworkVersion: '3' -useDotenv: true - -provider: - name: aws - runtime: nodejs18.x - region: ${opt:region, 'ap-northeast-2'} - stage: ${opt:stage, 'dev'} - stackName: ${self:provider.stage}-${self:service} - -custom: - bucketName: my-commit-web-sb-${self:provider.stage} - webDomainName: ${env:WEB_DOMAIN_NAME, ''} - acmCertificateArn: ${env:ACM_CERTIFICATE_ARN, ''} - - s3Sync: - - bucketName: ${self:custom.bucketName} - localDir: dist - deleteRemoved: true - params: - - index.html: - CacheControl: 'no-cache' - # Service Worker는 항상 최신 버전 체크가 가능해야 하므로 immutable 금지 - - sw.js: - CacheControl: 'no-cache, no-store, must-revalidate' - - registerSW.js: - CacheControl: 'no-cache, no-store, must-revalidate' - - manifest.webmanifest: - CacheControl: 'no-cache, no-store, must-revalidate' - - 'workbox-*.js': - CacheControl: 'public, max-age=31536000, immutable' - - '*.css': - CacheControl: 'public, max-age=31536000, immutable' - - '*.woff2': - CacheControl: 'public, max-age=31536000, immutable' - - 'assets/*': - CacheControl: 'public, max-age=31536000, immutable' - -plugins: - - serverless-s3-sync - # [이유] CloudFront Distribution ID를 자동으로 찾지 못하는 문제 등으로 인해 배포 실패 발생. 캐시 무효화는 수동으로 진행하거나 추후 설정. - # - serverless-cloudfront-invalidate # 설정 필요, 일단 비활성화 - -resources: - Conditions: - HasWebDomainName: - Fn::Not: - - Fn::Equals: - - ${self:custom.webDomainName} - - '' - HasAcmCertificateArn: - Fn::Not: - - Fn::Equals: - - ${self:custom.acmCertificateArn} - - '' - UseCustomDomain: - Fn::And: - - Condition: HasWebDomainName - - Condition: HasAcmCertificateArn - - Resources: - # index.html, service worker 같은 진입 파일의 배포 직후 최신 버전 확인 - CachingDisabledPolicy: - Type: AWS::CloudFront::CachePolicy - Properties: - CachePolicyConfig: - Name: ${self:provider.stage}-${self:service}-caching-disabled - DefaultTTL: 0 - MaxTTL: 1 - MinTTL: 0 - ParametersInCacheKeyAndForwardedToOrigin: - CookiesConfig: - CookieBehavior: none - EnableAcceptEncodingBrotli: true - EnableAcceptEncodingGzip: true - HeadersConfig: - HeaderBehavior: none - QueryStringsConfig: - QueryStringBehavior: none - - # 파일명 변경 기반 해시 정적 자산의 장기 캐시 - StaticAssetsPolicy: - Type: AWS::CloudFront::CachePolicy - Properties: - CachePolicyConfig: - Name: ${self:provider.stage}-${self:service}-static-assets - DefaultTTL: 31536000 - MaxTTL: 31536000 - MinTTL: 86400 - ParametersInCacheKeyAndForwardedToOrigin: - CookiesConfig: - CookieBehavior: none - EnableAcceptEncodingBrotli: true - EnableAcceptEncodingGzip: true - HeadersConfig: - HeaderBehavior: none - QueryStringsConfig: - QueryStringBehavior: none - - WebsiteBucket: - Type: AWS::S3::Bucket - Properties: - BucketName: ${self:custom.bucketName} - WebsiteConfiguration: - IndexDocument: index.html - ErrorDocument: index.html - PublicAccessBlockConfiguration: - BlockPublicAcls: false - BlockPublicPolicy: false - IgnorePublicAcls: false - RestrictPublicBuckets: false - CorsConfiguration: - CorsRules: - - AllowedOrigins: - - '*' - AllowedMethods: - - GET - - HEAD - AllowedHeaders: - - '*' - - WebsiteBucketPolicy: - Type: AWS::S3::BucketPolicy - Properties: - Bucket: !Ref WebsiteBucket - PolicyDocument: - Statement: - - Sid: PublicReadGetObject - Effect: Allow - Principal: '*' - Action: - - s3:GetObject - Resource: !Sub '${WebsiteBucket.Arn}/*' - - WebsiteCloudFront: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Enabled: true - DefaultRootObject: index.html - Comment: My Commit Web App - ${self:provider.stage} - PriceClass: PriceClass_All # 전 세계 모든 엣지 로케이션 - HttpVersion: http2and3 - - Aliases: - Fn::If: - - UseCustomDomain - - - ${self:custom.webDomainName} - - !Ref AWS::NoValue - ViewerCertificate: - Fn::If: - - UseCustomDomain - - AcmCertificateArn: ${self:custom.acmCertificateArn} - SslSupportMethod: sni-only - MinimumProtocolVersion: TLSv1.2_2021 - - CloudFrontDefaultCertificate: true - - Origins: - - Id: S3Origin - DomainName: !Sub '${WebsiteBucket}.s3-website.${AWS::Region}.amazonaws.com' - CustomOriginConfig: - HTTPPort: 80 - HTTPSPort: 443 - OriginProtocolPolicy: http-only - # SPA 기본 진입 경로 "/" 요청에 대한 최신 HTML 확인 - DefaultCacheBehavior: - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - - OPTIONS - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref CachingDisabledPolicy - CacheBehaviors: - # 직접 /index.html 접근 시 동일한 즉시 재검사 - - PathPattern: index.html - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref CachingDisabledPolicy - # 새 precache manifest 인지를 위한 서비스 워커 스크립트 갱신 - - PathPattern: sw.js - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref CachingDisabledPolicy - # vite-plugin-pwa 등록 스크립트의 즉시 최신본 수신 - - PathPattern: registerSW.js - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref CachingDisabledPolicy - # 설치 메타데이터의 오래된 상태 잔존 방지 - - PathPattern: manifest.webmanifest - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref CachingDisabledPolicy - # 파일명 변경 기반 workbox 런타임 파일의 immutable 캐시 - - PathPattern: workbox-*.js - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref StaticAssetsPolicy - # Vite 생성 해시 자산 묶음의 장기 캐시 - - PathPattern: assets/* - TargetOriginId: S3Origin - ViewerProtocolPolicy: redirect-to-https - AllowedMethods: - - GET - - HEAD - CachedMethods: - - GET - - HEAD - Compress: true - CachePolicyId: !Ref StaticAssetsPolicy - CustomErrorResponses: - - ErrorCode: 403 - ResponseCode: 200 - ResponsePagePath: /index.html - # SPA fallback HTML 의 에지 장기 잔존 방지 - ErrorCachingMinTTL: 0 - - ErrorCode: 404 - ResponseCode: 200 - ResponsePagePath: /index.html - # SPA fallback HTML 의 에지 장기 잔존 방지 - ErrorCachingMinTTL: 0 - - Outputs: - WebsiteBucketName: - Description: S3 Bucket Name - Value: !Ref WebsiteBucket - Export: - Name: ${self:service}-${self:provider.stage}-bucket-name - - WebsiteURL: - Description: S3 Website URL - Value: !GetAtt WebsiteBucket.WebsiteURL - Export: - Name: ${self:service}-${self:provider.stage}-website-url - - CloudFrontURL: - Description: CloudFront Distribution URL - Value: !GetAtt WebsiteCloudFront.DomainName - Export: - Name: ${self:service}-${self:provider.stage}-cloudfront-url - - CloudFrontDistributionId: - Description: CloudFront Distribution ID - Value: !Ref WebsiteCloudFront - Export: - Name: ${self:service}-${self:provider.stage}-cloudfront-id diff --git a/apps/web/src/features/dataManagement/backupService.test.ts b/apps/web/src/features/dataManagement/backupService.test.ts index 3388c9b..fe1ab7d 100644 --- a/apps/web/src/features/dataManagement/backupService.test.ts +++ b/apps/web/src/features/dataManagement/backupService.test.ts @@ -1,6 +1,7 @@ import { saveAs } from 'file-saver'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { calculateHashSync } from '../../utils/HashUtil'; import { fetchAndDownloadServerBackup } from './backupService'; // Mock file-saver @@ -385,11 +386,12 @@ describe('backupService', () => { storageMock.setItem('token', 'mock-token'); // Mock bulkSaveLogsToServer + const savedContentHash = calculateHashSync('legacy log'); const mockSavedLogs = [ { date: '2025-12-20', content: 'legacy log', - contentHash: 'hash-123', + contentHash: savedContentHash, parentHash: null, updatedAt: '2026-01-12T00:00:00Z', }, @@ -405,16 +407,82 @@ describe('backupService', () => { // Verify server sync expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining('/raw-logs/bulk'), - expect.objectContaining({ method: 'POST' }), + expect.objectContaining({ + method: 'POST', + body: expect.stringContaining('"source":"import"'), + }), ); // Verify localStorage (server-provided data should be stored) expect(storageMock.setItem).toHaveBeenCalledWith( '2025-12-20', - expect.stringContaining('hash-123'), + expect.stringContaining(savedContentHash), + ); + expect(JSON.parse(mockStorage['2025-12-20']).parentHash).toBe( + savedContentHash, ); }); + it('should skip empty logs when importing backup locally', async () => { + const backup = { + logs: { + '2025-12-24': '', + '2025-12-25': 'new log', + }, + settings: {}, + }; + const file = createMockFile(backup, 'backup.json'); + + const { importBackup } = await import('./backupService'); + const report = await importBackup(file); + + expect(report.appliedLogs).toBe(1); + expect(report.skippedEmptyLogs).toBe(1); + expect(mockStorage['2025-12-24']).toBeUndefined(); + expect(mockStorage['2025-12-25']).toBeDefined(); + }); + + it('should skip empty logs when importing backup with server sync', async () => { + const backup = { + logs: { + '2025-12-24': '', + '2025-12-25': 'new log', + }, + settings: {}, + }; + const file = createMockFile(backup, 'backup.json'); + + storageMock.setItem('token', 'mock-token'); + vi.mocked(global.fetch).mockResolvedValue({ + ok: true, + json: async () => ({ + success: true, + data: [ + { + date: '2025-12-25', + content: 'new log', + contentHash: 'hash-123', + parentHash: null, + updatedAt: '2026-01-12T00:00:00Z', + }, + ], + }), + } as Response); + + const { importBackup } = await import('./backupService'); + const report = await importBackup(file); + + expect(report.appliedLogs).toBe(1); + expect(report.skippedEmptyLogs).toBe(1); + expect(global.fetch).toHaveBeenCalledWith( + expect.stringContaining('/raw-logs/bulk'), + expect.objectContaining({ + body: expect.not.stringContaining('"date":"2025-12-24"'), + }), + ); + expect(mockStorage['2025-12-24']).toBeUndefined(); + }); + it('should clear existing logs before applying backup', async () => { const backup = { logs: { '2025-12-25': 'new log' }, settings: {} }; const file = createMockFile(backup, 'backup.json'); diff --git a/apps/web/src/features/dataManagement/backupService.ts b/apps/web/src/features/dataManagement/backupService.ts index 224a6a8..606ed54 100644 --- a/apps/web/src/features/dataManagement/backupService.ts +++ b/apps/web/src/features/dataManagement/backupService.ts @@ -246,6 +246,7 @@ interface BulkLogInput { content: string; contentHash: string; parentHash: string | null; + source?: 'edit' | 'restore' | 'import'; } /** @@ -278,6 +279,9 @@ export const importBackup = async (file: File): Promise => { ) .sort(([a], [b]) => a.localeCompare(b)); const emptyLogs = logEntries.filter(([, value]) => !value); + const logsToApply = logEntries.filter(([, value]) => + hasLogContent(value), + ); // 1. 기존 로그 데이터 삭제 (For both sync and local modes, to ensure clean state) // 기존 HEAD는 selective update였지만, main은 clean & replace 방식이었음. @@ -293,12 +297,13 @@ export const importBackup = async (file: File): Promise => { ); // 2. Bulk API로 모든 로그를 한 번에 업로드 - const bulkLogs: BulkLogInput[] = logEntries.map( + const bulkLogs: BulkLogInput[] = logsToApply.map( ([date, logContent]) => ({ date, content: logContent, contentHash: calculateHashSync(logContent), parentHash: null, // 새로운 체인 시작 + source: 'import', }), ); @@ -307,23 +312,32 @@ export const importBackup = async (file: File): Promise => { // 3. 서버 응답으로 localStorage 저장 if (result?.success && result.data) { const serverDataMap = new Map( - result.data.map((item: { date: string; contentHash: string }) => [ - item.date, - item.contentHash, - ]), + result.data.map( + (item: { + date: string; + contentHash: string; + parentHash: string | null; + promoted?: boolean; + }) => [item.date, item], + ), ); - logEntries.forEach(([date, logContent]) => { - const serverHash = serverDataMap.get(date); - if (serverHash) { - saveToStorage(date, logContent, { parentHash: serverHash }); + logsToApply.forEach(([date, logContent]) => { + const serverData = serverDataMap.get(date); + if (serverData) { + saveToStorage(date, logContent, { + parentHash: + serverData.promoted === false + ? (serverData.parentHash ?? null) + : serverData.contentHash, + }); } else { saveToStorage(date, logContent); } }); } else { // 서버 저장 실패 시 로컬에만 저장 - logEntries.forEach(([date, logContent]) => { + logsToApply.forEach(([date, logContent]) => { saveToStorage(date, logContent); }); } @@ -331,7 +345,7 @@ export const importBackup = async (file: File): Promise => { // 비로그인 상태: 로컬 저장만 console.log('[importBackup] Not logged in - saving to local only'); - logEntries.forEach(([date, logContent]) => { + logsToApply.forEach(([date, logContent]) => { saveToStorage(date, logContent); }); } @@ -346,14 +360,12 @@ export const importBackup = async (file: File): Promise => { }); const failedLogs: string[] = []; - logEntries - .filter(([, value]) => value) - .forEach(([key, value]) => { - const stored = localStorage.getItem(key); - if (!matchesStoredContent(stored, String(value))) { - failedLogs.push(key); - } - }); + logsToApply.forEach(([key, value]) => { + const stored = localStorage.getItem(key); + if (!matchesStoredContent(stored, String(value))) { + failedLogs.push(key); + } + }); const report: ImportBackupReport = { fileName: file.name, diff --git a/apps/web/src/services/LogService.ts b/apps/web/src/services/LogService.ts index 184e616..8b3985d 100644 --- a/apps/web/src/services/LogService.ts +++ b/apps/web/src/services/LogService.ts @@ -12,7 +12,11 @@ export interface ServerLogResponse { parentHash: string | null; updatedAt: string; version: number; + promoted?: boolean; + reason?: 'STALE_BASE'; }; + promoted?: boolean; + reason?: 'STALE_BASE'; } export interface BackupItem { @@ -208,6 +212,7 @@ export async function bulkSaveLogsToServer( content: string; contentHash: string; parentHash: string | null; + source?: 'edit' | 'restore' | 'import'; }[], ): Promise<{ success: boolean; diff --git a/apps/web/src/store/RawLogStorageSyncMiddleware.test.ts b/apps/web/src/store/RawLogStorageSyncMiddleware.test.ts new file mode 100644 index 0000000..b0e59e6 --- /dev/null +++ b/apps/web/src/store/RawLogStorageSyncMiddleware.test.ts @@ -0,0 +1,167 @@ +import { configureStore } from '@reduxjs/toolkit'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { calculateHashSync } from '../utils/HashUtil'; +import { loadFromStorage, saveToStorage } from '../utils/StorageUtil'; +import { authReducer } from './auth'; +import { + LogsReducer, + resolveConflict, + setConflict, + setCurrentDateServerFetchComplete, + triggerCurrentDateFetch, + updateRawLog, +} from './logs'; +import { RawLogStorageSyncMiddleware } from './RawLogStorageSyncMiddleware'; +import { RestNotificationReducer } from './restNotification'; +import { SoundSettingsReducer } from './soundSettings'; + +const saveLogToServer = vi.fn(); + +vi.mock('../services/LogService', () => ({ + getLogFromServer: vi.fn(), + saveLogToServer: (...args: unknown[]) => saveLogToServer(...args), +})); + +const createTestStore = () => + configureStore({ + reducer: { + logs: LogsReducer, + restNotification: RestNotificationReducer, + soundSettings: SoundSettingsReducer, + auth: authReducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware().prepend(RawLogStorageSyncMiddleware.middleware), + }); + +describe('RawLogStorageSyncMiddleware', () => { + beforeEach(() => { + localStorage.clear(); + saveLogToServer.mockReset(); + }); + + afterEach(() => { + vi.useRealTimers(); + vi.restoreAllMocks(); + }); + + it('should save the selected conflict resolution to the server', async () => { + const store = createTestStore(); + const currentDate = store.getState().logs.currentDate; + const localContent = '[09:00] + local'; + const localHash = calculateHashSync(localContent); + + saveToStorage(currentDate, localContent); + saveLogToServer.mockResolvedValue({ + success: true, + data: { + contentHash: localHash, + parentHash: null, + updatedAt: '2026-01-12T00:00:00.000Z', + }, + }); + + store.dispatch( + setConflict({ + localContent, + serverContent: '[09:00] + server', + baseContent: '', + localUpdatedAt: '2026-01-12T00:00:00.000Z', + serverUpdatedAt: '2026-01-12T00:01:00.000Z', + }), + ); + + store.dispatch(resolveConflict({ choice: 'local' })); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(saveLogToServer).toHaveBeenCalledWith( + currentDate, + localContent, + localHash, + localHash, + ); + }); + + it('should store the saved content hash as the next sync base after a promoted edit sync', async () => { + vi.useFakeTimers(); + localStorage.setItem('token', 'mock-token'); + + const store = createTestStore(); + const currentDate = store.getState().logs.currentDate; + const previousContent = '[08:00] + previous'; + const content = '[09:00] + synced'; + const previousHash = calculateHashSync(previousContent); + const contentHash = calculateHashSync(content); + + saveToStorage(currentDate, previousContent); + + saveLogToServer.mockResolvedValue({ + success: true, + data: { + contentHash, + parentHash: previousHash, + updatedAt: '2026-01-12T00:00:00.000Z', + promoted: true, + }, + promoted: true, + }); + + store.dispatch(setCurrentDateServerFetchComplete(true)); + store.dispatch(updateRawLog(content)); + + await vi.advanceTimersByTimeAsync(300); + + expect(loadFromStorage(currentDate).parentHash).toBe(contentHash); + }); + + it('should keep an unpromoted save as a local draft instead of marking it synced', async () => { + vi.useFakeTimers(); + localStorage.setItem('token', 'mock-token'); + + const store = createTestStore(); + const currentDate = store.getState().logs.currentDate; + const serverContent = '[08:00] + server current'; + const draftContent = '[09:00] + stale draft'; + const serverHash = calculateHashSync(serverContent); + const draftHash = calculateHashSync(draftContent); + + saveToStorage(currentDate, serverContent); + + saveLogToServer.mockResolvedValue({ + success: true, + data: { + content: serverContent, + contentHash: serverHash, + parentHash: null, + updatedAt: '2026-01-12T00:00:00.000Z', + promoted: false, + reason: 'STALE_BASE', + }, + promoted: false, + reason: 'STALE_BASE', + }); + + store.dispatch(setCurrentDateServerFetchComplete(true)); + store.dispatch(updateRawLog(draftContent)); + + await vi.advanceTimersByTimeAsync(300); + + const stored = loadFromStorage(currentDate); + expect(store.getState().logs.syncStatus).toBe('error'); + expect(stored.content).toBe(draftContent); + expect(stored.contentHash).toBe(draftHash); + expect(stored.parentHash).toBe(serverHash); + }); + + it('should not upload a hydrated server fetch as a user edit', async () => { + const store = createTestStore(); + + store.dispatch(triggerCurrentDateFetch()); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(saveLogToServer).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/web/src/store/RawLogStorageSyncMiddleware.ts b/apps/web/src/store/RawLogStorageSyncMiddleware.ts index 2c660f5..0f6cdae 100644 --- a/apps/web/src/store/RawLogStorageSyncMiddleware.ts +++ b/apps/web/src/store/RawLogStorageSyncMiddleware.ts @@ -92,12 +92,11 @@ startAppListening({ localData.parentHash, ); - if (result && result.success) { + if (result && result.success && result.promoted !== false) { listenerApi.dispatch(setSyncStatus('synced')); if (result.data?.updatedAt) { listenerApi.dispatch(setLastSyncedAt(result.data.updatedAt)); - // 서버 저장 성공 시 parentHash를 서버의 contentHash로 업데이트 - // (서버가 새로운 부모가 됨) + // 다음 편집의 base로 방금 서버 current가 된 content hash를 저장 saveToStorage(currentDate, nextRawLogs, { parentHash: result.data.contentHash, }); @@ -205,7 +204,11 @@ startAppListening({ localHash, localParent, ); - if (result?.success && result.data?.updatedAt) { + if ( + result?.success && + result.promoted !== false && + result.data?.updatedAt + ) { listenerApi.dispatch(setLastSyncedAt(result.data.updatedAt)); saveToStorage(changedDate, localContent, { parentHash: result.data.contentHash, @@ -252,7 +255,8 @@ startAppListening({ startAppListening({ actionCreator: resolveConflict, effect: async (action, listenerApi) => { - const { currentDate, conflict } = listenerApi.getState().logs; + const { conflict } = listenerApi.getOriginalState().logs; + const { currentDate } = listenerApi.getState().logs; if (!conflict) { console.error('No conflict to resolve'); @@ -291,7 +295,11 @@ startAppListening({ parentHash, ); - if (result?.success && result.data?.updatedAt) { + if ( + result?.success && + result.promoted !== false && + result.data?.updatedAt + ) { listenerApi.dispatch(setLastSyncedAt(result.data.updatedAt)); saveToStorage(currentDate, selectedContent, { parentHash: result.data.contentHash, diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3c993ad --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +services: + api: + build: + context: . + dockerfile: Dockerfile.api + environment: + PORT: 3000 + JWT_SECRET: ${JWT_SECRET} + ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-http://localhost,http://localhost:8080,http://localhost:4000} + DATABASE_URL: ${DOCKER_DATABASE_URL:-mysql://my_commit:my_commit@host.docker.internal:3306/my_commit} + extra_hosts: + - "host.docker.internal:host-gateway" + restart: unless-stopped + + web: + build: + context: . + dockerfile: Dockerfile.web + args: + VITE_API_URL: /api + ports: + - "${WEB_PORT:-8080}:80" + depends_on: + - api + restart: unless-stopped diff --git a/docs/API_GATEWAY_HTTPS_SETUP.md b/docs/API_GATEWAY_HTTPS_SETUP.md deleted file mode 100644 index b9c4125..0000000 --- a/docs/API_GATEWAY_HTTPS_SETUP.md +++ /dev/null @@ -1,90 +0,0 @@ -# API Gateway HTTPS / Custom Domain (선택) - -이 문서는 **기본 배포(= execute-api 기본 도메인 사용)** 이후, 필요할 때만 커스텀 API 도메인을 붙이는 절차를 설명합니다. - -기본 운영에는 필수 아님: - -- 기본 API URL: `https://{api-id}.execute-api.ap-northeast-2.amazonaws.com` - ---- - -## 1. 사전 준비 - -### 1-1. ACM 인증서 - -API Gateway용 인증서는 API 리전(`ap-northeast-2`)에 있어야 합니다. - -1. AWS Console > Certificate Manager -2. 리전: `ap-northeast-2` -3. `api.yourdomain.com` 또는 `*.yourdomain.com` 인증서 발급 -4. DNS 검증 완료(`Issued`) - -### 1-2. 배포 전제 - -- API 스택(`my-commit-api-prod`)이 먼저 배포되어 있어야 함 -- 외부 DNS(가비아/Cloudflare 등) 접근 권한 필요 - ---- - -## 2. API Gateway 콘솔에서 커스텀 도메인 연결 - -1. AWS Console > API Gateway > Custom domain names -2. `Create` -3. 설정: - - Domain name: `api.yourdomain.com` - - Endpoint: `Regional` - - TLS: `TLS 1.2` - - Certificate: 위 인증서 선택 -4. 저장 후 대상 API(`my-commit-api-prod`)와 매핑 - ---- - -## 3. 외부 DNS 설정 - -외부 DNS에 CNAME 추가: - -- Host: `api` -- Value: API Gateway가 제공한 regional domain (`d-xxxxx.execute-api.ap-northeast-2.amazonaws.com`) - -전파 확인: - -```bash -dig api.yourdomain.com -``` - ---- - -## 4. 애플리케이션 반영 - -커스텀 API 도메인을 쓰려면 아래도 함께 갱신해야 합니다. - -1. Web API URL - -```bash -VITE_API_URL=https://api.yourdomain.com -``` - -2. API CORS 허용 도메인 - -```bash -WEB_ORIGIN=https://{your-web-domain} -``` - -API 재배포: - -```bash -pnpm deploy:api -- --stage prod -``` - -Web 재배포: - -```bash -pnpm deploy:web -- --stage prod -``` - ---- - -## 5. 참고 - -- 현재 프로젝트 기본 전략은 커스텀 도메인 없이도 운영 가능 -- `serverless-domain-manager` 플러그인 기반 자동 도메인 생성은 사용하지 않음 diff --git a/docs/CLOUDFORMATION_GUIDE.md b/docs/CLOUDFORMATION_GUIDE.md deleted file mode 100644 index cdc00b1..0000000 --- a/docs/CLOUDFORMATION_GUIDE.md +++ /dev/null @@ -1,100 +0,0 @@ -# CloudFormation & Serverless Framework 학습 노트 - -이번 배포 과정에서 경험한 AWS CloudFormation과 Serverless Framework의 작동 원리를 정리한 문서입니다. - -## 1. Serverless Framework와 CloudFormation의 관계 - -Serverless Framework(`sls`)는 독자적인 배포 도구가 아니라, **CloudFormation 템플릿 생성기**에 가깝습니다. - -1. 개발자가 `serverless.yml`을 작성합니다. -2. `sls deploy` 명령어를 실행하면: - - `serverless.yml`이 AWS의 **CloudFormation Template (JSON/YAML)** 으로 변환됩니다. - - 변환된 템플릿이 S3 배포 버킷(`serverlessdeploymentbucket`)에 업로드됩니다. - - CloudFormation에 "이 템플릿대로 스택을 만들어/업데이트해줘"라고 요청합니다. - -즉, **모든 배포의 실체는 CloudFormation Stack**입니다. - -## 2. 배포(Deploy)의 작동 원리: Change Set - -`sls deploy`를 다시 실행했을 때 "이미 배포된 건데 또 돌려도 되나?"에 대한 답은 **"된다"** 입니다. CloudFormation의 **상태 기반 관리(State Management)** 덕분입니다. - -### 동작 과정 - -1. **현재 상태 확인**: CloudFormation은 현재 AWS에 배포된 리소스 상태를 알고 있습니다. -2. **변경 감지**: 새로 업로드된 템플릿과 현재 상태를 비교합니다. -3. **Change Set 생성**: "무엇이 바뀌었는지" 목록을 만듭니다. - - 예: CloudFront의 `PriceClass`가 `100` -> `All`로 변경됨 - - 예: S3 버킷 설정은 변경 없음 -4. **업데이트 실행**: - - 변경된 리소스만 수정(Update)합니다. (예: CloudFront 설정 변경) - - 변경 없는 리소스는 건드리지 않습니다. (예: S3 파일 보존) - - 더 이상 필요 없는 리소스는 삭제(Delete)합니다. - -따라서 배포 명령어를 여러 번 실행해도 안전하며, 변경사항이 없으면 "No changes to deploy"라고 뜨고 끝납니다. - -## 3. 리소스(Resources) 섹션 - -`serverless.yml`의 `resources` 섹션은 CloudFormation 문법을 그대로 사용합니다. - -```yaml -resources: - Resources: - MyBucket: - Type: AWS::S3::Bucket # CloudFormation 리소스 타입 - Properties: - BucketName: my-bucket -``` - -- **Type**: 만들고 싶은 AWS 리소스 종류 (S3, DynamoDB, IAM Role 등) -- **Properties**: 해당 리소스의 구체적인 설정 -- **CloudFormation 문서**: 구글에 `AWS::S3::Bucket` 등으로 검색하면 나오는 공식 문서의 프로퍼티를 그대로 사용할 수 있습니다. - -## 4. 커스텀 에러 트러블슈팅 - -### "Invalid request provided: Exactly one of..." - -- **원인**: CloudFormation이 리소스를 생성할 때 논리적으로 말이 안 되는 설정을 발견하고 거부한 것입니다. -- **예시**: CloudFront Origin 설정에 `S3OriginConfig`(REST API용)와 `CustomOriginConfig`(웹사이트용)를 동시에 넣었을 때. - -### "Stack ... failed to deploy" - -- Serverless Framework 오류가 아니라, **CloudFormation Stack 업데이트가 실패**한 것입니다. -- AWS Console > CloudFormation > 해당 스택 > **Events** 탭을 보면 정확히 어떤 리소스 생성에 실패했는지(빨간색 글씨) 상세 로그를 볼 수 있습니다. - -## 5. 배포 버킷 vs 서비스 버킷 - -`serverless.yml` 하나로 배포했는데 S3 버킷이 여러 개 생기는 이유: - -1. **Deployment Bucket** (`...-serverlessdeploymentbucket-...`) - - **용도**: 배포 "과정"을 위한 임시 저장소 - - **내용물**: 변환된 CloudFormation 템플릿 파일, Lambda zip 파일 등 - - Serverless Framework가 자동으로 생성하고 관리합니다. - -2. **Service Bucket** (`my-commit-web-sb-dev`) - - **용도**: 실제 우리가 만든 서비스(웹사이트)를 위한 저장소 - - **내용물**: `index.html`, `bundle.js`, 이미지 등 - - 우리가 `resources` 섹션에서 명시적으로 정의한 리소스입니다. - -## 6. CloudFront 배포 소요 시간과 CLI 대기 원리 - -`PriceClass` 변경이나 커스텀 도메인 설정 등 CloudFront 관련 변경사항이 포함된 배포는 시간이 매우 오래 걸릴 수 있습니다 (5분 ~ 20분). - -### 1) 왜 오래 걸리는가? (Stabilization) - -CloudFormation은 단순히 리소스 생성 요청만 보내고 끝내는 것이 아니라, 해당 리소스가 **"완전히 사용 가능한 상태(Stabilized)"**가 될 때까지 기다립니다. - -- CloudFront의 경우: "전 세계 수백 개의 엣지 로케이션에 설정이 모두 전파 완료됨" 상태가 되어야 "배포 완료"로 간주합니다. -- 물리적으로 전 세계 서버에 복제되는 시간이 필요하므로 오래 걸립니다. - -### 2) 왜 터미널(CLI)이 멈춰 있는가? (Polling) - -Serverless CLI(`sls deploy`)는 CloudFormation Stack의 상태를 주기적으로 확인(Polling)합니다. - -- CLI: "다 됐어?" -- AWS: "아직 진행 중(UPDATE_IN_PROGRESS)" -- ... (5분 경과) ... -- CLI: "다 됐어?" -- AWS: "완료됨(UPDATE_COMPLETE)" -- CLI: "성공!" (종료) - -**결론**: 터미널이 멈춘 것처럼 보이는 것은 **CloudFormation이 리소스 안정화를 기다리고 있고, CLI는 그 결과를 기다리고 있기 때문**입니다. 강제로 종료(Ctrl+C)해도 AWS 내부에서는 작업이 계속 진행되지만, 배포 성공 여부를 확인하기 위해 기다리는 것을 권장합니다. diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md deleted file mode 100644 index 0ae04d5..0000000 --- a/docs/DEPLOYMENT.md +++ /dev/null @@ -1,215 +0,0 @@ -# My Commit 배포 가이드 - -## 1) 수동 배포 시나리오 - -1. AWS CLI 인증 설정 - -```bash -aws configure -# 필요 시 멀티 프로필 구성 후 원하는 프로필 사용: -# export AWS_PROFILE=my-aws-profile -``` - -2. 루트에 `.env.production` 파일 생성 - -```bash -cp .env.production.example .env.production -``` - -예시: - -```bash -JWT_SECRET=your-fixed-secret - -WEB_DOMAIN_NAME=my-commit.com -ACM_CERTIFICATE_ARN=arn:aws:acm:us-east-1:ACCOUNT_ID:certificate/xxxx -``` - -### 1-1) 최초 배포 (새 도메인/새 환경) - -1. `.env.production`에 `WEB_DOMAIN_NAME` 먼저 설정 - -2. ACM 생성 + DNS 검증 자동화(권장, Porkbun): - -```bash -pnpm run create:aws:acm:prod:porkbun -# ACM DNS 검증/발급 대기 시간은 보통 약 1분 30초 내외(환경에 따라 더 길 수 있음) -``` - -3. 출력된 박스의 `Copy line` 값을 `.env.production`의 `ACM_CERTIFICATE_ARN=`에 그대로 반영 - -4. 첫 배포 실행 - -```bash -pnpm install -pnpm run deploy:prod:porkbun -``` - -### 1-2) 후속 배포 (코드 변경 배포) - -기본적으로 DNS가 이미 맞다면 아래만 실행: - -```bash -pnpm run deploy:prod -``` - -DNS 변경(도메인 이전/레코드 재동기화)이 필요한 경우만: - -```bash -pnpm run deploy:prod:porkbun -# DNS만 별도 반영할 때 (CloudFront 주소는 스택 Output에서 자동 조회) -pnpm run dns:sync:porkbun -- prod -``` - -dev는 동일하게 `:dev` 명령으로 대응: - -```bash -pnpm run create:aws:acm:dev:porkbun -pnpm run deploy:dev -# 필요 시 pnpm run deploy:dev:porkbun -``` - -주의: - -- 간결한 env 관리를 위해 배포는 저장소 루트에서만 허용됩니다. (`apps/api`, `apps/web` 직접 배포 차단) - -`export`가 필요 없는 이유: - -- `scripts/deploy.sh`가 루트 `.env` 계열 파일을 자동 로드합니다. -- 로드 순서: `.env` -> `.env.local` -> `.env.` -> `.env..local` -- stage 매핑: `prod -> production`, `dev -> development` -- 나중 파일 값이 앞선 파일 값을 덮어씁니다. - -## 2) 배포 변수 표 - -### 2-1. 수동 입력 변수 - -| 변수 | 필수 | 설명 | -| --------------------- | ------ | ----------------------------------- | -| `JWT_SECRET` | 예 | JWT 서명 키 (배포 간 동일 값 유지) | -| `WEB_DOMAIN_NAME` | 예 | Web 커스텀 도메인 (`my-commit.com`) | -| `ACM_CERTIFICATE_ARN` | 예 | `us-east-1` ACM 인증서 ARN | -| `AWS_REGION` | 아니오 | 기본값 `ap-northeast-2` | -| `AWS_PROFILE` | 아니오 | 미지정 시 AWS CLI 기본 profile 사용 | -| `UPDATE_DNS_RECORD` | 아니오 | `false`(기본). `true`일 때만 DNS 동기화 수행 | -| `DNS_PROVIDER` | 아니오 | `none`(기본) 또는 `porkbun` | - -### 2-2. 자동 계산 변수 (입력 금지) - -| 변수 | 계산 방식 | 용도 | -| -------------- | ----------------------------------------------------- | ----------------- | -| `WEB_ORIGIN` | `https://WEB_DOMAIN_NAME` | API CORS | -| `VITE_API_URL` | `-my-commit-api` 스택의 `HttpApiUrl` Output 조회 | Web 빌드 환경변수 | - -### 2-3. Porkbun DNS 자동 동기화 변수 (`UPDATE_DNS_RECORD=true` + `DNS_PROVIDER=porkbun`) - -| 변수 | 필수 | 설명 | -| -------------------------- | ------ | -------------------------------------------------------------------- | -| `PORKBUN_API_KEY` | 예 | Porkbun API Key | -| `PORKBUN_SECRET_API_KEY` | 예 | Porkbun Secret API Key | -| `PORKBUN_ROOT_DOMAIN` | 아니오 | 루트 도메인(미지정 시 `WEB_DOMAIN_NAME`에서 단순 추론, 권장: 명시) | -| `PORKBUN_RECORD_TYPE` | 아니오 | 기본: apex=`ALIAS`, subdomain=`CNAME` | -| `PORKBUN_TTL` | 아니오 | 기본: `600` | -| `PORKBUN_DRY_RUN` | 아니오 | `true`면 API 호출 없이 변경 계획만 출력 | - -필수 사전 설정: -- Porkbun 도메인 관리 화면에서 대상 도메인(`my-commit.com`)의 `API Access`를 먼저 활성화(Opt-in)해야 합니다. - -## 3) 커스텀 도메인 (필수) - -1. `us-east-1` ACM에서 `my-commit.com` 인증서 발급 + DNS CNAME 검증 완료 -2. `.env.production`(prod) 또는 `.env.development`(dev)에 `WEB_DOMAIN_NAME`, `ACM_CERTIFICATE_ARN` 추가 -3. `pnpm run deploy:prod` 또는 `pnpm run deploy:dev` 실행 - - Porkbun DNS 자동 동기화까지 원하면 `pnpm run deploy:prod:porkbun` 사용 -4. DNS 구매처에서 루트 도메인을 CloudFront 도메인으로 ALIAS/ANAME(flattening) 연결 - -참고: - -- 이 프로젝트는 CloudFront `Add domain`을 IaC로 처리하므로 콘솔 수동 입력이 필수가 아닙니다. -- `deploy:*:porkbun` 스크립트는 `UPDATE_DNS_RECORD=true`를 자동으로 설정해 DNS까지 반영합니다. -- Porkbun API를 쓰는 경우, 도메인별 `API Access`가 꺼져 있으면 `Domain is not opted in to API access` 오류가 발생합니다. -- API Gateway 커스텀 도메인은 현재 기본 배포 범위 밖입니다. - -## 4) 배포 실패 시 가능한 원인 - -- `JWT_SECRET`, `WEB_DOMAIN_NAME`, `ACM_CERTIFICATE_ARN` 누락 -- ACM 인증서 조건 불일치 (`us-east-1` 아님, DNS 검증 미완료, 도메인 불일치) -- 동일 도메인을 다른 CloudFront 배포에서 이미 사용 중 (`CNAMEAlreadyExists`) - -## 4-1) ACM 자동 생성 (선택) - -```bash -# prod 기준: ACM 생성 + Porkbun DNS 검증 레코드 자동 반영 + 발급 대기 -pnpm run create:aws:acm:prod:porkbun -# ACM DNS 검증/발급 대기 시간은 보통 약 1분 30초 내외(환경에 따라 더 길 수 있음) - -# 수동 DNS를 원하면 -pnpm run create:aws:acm -- prod --domain my-commit.com -``` - -## 4-2) 전체 제거/초기화 (선택) - -```bash -# prod 스택 + ACM 제거 (기본) -pnpm run undeploy:prod - -# dev 스택 + ACM 제거 (기본) -pnpm run undeploy:dev - -# ACM 유지가 필요하면 -pnpm run undeploy -- prod --keep-acm -``` - -## 5) 자동 배포 (GitHub Actions) - -`main` 브랜치 push 시 `.github/workflows/deploy.yml`가 실행됩니다. - -필수 Secrets: - -- `AWS_ACCESS_KEY_ID` -- `AWS_SECRET_ACCESS_KEY` -- `JWT_SECRET` -- `WEB_DOMAIN_NAME` -- `ACM_CERTIFICATE_ARN` - -## 6) 배포 결과 확인 - -API URL: - -```bash -aws cloudformation describe-stacks \ - --stack-name prod-my-commit-api \ - --region ap-northeast-2 \ - --query 'Stacks[0].Outputs[?OutputKey==`HttpApiUrl`].OutputValue' \ - --output text - -# profile 지정 시 (env 방식) -AWS_PROFILE=my-aws-profile aws cloudformation describe-stacks \ - --stack-name prod-my-commit-api \ - --region ap-northeast-2 \ - --query 'Stacks[0].Outputs[?OutputKey==`HttpApiUrl`].OutputValue' \ - --output text -``` - -Web URL: - -```bash -aws cloudformation describe-stacks \ - --stack-name prod-my-commit-web \ - --region ap-northeast-2 \ - --query 'Stacks[0].Outputs[?OutputKey==`CloudFrontURL`].OutputValue' \ - --output text - -# profile 지정 시 (env 방식) -AWS_PROFILE=my-aws-profile aws cloudformation describe-stacks \ - --stack-name prod-my-commit-web \ - --region ap-northeast-2 \ - --query 'Stacks[0].Outputs[?OutputKey==`CloudFrontURL`].OutputValue' \ - --output text -``` - -Porkbun DNS 조회: - -```bash -pnpm run dns:check:porkbun -- prod --name my-commit.com -``` diff --git a/docs/EDITOR_SYNC_REDESIGN.md b/docs/EDITOR_SYNC_REDESIGN.md index 72c517f..56b5949 100644 --- a/docs/EDITOR_SYNC_REDESIGN.md +++ b/docs/EDITOR_SYNC_REDESIGN.md @@ -1,5 +1,7 @@ # 에디터 동기화 안정화 재설계 제안 +> 상태: 일부 내용은 방안 검토 당시 문서이며, 2026-06-21 기준 최종 채택안은 `docs/SYNC_POLICY_COMPARISON.md`의 "방안 3. Append-only revision + current pointer" 및 ADR 004입니다. 서버는 AWS/DynamoDB가 아니라 NestJS + host MySQL transaction 기준입니다. + ## 1. 문제 정의 백엔드 동기화 도입 이후 에디터가 불안정해진 핵심 원인은 **사용자 편집, 하이드레이션, 복원, 타이머 갱신이 하나의 액션/경로로 섞여** 저장 및 서버 동기화가 무차별적으로 발생하기 때문입니다. @@ -31,7 +33,7 @@ 1. **데이터 유실 방지 우선** 2. **도메인 경계 명확화** (editor/store vs storage payload) 3. **동기화 경로 단일화** (명확한 상태기계 + 큐) -4. **서버 CAS 기반의 최종 일관성 보장** +4. **서버 revision/current pointer 기반의 최종 일관성 보장** --- @@ -73,18 +75,19 @@ - hydrate 진행 중 업로드 게이트(`hydrating=true`) 적용 - 동일 날짜에 대해 동시 다발 요청 금지(serialize) -### 4.4 서버 CAS(Compare-And-Set) 필수화 +### 4.4 서버 append-only revision 저장 필수화 -클라이언트는 저장 시 `expectedContentHash`(또는 `expectedVersion`)를 전달합니다. +클라이언트는 저장 시 `baseRevisionId` 또는 기존 호환용 `parentHash`를 전달합니다. -- 일치 시 저장 성공 -- 불일치 시 `409 Conflict` 반환 +- 서버는 새 content를 항상 revision으로 저장 +- base가 current와 일치하면 current pointer 승격 +- base가 stale이면 current pointer는 유지하고 revision만 보존 -백엔드는 조건부 쓰기(예: DynamoDB `ConditionExpression`)로 현재 head 검증 후에만 업데이트해야 합니다. +백엔드는 MySQL transaction 안에서 현재 pointer를 확인하고, 새 revision insert와 current pointer update를 한 단위로 처리해야 합니다. ### 4.5 충돌 처리 규약 -409 응답 시: +stale 저장 응답 시: 1. 서버 최신본 fetch 2. 로컬 스냅샷과 비교 UI 노출 @@ -116,8 +119,8 @@ ### Phase 2 (Backend 정합성 강화) -- CAS API 추가 (`expectedContentHash`/`expectedVersion`) -- 409 conflict 표준 응답 스키마 확정 +- revision append API 추가 (`baseRevisionId`/`parentHash`) +- stale 저장 표준 응답 스키마 확정 - 클라이언트 409 핸들링 연결 ### Phase 3 (운영 신뢰성 강화) @@ -145,4 +148,3 @@ 2. 데이터 유실 가능성 실질적 감소(클라이언트 + 서버 이중 보호) 3. 로그인 후 편집에 대한 심리적 불안 해소 4. 향후 기능 확장(멀티기기/히스토리/머지) 기반 확보 - diff --git a/docs/LOCAL_ENV_TECH_COMPARISON.md b/docs/LOCAL_ENV_TECH_COMPARISON.md deleted file mode 100644 index 4fc3c76..0000000 --- a/docs/LOCAL_ENV_TECH_COMPARISON.md +++ /dev/null @@ -1,64 +0,0 @@ -# Local Environment Tech Comparison - -## 1. Repository Facts -- API deploy config: `apps/api/serverless.yml` -- Web deploy config: `apps/web/serverless.yml` -- API Lambda count: 1 (`functions.api`, `httpApi: "*"`) -- API local command A: `pnpm --filter my-commit-api dev:local` -> `tsx watch src/index.ts` -- API local command B: `pnpm --filter my-commit-api sls:offline` -> `pnpm build && serverless offline --stage local` - -## 2. Tool Matrix (Objective) - -| Item | serverless-offline | AWS SAM CLI | Lambda RIE | -| --- | --- | --- | --- | -| Maintainer | Serverless plugin | AWS | AWS | -| Primary config input | `serverless.yml` | `template.yaml` (`AWS::Serverless::*`) | Container/runtime entry setup | -| Local HTTP server built-in | Yes | Yes (`sam local start-api`) | No | -| API Gateway event shaping | Yes | Yes | No | -| Single function invoke | Via framework route/test | Yes (`sam local invoke`) | Yes (runtime API level) | -| Docker required for local run | No (default) | Yes | Yes (typical usage) | -| Cloud deploy capability | Yes (`serverless deploy`) | Yes (`sam deploy`) | No | -| Direct fit for this repo now | Yes | No (template migration needed) | No (manual wiring needed) | - -## 3. SAM Commands (Exact Scope) -- `sam local start-api` - - Input: SAM template + HTTP request - - Output: local HTTP response - - Behavior: converts HTTP request to API Gateway-style event and invokes local Lambda container -- `sam local invoke` - - Input: function logical ID + event JSON - - Output: one function execution result - - Behavior: no HTTP router; one-shot invocation -- `sam validate` - - Input: SAM template - - Output: template validity result - - Behavior: static validation only (no function run) -- `sam validate --lint` - - Input: SAM template - - Output: additional rule-based findings (`cfn-lint`) - - Behavior: stricter static checks than plain `validate` -- `sam sync` - - Input: local changes + target stack - - Output: updated cloud stack/resources - - Behavior: cloud synchronization command (not local emulation) - -## 4. Local vs Cloud Coverage - -| Check Item | `dev:local` (`tsx`) | `sls:offline` | AWS dev deploy | -| --- | --- | --- | --- | -| Uses Lambda handler entry (`dist/index.handler`) | No | Yes | Yes | -| Uses API Gateway-like event pipeline | No | Yes | Yes | -| Uses local DynamoDB endpoint (`IS_OFFLINE=true`) | Yes | Yes (`--stage local`) | No | -| Real IAM policy enforcement | No | No | Yes | -| Real CloudFront/API Gateway managed service path | No | No | Yes | - -## 5. Environment Variable Handling -- API - - Source at deploy: `${env:...}` in `apps/api/serverless.yml` - - Runtime read: `process.env` in Lambda - - Change reflection: requires redeploy -- Web - - Source at build: `VITE_*` env (file or CI env) - - Runtime in browser: baked into build artifact -- `JWT_SECRET` - - Required and fixed across deployments (`apps/api/src/index.ts`) diff --git a/docs/SYNC_POLICY_COMPARISON.md b/docs/SYNC_POLICY_COMPARISON.md new file mode 100644 index 0000000..217fd4d --- /dev/null +++ b/docs/SYNC_POLICY_COMPARISON.md @@ -0,0 +1,667 @@ +# 동기화 정책 비교: 현재 구현과 단순화된 To-be + +## 전제 + +이 문서는 다음 전제를 기준으로 현재 동기화 정책과 To-be 정책을 비교한다. + +1. 동시 수정 충돌 해결은 지금 필요 없다. +2. 정보 손실은 없어야 한다. 서버에 올라간 개별 버전은 완전한 원문으로 모두 보관되어야 한다. +3. 동시에 여러 곳에서 같은 날짜를 편집하는 일은 없다고 본다. +4. 핵심 위험은 로딩 실패, stale 로컬 캐시, 신규 디바이스 초기 fetch 실패 같은 상황에서 오래된 내용이 최신 서버 내용을 덮어쓰는 것이다. +5. 버그성 저장이 발생하더라도 기존 서버 데이터와 새로 작성된 데이터가 모두 남아야 한다. + +## 현재 정책 요약 + +현재 프론트엔드는 날짜별 localStorage wrapper에 다음 메타데이터를 저장한다. + +- `content` +- `contentHash` +- `parentHash` +- `localUpdatedAt` + +사용자가 편집하면 `updateRawLog`가 발생하고, middleware가 즉시 localStorage에 저장한 뒤 300ms debounce 후 서버에 저장한다. + +날짜 이동, 로그인, 현재 날짜 fetch 트리거가 발생하면 localStorage를 먼저 hydrate하고 서버 데이터를 fetch한다. 이후 `contentHash`/`parentHash`를 비교해 다음 네 가지 상태 중 하나로 판단한다. + +- `NO_CONFLICT_SAME` +- `FAST_FORWARD` +- `LOCAL_AHEAD` +- `CONFLICT_DIVERGED` + +백엔드는 `saveLog`에서 기존 current log가 있으면 `log-backups` 테이블에 기존 current 내용을 저장한 뒤 current row를 새 내용으로 덮어쓴다. 즉 서버는 overwrite 전에 직전 버전을 백업한다. + +관련 구현: + +- `apps/web/src/store/RawLogStorageSyncMiddleware.ts` +- `apps/web/src/utils/StorageUtil.ts` +- `apps/web/src/utils/ConflictDetector.ts` +- `apps/api/src/logs.ts` +- `apps/web/src/features/dataManagement/backupService.ts` + +## 현재 정책의 장점 + +- 사용자 편집과 서버 저장 경로가 이미 존재한다. +- 서버 저장 전 기존 current를 백업하므로, 정상 저장 경로를 타면 직전 서버 버전은 남는다. +- localStorage wrapper가 있어 로컬 수정 chain을 어느 정도 추적할 수 있다. +- 최근 수정으로 hydrate/refresh/import에서 발생하던 일부 오염과 잘못된 parent metadata 문제는 완화되었다. + +## 현재 정책의 문제 + +### 1. 문제 정의보다 정책이 복잡하다 + +현재 구조는 Git식 `parentHash`와 충돌 감지에 가깝다. 하지만 실제 요구는 동시 편집 병합이 아니라, stale 클라이언트가 최신 데이터를 잃게 만들지 않는 것이다. + +동시 편집을 다루지 않는다면 `FAST_FORWARD`, `LOCAL_AHEAD`, `CONFLICT_DIVERGED` 같은 클라이언트 추론은 핵심 요구보다 복잡하다. + +### 2. 서버가 최종 방어선이 아니다 + +프론트엔드는 `parentHash`를 보내지만, 백엔드는 이를 검증하지 않는다. 서버는 현재 row를 읽고 백업한 뒤 새 row로 덮어쓴다. + +따라서 클라이언트가 stale content를 보내도 서버는 이를 current로 승격한다. 기존 current는 backup에 남으므로 완전 유실은 막을 수 있지만, 최신 current가 오래된 내용으로 바뀌는 것은 막지 못한다. + +### 3. 버전 보관 모델이 current + backups로 나뉘어 있다 + +현재 최신본은 `logs` 테이블에 있고, 이전본은 `log-backups` 테이블에 있다. 이 모델도 운영 가능하지만, "모든 개별 버전은 완전한 원문으로 저장된다"는 정책을 코드에서 이해하기 어렵다. + +더 명확한 모델은 "모든 저장은 revision을 append하고, current는 특정 revision을 가리키는 pointer"이다. + +### 4. 로컬 캐시와 서버 캐시가 같은 저장소 키를 공유한다 + +localStorage의 날짜 키가 에디터 표시, 로컬 draft, 서버에서 받아온 snapshot 역할을 섞어 가진다. 이 때문에 로딩 실패와 사용자 편집 경계가 흐려진다. + +To-be에서는 최소한 개념적으로 다음을 분리해야 한다. + +- 서버에서 확인된 현재 revision cache +- 아직 서버에 안전하게 승격되지 않은 local draft + +### 5. 백업 import가 destructive replace 성격을 가진다 + +현재 import는 적용 전 날짜 키를 모두 지우고 backup 내용을 다시 저장한다. 로컬에서는 이해 가능하지만, 서버 동기화와 결합되면 "복구"가 곧 "여러 날짜 current 교체"가 된다. + +To-be에서는 import도 revision append로 취급해야 한다. 기존 서버 current를 지우지 않고, import된 각 날짜 content를 새 revision으로 남긴 뒤 명시적으로 current 승격 여부를 결정해야 한다. + +## To-be 정책: Append-only Revision + Guarded Promotion + +권장 To-be는 CAS 기반 충돌 해결 UI가 아니라, 더 단순한 **append-only revision + guarded promotion** 모델이다. + +핵심은 두 가지다. + +1. 저장 요청은 어떤 경우에도 완전한 원문 revision으로 서버에 남긴다. +2. 단, 그 revision을 current로 승격할지는 서버가 `baseRevisionId`로 판단한다. + +## To-be 데이터 모델 + +### Current + +날짜별 현재 상태를 가리키는 pointer. + +```ts +type CurrentLog = { + userId: string; + date: string; + currentRevisionId: string; + updatedAt: string; +}; +``` + +### Revision + +모든 저장 시도는 완전한 원문 snapshot으로 append된다. + +```ts +type LogRevision = { + userId: string; + date: string; + revisionId: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + createdAt: string; + source: 'edit' | 'restore' | 'import' | 'draft-save'; + promoted: boolean; +}; +``` + +`revisionId`는 ULID 또는 timestamp + random suffix처럼 정렬 가능하고 충돌 가능성이 낮은 ID를 쓴다. + +## To-be 저장 규칙 + +### 정상 저장 + +클라이언트가 현재 서버 revision을 로드한 뒤 편집한다. + +1. 클라이언트가 `content`, `baseRevisionId`, `clientMutationId`를 보낸다. +2. 서버는 현재 `currentRevisionId`와 `baseRevisionId`를 비교한다. +3. 같으면 새 revision을 append하고 current pointer를 새 revision으로 갱신한다. +4. 응답은 새 `revisionId`와 `promoted: true`를 반환한다. + +### 로딩 실패 또는 stale 저장 + +신규 디바이스가 서버 current를 제대로 로드하지 못했거나, 오래된 base로 저장한다. + +1. 클라이언트가 `baseRevisionId: null` 또는 오래된 revision id로 저장한다. +2. 서버는 새 revision을 append한다. +3. 서버는 current pointer를 바꾸지 않는다. +4. 응답은 `promoted: false`, `reason: 'STALE_BASE'`를 반환한다. +5. 클라이언트는 서버 current를 다시 fetch하고, 사용자가 작성한 내용은 "보존된 미승격 revision"으로 표시한다. + +이 정책이면 오래된 내용이 최신 current를 덮지 않고, 사용자가 새로 쓴 내용도 서버에 남는다. + +### 같은 내용 저장 + +현재 revision의 `contentHash`와 요청 `contentHash`가 같으면 새 revision을 만들지 않고 no-op으로 처리해도 된다. + +다만 "모든 저장 시도"까지 감사 로그로 남기고 싶다면 별도 event log를 둔다. 지금 요구는 개별 내용 버전 보존이므로 동일 content 중복 revision은 만들지 않아도 된다. + +## 현재 정책 vs To-be 비교 + +| 항목 | 현재 정책 | To-be 정책 | +| --- | --- | --- | +| 핵심 모델 | current row + backup row + 클라이언트 parentHash | append-only revision + current pointer | +| 주요 목적 | 충돌 감지/fast-forward/local-ahead 추론 | 로딩 실패와 stale 저장으로부터 current 보호 | +| 서버 저장 | 기존 current를 backup 후 current overwrite | 새 revision append 후 조건부 current promotion | +| stale 저장 | current로 승격될 수 있음 | revision은 보존하되 current 승격 차단 | +| 동시 편집 처리 | 클라이언트가 hash 관계로 추정 | 지금은 목표 밖. stale base는 단순히 미승격 처리 | +| 정보 보존 | 정상 저장 경로에서는 직전 current 보존 | 모든 저장 content를 revision으로 보존 | +| 로컬 상태 | 날짜 key 하나가 cache/draft 역할을 섞음 | server cache와 local draft 개념 분리 | +| 복원 | 과거 backup content를 `updateRawLog`로 재저장 | 과거 revision content로 새 revision append | +| import | 기존 로컬 로그 삭제 후 backup 적용, 로그인 시 bulk sync | import content를 revision append. current 승격은 명시 정책 적용 | +| 코드 복잡도 | `ConflictDetector`, parent chain, conflict dialog | revision id, loaded gate, promotion result | +| 사용자 경험 | 충돌 dialog가 중심 | 로딩 완료 전 편집 제한, stale 저장 시 보존 안내 | + +## 구현 가능한 방안들 + +아래 방안들은 모두 구현 가능하다. 차이는 "얼마나 빨리 안정화할 것인가", "서버 모델을 얼마나 바꿀 것인가", "데이터 보존을 어디까지 명시적으로 보장할 것인가"이다. + +### 방안 1. 기존 구현 유지 + 국소 안정화 + +현재 구조를 유지한다. + +- 프론트엔드: `contentHash`/`parentHash`/`ConflictDetector` 유지 +- 백엔드: `logs` current row + `log-backups` 유지 +- 저장: 기존 current를 backup에 저장한 뒤 current overwrite +- import/restore: 현재 방식 유지하되, 빈 로그/metadata 오염만 계속 보정 + +필요한 구현: + +- 지금 수정한 내용 유지 +- hydrate, restore, import, storage event가 `updateRawLog` 경로를 잘못 타지 않도록 테스트 추가 +- `log-backups` 조회/복원 UX 개선 +- 서버 backup 생성 실패 시 current overwrite도 실패하도록 트랜잭션 또는 방어 로직 추가 + +장점: + +- 가장 적은 변경으로 갈 수 있다. +- 현재 테스트/코드 대부분을 재사용한다. +- 단기간 안정화에는 좋다. + +단점: + +- stale 저장이 current로 승격되는 문제는 남는다. +- `parentHash`를 서버가 검증하지 않으므로 모델이 정직하지 않다. +- 클라이언트 충돌 추론 로직이 요구사항보다 복잡하다. + +적합한 경우: + +- 당장 배포 리스크를 최소화해야 할 때 +- 데이터 유실 방지는 "직전 current backup" 정도로 충분하다고 판단할 때 + +### 방안 2. 기존 테이블 유지 + Guarded overwrite + +현재 `logs`와 `log-backups` 구조는 유지하되, 저장 API에 `baseContentHash` 또는 `baseVersion`을 추가한다. + +- 정상 base면 current overwrite +- stale base면 current overwrite 금지 +- stale 저장 content는 backup 또는 별도 rejected table에 저장 + +필요한 구현: + +```ts +type SaveLogRequest = { + date: string; + content: string; + contentHash: string; + baseContentHash: string | null; +}; + +type SaveLogResponse = { + success: true; + promoted: boolean; + reason?: 'STALE_BASE'; + current?: LogItem; +}; +``` + +백엔드 동작: + +1. 현재 `logs` row 조회 +2. 요청 content를 먼저 보존용 row로 저장 +3. `baseContentHash === current.contentHash`일 때만 current overwrite +4. 다르면 current 유지 후 `promoted: false` 반환 + +장점: + +- 기존 current table 중심 구조를 크게 유지한다. +- stale overwrite를 막을 수 있다. +- append-only revision 모델보다 migration 부담이 작다. + +단점: + +- "모든 버전"이 어디에 저장되는지 모델이 여전히 애매할 수 있다. +- backup/rejected/draft 성격의 저장소를 잘 설계하지 않으면 다시 복잡해진다. +- current row에 content가 남아 있어 revision pointer 모델보다 덜 명확하다. + +적합한 경우: + +- 구조 개편은 줄이고 stale overwrite만 확실히 막고 싶을 때 +- 현재 API와 UI를 대부분 유지하고 싶을 때 + +### 방안 3. Append-only revision + current pointer + +이 문서의 기본 To-be다. + +- 모든 저장은 `log-revisions`에 완전한 content snapshot으로 append +- `current`는 `currentRevisionId`만 가리킴 +- `baseRevisionId`가 current와 같을 때만 새 revision을 current로 승격 +- stale 저장은 revision으로 남기되 current는 유지 + +필요한 구현: + +```ts +type LogRevision = { + userId: string; + date: string; + revisionId: string; + content: string; + contentHash: string; + baseRevisionId: string | null; + source: 'edit' | 'restore' | 'import'; + promoted: boolean; + createdAt: string; +}; + +type CurrentLog = { + userId: string; + date: string; + currentRevisionId: string; + updatedAt: string; +}; +``` + +장점: + +- 정보 보존 정책이 가장 명확하다. +- stale 저장과 current 승격을 분리할 수 있다. +- 복원/import도 같은 append 모델로 설명된다. +- `parentHash`와 conflict detector를 제거할 근거가 생긴다. + +단점: + +- 서버 데이터 모델 변경이 필요하다. +- 기존 `logs`/`log-backups` 데이터 migration 계획이 필요하다. +- current 조회 시 revision join 또는 2회 조회가 필요할 수 있다. + +적합한 경우: + +- 정보 손실 방지를 최우선 정책으로 명확히 만들고 싶을 때 +- 중장기적으로 가장 이해하기 쉬운 모델을 원할 때 + +### 방안 4. Append-only revision only, current는 최신 promoted revision으로 계산 + +방안 3에서 current pointer table을 제거한다. 날짜별 current는 revision 목록 중 `promoted=true`인 최신 revision으로 계산한다. + +필요한 구현: + +- `log-revisions`만 둔다. +- promoted revision에는 `promotedAt`을 둔다. +- current 조회는 `date`별 `promotedAt` 최신 항목을 query한다. +- stale 저장은 `promoted=false`로 append한다. + +장점: + +- 테이블 수가 줄어든다. +- "저장소는 오직 revision log"라는 모델이 단순하다. +- current pointer와 revision append 사이의 transaction 부담이 줄어든다. + +단점: + +- MySQL에서 "날짜별 최신 promoted revision" 조회 인덱스를 신중히 설계해야 한다. +- promoted revision이 많아질 때 current 조회 비용/복잡도가 늘 수 있다. +- current 변경을 원자적으로 보장하려면 결국 조건부 promoted 처리나 별도 pointer가 다시 필요할 수 있다. + +적합한 경우: + +- 데이터량이 작고, current 조회 최적화보다 저장 모델 단순성이 더 중요할 때 +- 날짜별 revision 수가 많지 않을 것으로 볼 때 + +### 방안 5. Draft-first 모델 + +서버 current를 직접 바꾸는 자동 저장을 없애거나 크게 제한한다. 모든 자동 저장은 draft/revision으로만 저장하고, current 승격은 명시적 조건에서만 한다. + +동작: + +- 타이핑 자동 저장: `draft revision` append +- 서버 current 로딩 완료 전: draft만 저장 +- current 승격: 사용자가 저장 완료 상태를 확인했거나, base가 검증된 경우에만 수행 + +필요한 구현: + +```ts +type DraftRevision = { + userId: string; + date: string; + draftId: string; + content: string; + baseRevisionId: string | null; + createdAt: string; + promotedRevisionId?: string; +}; +``` + +장점: + +- 로딩 이슈로 current가 덮이는 경로를 강하게 줄인다. +- 자동 저장이 항상 보존용이라는 점이 명확하다. +- 신규 디바이스/느린 네트워크에 강하다. + +단점: + +- UX가 바뀐다. "자동 저장됨"과 "현재 버전으로 반영됨"을 구분해야 한다. +- 사용자가 보기에 저장 상태가 복잡해질 수 있다. +- current 승격 타이밍 정책을 따로 정해야 한다. + +적합한 경우: + +- 데이터 보존이 current 최신성보다 더 중요할 때 +- 자동 저장을 안전한 임시 저장소로 격하해도 괜찮을 때 + +### 방안 6. Immutable object snapshot + MySQL metadata + +로그 content 원문은 MinIO 같은 self-host object storage에 immutable snapshot으로 저장하고, MySQL에는 metadata와 current pointer만 둔다. + +동작: + +- 저장 시 content를 object로 업로드 +- MySQL revision metadata row에는 object key, hash, size, source 저장 +- current pointer는 promoted revision id 또는 object key를 가리킴 + +필요한 구현: + +```ts +type LogRevisionMetadata = { + userId: string; + date: string; + revisionId: string; + objectKey: string; + contentHash: string; + contentSize: number; + baseRevisionId: string | null; + promoted: boolean; + createdAt: string; +}; +``` + +장점: + +- 원문 snapshot 보존이 매우 명확하다. +- 큰 로그나 full history export에 유리하다. +- DB row size 제한에서 자유롭다. + +단점: + +- 현재 로그 텍스트 크기에는 과할 가능성이 높다. +- object write와 metadata write의 정합성 처리가 필요하다. +- 운영 리소스가 늘어난다. + +적합한 경우: + +- 로그 원문이 커질 가능성이 크거나 첨부/미디어까지 고려할 때 +- 감사/보존 요건이 강해서 immutable object archive가 필요할 때 + +### 방안 7. Local-first operation log + 서버 snapshot archive + +원문 snapshot이 아니라 사용자 입력 operation을 local queue에 쌓고 서버에 전송한다. 서버는 operation을 받아 snapshot revision을 만든다. + +동작: + +- 클라이언트는 edit operation을 queue에 저장 +- 서버는 operation batch를 받아 새 snapshot revision 생성 +- current 승격은 baseRevisionId 검증 후 수행 + +필요한 구현: + +```ts +type LogOperationBatch = { + date: string; + baseRevisionId: string | null; + operations: Array<{ + operationId: string; + kind: 'replace_all'; + content: string; + createdAt: string; + }>; +}; +``` + +현재 에디터가 textarea 전체 문자열 기반이므로, 실제 operation은 우선 `replace_all` 하나로 시작할 수 있다. + +장점: + +- 오프라인/재시도/idempotency를 체계화하기 좋다. +- `clientMutationId` 기반 중복 요청 방지가 쉽다. +- 나중에 세밀한 edit operation으로 확장 가능하다. + +단점: + +- 지금 요구에는 과할 수 있다. +- queue, retry, idempotency, compaction 정책이 필요하다. +- textarea 전체 content 앱에서는 append-only revision보다 이점이 작다. + +적합한 경우: + +- 오프라인 편집과 재시도 안정성을 중요하게 볼 때 +- 앞으로 여러 입력 source나 자동화된 수정 이벤트가 많아질 때 + +## 방안별 간단 평가 + +| 방안 | 변경량 | 정보 보존 명확성 | stale overwrite 방지 | 모델 단순성 | 현재 맥락 적합도 | +| --- | --- | --- | --- | --- | --- | +| 1. 기존 유지 + 국소 안정화 | 낮음 | 중간 | 낮음 | 중간 | 단기 응급용 | +| 2. 기존 테이블 + guarded overwrite | 중간 | 중간 | 높음 | 중간 | 점진 개선용 | +| 3. revision + current pointer | 중간-높음 | 높음 | 높음 | 높음 | 가장 균형적 | +| 4. revision only | 중간 | 높음 | 중간-높음 | 중간 | 데이터량 작을 때 | +| 5. draft-first | 중간-높음 | 높음 | 매우 높음 | 중간 | 보수적 UX 가능할 때 | +| 6. immutable object snapshot | 높음 | 매우 높음 | 높음 | 낮음 | 현재는 과할 수 있음 | +| 7. operation log + snapshot | 높음 | 높음 | 높음 | 낮음 | 오프라인 중심일 때 | + +내 기준의 1차 판단은 다음과 같다. + +- 가장 빨리 안정화하려면 방안 2. +- 장기적으로 가장 명확하게 만들려면 방안 3. +- 사용자 입력을 절대 current에 바로 반영하지 않는 보수 정책이 필요하면 방안 5. +- 방안 6과 7은 지금 요구에는 YAGNI일 가능성이 높지만, 보존/오프라인 요구가 커지면 선택지가 된다. + +## To-be 클라이언트 정책 + +### 1. 로딩 완료 전 저장 금지 + +로그인 상태에서 날짜를 열면 서버 current fetch가 끝나기 전까지 해당 날짜의 서버 저장을 금지한다. + +UI 선택지는 두 가지다. + +- 더 안전한 방식: 에디터를 read-only/loading 상태로 둔다. +- 덜 답답한 방식: 편집은 허용하되 local draft로만 저장하고, 서버 current 로드 전에는 upload하지 않는다. + +현재 요구 기준으로는 첫 번째가 더 명확하다. + +### 2. localStorage key 역할 분리 + +권장 키: + +- `server-log:${date}`: 마지막으로 서버에서 확인한 current revision snapshot +- `draft-log:${date}`: 아직 서버에 승격되지 않은 사용자 draft +- `my-commit:current-date`: 현재 날짜 + +에디터는 다음 우선순위로 표시한다. + +1. 미전송 draft가 있으면 draft +2. 없으면 server cache +3. 둘 다 없으면 빈 문자열 + +### 3. 저장 요청은 revision 기준으로 보낸다 + +```ts +type SaveLogRequest = { + date: string; + content: string; + baseRevisionId: string | null; + clientMutationId: string; + source: 'edit' | 'restore' | 'import'; +}; +``` + +`parentHash`는 제거한다. `contentHash`는 서버나 클라이언트가 계산해 중복 저장 방지에만 쓴다. + +### 4. 저장 응답은 승격 여부를 명확히 반환한다 + +```ts +type SaveLogResponse = { + revisionId: string; + currentRevisionId: string; + promoted: boolean; + reason?: 'STALE_BASE' | 'DUPLICATE_CONTENT'; +}; +``` + +`promoted: true`면 draft를 지우고 server cache를 갱신한다. + +`promoted: false`면 draft는 "서버에 보존됨" 상태로 남기고, current를 다시 fetch한다. + +## To-be 서버 정책 + +### 1. 저장은 transaction으로 처리한다 + +서버 저장은 다음을 한 단위로 처리한다. + +1. 현재 pointer 조회 +2. 새 revision append +3. `baseRevisionId === currentRevisionId`일 때만 current pointer 갱신 + +MySQL에서는 transaction 안에서 현재 pointer row를 잠그고, 새 revision을 먼저 insert한 뒤, base가 current와 일치할 때만 current pointer를 갱신한다. 단, "stale 저장도 revision으로 남겨야 한다"는 요구 때문에 단순 CAS reject만으로 끝내면 안 된다. + +### 2. current overwrite는 직접 하지 않는다 + +current row는 content를 직접 들고 있지 않고 current revision id만 가진다. content는 revision table에서 읽는다. + +현재 구조를 크게 바꾸기 어렵다면 중간 단계로 `logs` row에 latest content를 유지하되, 저장 요청마다 먼저 `log-revisions`에 새 content를 append하고, 조건이 맞을 때만 `logs` row를 갱신한다. + +### 3. 복원은 과거 revision을 current로 직접 되돌리지 않는다 + +복원은 새 revision을 만든다. + +예: + +- r10: 현재 내용 +- r3: 과거 내용 +- 사용자가 r3 복원 선택 +- r11: r3의 content를 가진 새 revision, source=`restore` +- current pointer -> r11 + +이러면 복원 작업 자체도 되돌릴 수 있다. + +## 백업/import To-be + +### export + +export는 두 종류로 나눈다. + +- current export: 날짜별 현재 content만 내보냄 +- full history export: 날짜별 모든 revision을 내보냄 + +현재 수동 백업 버튼은 current export로 유지해도 된다. "절대 정보 손실 없음" 보장은 서버 revision history가 담당한다. + +### import + +import는 기존 데이터를 지우지 않는다. + +기본 정책: + +1. import 파일의 각 날짜 content를 revision으로 append한다. +2. 서버 current를 이미 로드했고 사용자가 명시적으로 "현재값으로 복구"를 선택한 경우에만 promoted revision으로 만든다. +3. 그렇지 않으면 `source='import'`, `promoted=false`로 저장하고 사용자가 history에서 선택하게 한다. + +이렇게 하면 잘못된 백업 파일을 import해도 기존 current는 보존된다. + +## 기존 설계 문서와의 차이 + +`docs/EDITOR_SYNC_REDESIGN.md`는 서버 CAS와 409 conflict를 중심으로 한다. 그 설계는 다중 디바이스 동시 편집까지 고려하면 더 정석적이다. + +하지만 현재 전제에서는 CAS reject보다 append-only guarded promotion이 더 맞다. + +- CAS reject만 하면 클라이언트가 가진 새 content가 서버에 안 남을 수 있다. +- conflict dialog는 지금 요구에 비해 과하다. +- 필요한 것은 "같으면 승격, 다르면 보존만 하고 최신 current는 유지"이다. + +즉 To-be는 CAS를 완전히 버리는 것이 아니라, CAS를 "저장 거부"가 아니라 "current 승격 조건"으로만 사용한다. + +## 제거하거나 축소할 수 있는 것 + +To-be로 가면 다음은 제거 또는 축소 가능하다. + +- `parentHash` +- `ConflictDetector` +- `FAST_FORWARD` / `LOCAL_AHEAD` / `CONFLICT_DIVERGED` +- 충돌 해결 모달 중심 UX +- 클라이언트의 Git식 parent chain 테스트 + +대신 다음이 필요하다. + +- `revisionId` +- `baseRevisionId` +- `promoted` 상태 +- 로딩 완료 전 edit/save gate +- revision history 조회/복원 UI + +## 단계적 이전안 + +### Phase 1: 정책 경계 정리 + +- `parentHash` 용어를 신규 설계에서 사용하지 않는다. +- 클라이언트 상태를 `serverLoaded`, `serverRevisionId`, `draftContent` 중심으로 재정의한다. +- 로딩 완료 전 서버 upload를 금지한다. +- hydrate는 저장을 트리거하지 않는다는 규칙을 테스트로 고정한다. + +### Phase 2: 서버 revision append 도입 + +- `log-revisions` 저장소를 추가한다. +- 기존 `saveLog`는 저장 요청마다 revision을 먼저 append한다. +- current 갱신은 `baseRevisionId`가 현재 revision과 같을 때만 수행한다. +- stale 저장은 `promoted=false` revision으로 남긴다. + +### Phase 3: 클라이언트 단순화 + +- `ConflictDetector` 사용을 제거한다. +- `saveLogToServer` 요청/응답을 revision id 기반으로 바꾼다. +- 저장 응답의 `promoted` 값으로 draft 정리 여부를 결정한다. + +### Phase 4: 백업/복원 UX 정리 + +- history는 revision 목록을 보여준다. +- 복원은 과거 content로 새 revision을 append한다. +- import는 기본적으로 destructive replace가 아니라 revision append다. + +## 수용 기준 + +- 신규 디바이스에서 서버 current 로드 전에는 current overwrite 요청이 나가지 않는다. +- stale `baseRevisionId`로 저장해도 서버 current pointer는 바뀌지 않는다. +- stale 저장 content는 revision으로 서버에 남는다. +- 정상 저장은 새 revision을 만들고 current pointer를 갱신한다. +- 복원은 과거 revision을 수정하지 않고 새 revision을 만든다. +- import는 기존 current를 지우지 않는다. +- 날짜별 모든 서버 revision은 완전한 원문 content를 가진다. + +## 결론 + +현재 정책은 "클라이언트가 충돌을 추론하고 서버는 overwrite 전에 백업한다"에 가깝다. 최근 수정으로 몇 가지 실제 버그는 막았지만, 요구 전제에 비해 여전히 복잡하고 서버 방어선이 약하다. + +To-be는 "서버는 모든 저장을 revision으로 남기고, current 승격만 조건부로 한다"가 되어야 한다. 이 모델은 동시 편집 병합을 포기하는 대신, 로딩 실패와 stale 저장으로 인한 데이터 손실을 더 단순하고 명확하게 막는다. diff --git a/docs/adr/001-api-framework-selection/001-api-framework-selection.md b/docs/adr/001-api-framework-selection/001-api-framework-selection.md index cb6f0b5..9849992 100644 --- a/docs/adr/001-api-framework-selection/001-api-framework-selection.md +++ b/docs/adr/001-api-framework-selection/001-api-framework-selection.md @@ -1,5 +1,7 @@ # API 프레임워크 선택: Hono +> 상태: Superseded. 2026-06-21 기준 AWS Lambda + DynamoDB + Serverless Framework 선택은 더 이상 현재 아키텍처가 아닙니다. 현재 결정은 ADR 004의 NestJS + host MySQL + local Docker self-host 구조입니다. + ## 핵심 결정 요인: 서비스 비용 최소화 개인 프로젝트 `my-commit`은 트래픽이 적은 서비스입니다. diff --git a/docs/adr/003-conflict-detection-system/003-conflict-detection-system.md b/docs/adr/003-conflict-detection-system/003-conflict-detection-system.md index b6b0982..b204d9b 100644 --- a/docs/adr/003-conflict-detection-system/003-conflict-detection-system.md +++ b/docs/adr/003-conflict-detection-system/003-conflict-detection-system.md @@ -1,5 +1,7 @@ # 충돌 탐지 시스템: Linked List of Hashes +> 상태: 부분 대체. 해시 기반 parent 추적 아이디어는 기존 클라이언트 호환 계층으로 유지하지만, 2026-06-21 기준 서버의 최종 보존 모델은 ADR 004의 append-only revision + current pointer입니다. + ## 핵심 결정: Git 방식의 버전 체인 추적 `my-commit` 프로젝트는 로컬과 서버 간의 데이터 동기화에서 발생하는 충돌을 정확하게 감지하고 해결하기 위해, 단순한 타임스탬프 비교 대신 Git과 유사한 Linked List of Hashes 방식을 채택했습니다. diff --git a/docs/adr/003-conflict-detection-system/CONFLICT_DETECTION.md b/docs/adr/003-conflict-detection-system/CONFLICT_DETECTION.md index 2ae5392..2741da1 100644 --- a/docs/adr/003-conflict-detection-system/CONFLICT_DETECTION.md +++ b/docs/adr/003-conflict-detection-system/CONFLICT_DETECTION.md @@ -1,5 +1,7 @@ # 충돌 탐지 시스템 (Conflict Detection System) +> 상태: 부분 대체. 해시 기반 parent 추적 아이디어는 기존 클라이언트 호환 계층으로 유지하지만, 2026-06-21 기준 서버 저장소는 DynamoDB가 아니라 MySQL 기반 append-only revision + current pointer입니다. + ## 개요 본 시스템은 Git과 유사한 Linked List of Hashes 방식을 사용하여 로컬과 서버 간의 데이터 동기화 충돌을 감지합니다. 단순한 타임스탬프 비교가 아닌, 버전 체인을 추적하여 정확한 충돌 판단을 수행합니다. @@ -248,17 +250,19 @@ interface LocalLogData { } ``` -### 서버 DB (DynamoDB) +### 서버 DB (현재 MySQL) ```typescript -interface LogItem { - userId: string; // Partition Key - date: string; // Sort Key +interface LogRevision { + userId: string; + date: string; + revisionId: string; content: string; contentHash: string; - parentHash: string | null; - updatedAt: string; - version: number; + baseContentHash: string | null; + baseRevisionId: string | null; + promoted: boolean; + createdAt: string; } ``` diff --git a/docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md b/docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md new file mode 100644 index 0000000..144393e --- /dev/null +++ b/docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md @@ -0,0 +1,85 @@ +# ADR 004: 자체 호스팅 NestJS + MySQL 리비전 동기화 + +## 상태 + +채택 및 구현 완료. + +## 배경 + +기존 프로젝트는 AWS 중심 인프라를 사용하고 있었다. + +- AWS Lambda용 Hono API. +- DynamoDB 및 DynamoDB Local. +- Serverless Framework 배포. +- S3/CloudFront 웹 배포. +- AWS ACM/DNS/deploy/reset 스크립트. + +제품 요구사항이 다음과 같이 바뀌었다. + +1. AWS 인프라 의존성을 제거해야 한다. +2. 런타임은 로컬 Docker 기반이어야 한다. +3. 필요한 인프라는 오픈소스이며 자체 호스팅 가능해야 한다. +4. 데이터베이스는 Docker MySQL이 아니라 호스트의 로컬 MySQL이어야 한다. +5. API는 NestJS로 직접 구현할 수 있다. +6. 로그 데이터는 유실되면 안 된다. 저장된 모든 버전은 원문 전체를 보관해야 한다. +7. 동시 편집은 목표 요구사항이 아니다. 핵심 위험은 로딩 실패나 오래된 로컬 상태로 인해 stale 데이터가 최신 데이터를 덮어쓰는 것이다. + +## 결정 + +백엔드를 NestJS + MySQL로 옮기고, current pointer를 가진 append-only revision 저장 방식을 사용한다. + +자체 호스팅 런타임은 다음으로 구성한다. + +- `api`: NestJS Docker 컨테이너. +- `web`: Docker 컨테이너의 Caddy가 서빙하는 Vite 정적 빌드. `/api/*` 요청도 API 컨테이너로 프록시한다. +- `mysql`: Docker Compose가 관리하지 않는 호스트 로컬 MySQL. + +로그 동기화는 다음을 사용한다. + +- `log_revisions`: append-only 전체 원문 스냅샷. +- `current_logs`: 날짜별 현재 승격 revision을 가리키는 pointer. + +모든 저장 요청은 전체 원문 revision을 insert한다. 서버는 해당 revision의 base revision이 현재 revision과 일치할 때만 current로 승격한다. base가 stale이거나 알 수 없는 경우, revision은 보존하지만 current를 대체하지 않는다. + +## 결과 + +### 장점 + +- AWS 배포와 DynamoDB lock-in을 제거한다. +- MySQL transaction 덕분에 revision append와 guarded promotion을 이해하기 쉽다. +- stale 저장이 current를 조용히 덮어쓸 수 없다. +- 거절되거나 stale인 저장도 `log_revisions`에 전체 원문으로 보존된다. +- 복원과 import를 destructive replacement가 아니라 새 revision으로 모델링할 수 있다. + +### 단점 + +- production 데이터가 있다면 기존 DynamoDB 데이터의 export/migration 경로가 필요하다. +- API 패키지 변경 폭이 크다. +- MySQL 호스트 lifecycle, backup, credential 관리는 이제 운영자의 책임이다. +- Docker 컨테이너가 호스트 MySQL에 연결하려면 플랫폼별 네트워킹 설정이 필요하다. + +## 구현 메모 + +- macOS Docker에서는 API가 `MYSQL_HOST=host.docker.internal`을 사용해야 한다. +- Linux Docker에서는 Compose가 `extra_hosts: host.docker.internal:host-gateway`를 제공해야 한다. +- `JWT_SECRET`은 계속 필요하며 안정적으로 유지되어야 한다. +- 첫 migration 단계에서는 기존 웹 endpoint 호환성을 유지해야 한다. +- 새 revision 필드는 기존 클라이언트를 깨뜨리지 않는 방식으로 응답에 추가해야 한다. + +## 기각한 대안 + +### AWS + DynamoDB 유지 + +AWS 인프라 의존성을 제거해야 한다는 요구사항이 명확하므로 기각한다. + +### Docker 기반 MySQL + +Docker 없는 로컬 MySQL을 원한다는 요구사항이 명확하므로 기각한다. + +### CAS 거절만 수행 + +stale 클라이언트 content도 보존되어야 하므로 기각한다. 제출된 content를 버리는 CAS 실패는 데이터 무손실 요구사항을 만족하지 못한다. + +### S3/오브젝트 스냅샷 저장소 + +현재는 YAGNI로 보고 기각한다. 현재 로그 content 크기에는 MySQL text snapshot으로 충분하며 로컬 운영도 더 단순하다. diff --git a/docs/apps/api/README.md b/docs/apps/api/README.md index 770611a..1527489 100644 --- a/docs/apps/api/README.md +++ b/docs/apps/api/README.md @@ -1,203 +1,113 @@ # My Commit API -Hono 기반 서버리스 API (AWS Lambda + DynamoDB) +NestJS + host MySQL 기반 API입니다. -## 아키텍처 +## Architecture +```text +Browser + │ + ▼ +Caddy web container + ├─ static web assets + └─ /api/* -> NestJS api container + │ + ▼ + host local MySQL ``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ API Gateway │────▶│ AWS Lambda │────▶│ DynamoDB │ -│ (HTTP API) │ │ (Hono App) │ │ │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ -``` - -## 로컬 개발 환경 설정 - -명령어는 기본적으로 `apps/api` 디렉터리 기준입니다. -저장소 루트에서 실행하려면 `pnpm --filter my-time-api <명령>` 형태를 사용하세요. - -### 사전 요구사항 - -- Node.js 18+ -- Docker & Docker Compose -- pnpm -### 1. 환경 변수 설정 +MySQL은 Docker Compose 서비스가 아닙니다. API 컨테이너는 `host.docker.internal`을 통해 host MySQL에 접속합니다. -```bash -# 저장소 루트에서 실행 -cp .env.example .env.local -``` - -`apps/api` 하위에 별도 `.env` 파일은 사용하지 않습니다. +## Data Model -### 2. 로컬 개발 서버 실행 (권장) +로그 저장은 append-only revision + current pointer 모델입니다. -#### 방법 A: Docker + 로컬 서버 (간단) +- `log_revisions`: 모든 저장 버전의 완전한 원문 snapshot +- `current_logs`: 날짜별 현재 revision pointer -```bash -# DynamoDB Local 시작 + 테이블 생성 + 개발 서버 실행 -pnpm local:start -``` +정상 base로 저장하면 새 revision을 만들고 current pointer를 갱신합니다. stale base로 저장하면 새 revision은 남기지만 current pointer는 바꾸지 않습니다. -처음 실행하거나 `.dynamodb-data`를 삭제한 경우에도 `local:start`가 테이블 생성을 자동 수행합니다. +## Scripts -#### 방법 B: 단계별 실행 +`apps/api` 기준: ```bash -# 1. DynamoDB Local 시작 (Docker) -pnpm db:start - -# 2. 테이블 생성 -pnpm db:create-tables - -# 3. 개발 서버 실행 -pnpm dev:local +pnpm dev +pnpm build +pnpm start +pnpm test +pnpm lint +pnpm db:migrate ``` -#### 방법 C: Serverless Offline (Lambda 환경 시뮬레이션) +저장소 루트 기준: ```bash -# 빌드 후 Serverless Offline 실행 -pnpm sls:offline +pnpm dev:api +pnpm build:api +pnpm --filter my-commit-api test +pnpm db:migrate ``` -### 3. 로컬 서비스 접속 - -| 서비스 | URL | -| ----------------- | --------------------- | -| API Server | http://localhost:3000 | -| DynamoDB Admin UI | http://localhost:8001 | -| DynamoDB Local | http://localhost:8000 | - -## 스크립트 명령어 - -### 개발 - -| 명령어 | 설명 | -| ------------------ | -------------------------------------------- | -| `pnpm dev` | 로컬 개발 서버 실행 | -| `pnpm dev:local` | IS_OFFLINE=true로 개발 서버 실행 | -| `pnpm local:start` | Docker + 테이블 생성 + 개발 서버 한번에 실행 | -| `pnpm build` | 프로덕션 빌드 | - -### DynamoDB Local +## Environment -| 명령어 | 설명 | -| ----------------------- | ---------------------------- | -| `pnpm db:start` | Docker로 DynamoDB Local 시작 | -| `pnpm db:stop` | DynamoDB Local 중지 | -| `pnpm db:create-tables` | 로컬 테이블 생성 | -| `pnpm db:delete-tables` | 로컬 테이블 삭제 | -| `pnpm db:reset` | 테이블 삭제 후 재생성 | - -### Serverless Framework - -| 명령어 | 설명 | -| ---------------------- | ------------------------------------------------ | -| `pnpm sls:offline` | Serverless Offline 실행 (Lambda 환경 시뮬레이션) | -| `pnpm sls:deploy` | 루트 배포 스크립트에서 내부 호출 전용 | -| `pnpm sls:remove` | AWS 리소스 제거 | - -## AWS 배포 - -### 사전 요구사항 +루트 `.env.local`을 사용합니다. ```bash -# AWS CLI 설정 -aws configure +JWT_SECRET=your-fixed-secret +DATABASE_URL=mysql://my_commit:my_commit@127.0.0.1:3306/my_commit +DOCKER_DATABASE_URL=mysql://my_commit:my_commit@host.docker.internal:3306/my_commit ``` -### 배포 +Prisma CLI와 로컬 API는 `DATABASE_URL`을 사용합니다. Docker API는 +`DOCKER_DATABASE_URL`을 사용합니다. -```bash -# 저장소 루트에서 실행 -pnpm run deploy:dev +## Endpoints -# 저장소 루트에서 실행 -pnpm run deploy:prod -``` - -`apps/api` 디렉터리에서 직접 `pnpm sls:deploy` 실행은 차단되어 있습니다. -원격 DynamoDB 테이블은 `serverless.yml`의 `resources` 정의로 배포 시 자동 생성됩니다. - -### 리소스 제거 - -```bash -pnpm sls:remove -``` - -## API 엔드포인트 +```text +POST /auth/signup +POST /auth/login -### 인증 +GET /user-settings +PUT /user-settings -``` -POST /auth/signup - 회원가입 -POST /auth/login - 로그인 +GET /raw-logs +POST /raw-logs +POST /raw-logs/bulk +GET /raw-logs/:date +GET /raw-logs/:date/backups ``` -### 로그 (인증 필요) +## Migration +```bash +pnpm --filter my-commit-api db:migrate ``` -POST /raw-logs - 로그 저장 -GET /raw-logs/:date - 날짜별 로그 조회 -``` - -## 환경 변수 - -| 변수 | 설명 | 기본값 | -| ---------------------- | ----------------------- | --------------------- | -| `AWS_REGION` | AWS 리전 | ap-northeast-2 | -| `IS_OFFLINE` | 로컬 DynamoDB 사용 여부 | false | -| `SLS_STAGE` | 스테이지 이름 | local | -| `JWT_SECRET` | JWT 서명 키(고정값) | - | -| `PORT` | 서버 포트 | 3000 | -테이블 이름은 환경변수 오버라이드 없이 `SLS_STAGE` 기준으로 자동 결정됩니다. -예: `local-my-commit-users`, `prod-my-commit-users` +Prisma가 `apps/api/prisma/migrations`의 적용 이력을 관리하며 다음 테이블을 생성합니다. -로컬 개발/배포 환경 변수는 모두 저장소 루트 `.env*` 파일에서 관리합니다. +- `users` +- `user_settings` +- `log_revisions` +- `current_logs` -## 트러블슈팅 - -### 로그인 시 `ResourceNotFoundException` - -다음 에러는 로컬 DynamoDB 테이블이 없는 경우 발생합니다. - -```text -Cannot do operations on a non-existent table -``` - -해결: +기존 수동 schema script로 이미 테이블을 만든 DB를 그대로 사용할 때는 최초 한 번 +초기 migration을 baseline 처리한 뒤 deploy합니다. ```bash -pnpm db:create-tables +pnpm --filter my-commit-api exec prisma migrate resolve --applied 20260712000000_init +pnpm db:migrate ``` -### 로그인 시 `ECONNREFUSED ...:8000` +## Troubleshooting -다음 에러는 DynamoDB Local 컨테이너가 꺼져 있을 때 발생합니다. +### `ECONNREFUSED 127.0.0.1:3306` -```text -connect ECONNREFUSED ::1:8000 -connect ECONNREFUSED 127.0.0.1:8000 -``` - -해결: +Host MySQL is not running or `DATABASE_URL` is wrong. -```bash -pnpm db:start -``` +### Docker container cannot connect to MySQL -## 테스트 환경 비교 - -| 기능 | Docker + dev | Serverless Offline | -| ---------------------- | ------------ | ------------------ | -| Lambda 환경 시뮬레이션 | ❌ | ✅ | -| API Gateway 시뮬레이션 | ❌ | ✅ | -| 핫 리로드 | ✅ | ⚠️ | -| DynamoDB | ✅ | ✅ | -| IAM 시뮬레이션 | ❌ | ❌ | -| 설정 복잡도 | 낮음 | 중간 | -| 속도 | 빠름 | 중간 | -| 권장 용도 | 일반 개발 | 배포 전 테스트 | +Check that MySQL listens on an address reachable from Docker and that the +`DOCKER_DATABASE_URL` user can connect from the Docker host gateway. On macOS, +`host.docker.internal` is supported by Docker Desktop. On Linux, +`docker-compose.yml` adds `host.docker.internal:host-gateway`. diff --git a/docs/superpowers/plans/2026-06-21-self-hosted-mysql-revision-sync.md b/docs/superpowers/plans/2026-06-21-self-hosted-mysql-revision-sync.md new file mode 100644 index 0000000..aa4926f --- /dev/null +++ b/docs/superpowers/plans/2026-06-21-self-hosted-mysql-revision-sync.md @@ -0,0 +1,192 @@ +# Self-hosted MySQL Revision Sync Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Remove AWS infrastructure dependency and replace the API/data layer with a NestJS + host MySQL implementation that stores every log version as a complete revision and only promotes safe revisions to current. + +**Architecture:** The API runs as a NestJS HTTP server in Docker. MySQL runs on the host, not in Docker, and the API connects through `host.docker.internal` when containerized. Logs use append-only `log_revisions` plus `current_logs` pointer; stale writes are preserved as revisions but do not overwrite current. + +**Tech Stack:** NestJS, mysql2, Node crypto, Vite web, Docker Compose, Caddy, host MySQL. + +--- + +### Task 1: Documentation Source of Truth + +**Files:** +- Modify: `docs/SYNC_POLICY_COMPARISON.md` +- Create: `docs/adr/004-self-hosted-nestjs-mysql-revision-sync/004-self-hosted-nestjs-mysql-revision-sync.md` +- Create: `docs/superpowers/plans/2026-06-21-self-hosted-mysql-revision-sync.md` + +- [x] **Step 1: Record requirements** + +Capture these decisions: + +- AWS infrastructure dependency must be removed. +- Runtime is local Docker based. +- Required external services must be open-source and self-hostable. +- DB is host local MySQL, not Docker MySQL. +- API moves to NestJS. +- Log sync uses option 3: append-only revisions plus current pointer. +- Technical decisions discovered during implementation must be written to MD/ADR. + +### Task 2: API Package Conversion + +**Files:** +- Modify: `apps/api/package.json` +- Modify: `apps/api/tsconfig.json` +- Delete/ignore: `apps/api/serverless.yml` +- Delete/ignore: `apps/api/docker-compose.yml` + +- [x] **Step 1: Replace Hono/Serverless/DynamoDB dependencies** + +Remove AWS, Hono, Serverless, DynamoDB Local dependencies. Add NestJS, mysql2, reflect-metadata, rxjs. + +- [x] **Step 2: Replace scripts** + +Use: + +- `dev`: `tsx watch src/main.ts` +- `build`: `tsc -p tsconfig.json` +- `start`: `node dist/main.js` +- `test`: `vitest run` +- `db:migrate`: `tsx scripts/migrate.ts` + +### Task 3: MySQL Schema + +**Files:** +- Create: `apps/api/src/db/schema.ts` +- Create: `apps/api/scripts/migrate.ts` + +- [x] **Step 1: Define schema** + +Create tables: + +- `users` +- `user_settings` +- `current_logs` +- `log_revisions` + +The critical invariant is that `log_revisions.content` stores the full content snapshot for every saved version. + +- [x] **Step 2: Implement migration script** + +The script connects to host MySQL using env vars and executes idempotent `CREATE TABLE IF NOT EXISTS` statements. + +### Task 4: MySQL Connection Layer + +**Files:** +- Create: `apps/api/src/db/mysql.ts` + +- [x] **Step 1: Implement mysql2 pool** + +Use env vars: + +- `MYSQL_HOST` +- `MYSQL_PORT` +- `MYSQL_USER` +- `MYSQL_PASSWORD` +- `MYSQL_DATABASE` + +Default host is `127.0.0.1` for local dev. Docker compose uses `host.docker.internal`. + +### Task 5: NestJS API Structure + +**Files:** +- Create: `apps/api/src/main.ts` +- Create: `apps/api/src/app.module.ts` +- Create: `apps/api/src/auth/*` +- Create: `apps/api/src/settings/*` +- Create: `apps/api/src/logs/*` + +- [x] **Step 1: Bootstrap Nest** + +Enable CORS using `ALLOWED_ORIGINS`. + +- [x] **Step 2: Implement JWT auth** + +Use Node crypto HS256 with the existing `JWT_SECRET` env var. Keep response shape `{ access_token }`. + +### Task 6: Users and Settings + +**Files:** +- Create: `apps/api/src/users/users.repository.ts` +- Create: `apps/api/src/settings/settings.controller.ts` +- Create: `apps/api/src/settings/settings.service.ts` + +- [x] **Step 1: Preserve API compatibility** + +Keep: + +- `POST /auth/signup` +- `POST /auth/login` +- `GET /user-settings` +- `PUT /user-settings` + +### Task 7: Revision Log Sync + +**Files:** +- Create: `apps/api/src/logs/logs.repository.ts` +- Create: `apps/api/src/logs/logs.service.ts` +- Create: `apps/api/src/logs/logs.controller.ts` + +- [x] **Step 1: Implement append plus guarded promotion** + +For `POST /raw-logs`: + +1. Start transaction. +2. Lock current row for user/date. +3. Insert full-content revision. +4. Promote only when request `baseRevisionId` matches current revision, or existing-client `parentHash` matches current content hash. If no current revision exists, the first saved revision becomes current. +5. Commit. + +- [x] **Step 2: Preserve legacy client compatibility** + +Existing web sends `contentHash` and `parentHash`. Until the web is migrated, map `parentHash` to `baseContentHash` and still append every save as a revision. A stale save is preserved with `promoted=false` and does not overwrite current. + +### Task 8: Self-host Docker + +**Files:** +- Create: `Dockerfile.api` +- Create: `Dockerfile.web` +- Create: `docker-compose.yml` +- Create: `Caddyfile` +- Modify: `.env.example` + +- [x] **Step 1: Compose api/web/caddy** + +Do not include MySQL as a service. API connects to host MySQL. + +### Task 9: AWS Removal + +**Files:** +- Modify: `package.json` +- Modify: `apps/web/package.json` +- Delete: `apps/api/serverless.yml` +- Delete: `apps/web/serverless.yml` +- Delete or deprecate AWS deployment scripts/docs. + +- [x] **Step 1: Remove AWS deploy scripts** + +Root scripts should not expose AWS deploy/undeploy commands. + +### Task 10: Verification + +**Files:** +- Existing test/build configs. + +- [x] **Step 1: Run API build** + +Run: `pnpm --filter my-commit-api build` + +- [x] **Step 2: Run web tests** + +Run: `pnpm --filter my-commit-client test` + +- [x] **Step 3: Run web build** + +Run: `pnpm --filter my-commit-client build` + +- [x] **Step 4: Run lint if dependency install succeeds** + +Run: `pnpm --filter my-commit-api lint` +Run: `pnpm --filter my-commit-client lint` diff --git a/package.json b/package.json index f3e98fd..ce4210c 100644 --- a/package.json +++ b/package.json @@ -14,27 +14,16 @@ "lint": "turbo run lint", "lint:fix": "turbo run lint:fix", "fix:backup-json": "node scripts/fix-contaminated-backup.mjs", - "deploy": "bash scripts/deploy.sh", - "deploy:prod": "pnpm run deploy -- prod", - "deploy:dev": "pnpm run deploy -- dev", - "deploy:prod:porkbun": "UPDATE_DNS_RECORD=true pnpm run deploy -- prod --dns-provider porkbun", - "deploy:dev:porkbun": "UPDATE_DNS_RECORD=true pnpm run deploy -- dev --dns-provider porkbun", - "create:aws:acm": "bash scripts/create-aws-acm.sh", - "create:aws:acm:prod:porkbun": "pnpm run create:aws:acm -- prod --dns-provider porkbun --wait", - "create:aws:acm:dev:porkbun": "pnpm run create:aws:acm -- dev --dns-provider porkbun --wait", - "dns:sync": "bash scripts/sync-dns.sh", - "dns:sync:porkbun": "pnpm run dns:sync -- --dns-provider porkbun", - "dns:check:porkbun": "bash scripts/check-porkbun-dns.sh", - "undeploy": "bash scripts/reset-aws.sh", - "undeploy:prod": "pnpm run undeploy -- prod --remove-acm", - "undeploy:dev": "pnpm run undeploy -- dev --remove-acm" + "selfhost:up": "pnpm db:migrate && docker compose up -d --build", + "selfhost:down": "docker compose down", + "selfhost:logs": "docker compose logs -f", + "db:migrate": "pnpm --filter my-commit-api db:migrate" }, "devDependencies": { "tsx": "^4.7.1", "turbo": "^1.10.0" }, "dependencies": { - "eslint-plugin-react-hooks": "7.1.0-canary-d6cae440-20260106", - "jose": "^6.1.3" + "eslint-plugin-react-hooks": "7.1.0-canary-d6cae440-20260106" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e82a0f8..7f8b9e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,7 @@ importers: dependencies: eslint-plugin-react-hooks: specifier: 7.1.0-canary-d6cae440-20260106 - version: 7.1.0-canary-d6cae440-20260106(eslint@9.39.2(jiti@1.21.7)) - jose: - specifier: ^6.1.3 - version: 6.1.3 + version: 7.1.0-canary-d6cae440-20260106(eslint@9.39.2(jiti@2.7.0)) devDependencies: tsx: specifier: ^4.7.1 @@ -24,58 +21,49 @@ importers: apps/api: dependencies: - '@aws-sdk/client-dynamodb': - specifier: ^3.500.0 - version: 3.965.0 - '@aws-sdk/lib-dynamodb': - specifier: ^3.500.0 - version: 3.965.0(@aws-sdk/client-dynamodb@3.965.0) - '@hono/node-server': - specifier: ^1.8.0 - version: 1.19.7(hono@4.11.3) - aws-lambda: - specifier: ^1.0.7 - version: 1.0.7 - hono: - specifier: ^4.0.0 - version: 4.11.3 - jose: - specifier: ^6.1.3 - version: 6.1.3 + '@nestjs/common': + specifier: ^10.4.0 + version: 10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': + specifier: ^10.4.0 + version: 10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.22)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/platform-express': + specifier: ^10.4.0 + version: 10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.22) + '@prisma/client': + specifier: '6' + version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3) + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 + rxjs: + specifier: ^7.8.1 + version: 7.8.2 devDependencies: '@my-commit/eslint-config': specifier: workspace:* version: link:../../packages/eslint-config - '@types/aws-lambda': - specifier: ^8.10.133 - version: 8.10.159 '@types/node': specifier: ^20.11.17 version: 20.19.27 eslint: specifier: ^9.0.0 - version: 9.39.2(jiti@1.21.7) + version: 9.39.2(jiti@2.7.0) globals: specifier: ^15.0.0 version: 15.15.0 - serverless: - specifier: ^3.38.0 - version: 3.40.0(@types/node@20.19.27) - serverless-dynamodb: - specifier: ^0.2.54 - version: 0.2.56 - serverless-offline: - specifier: ^13.3.3 - version: 13.9.0(serverless@3.40.0(@types/node@20.19.27)) - tsup: - specifier: ^8.0.2 - version: 8.5.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@1.10.2) + prisma: + specifier: '6' + version: 6.19.3(typescript@5.9.3) tsx: specifier: ^4.7.1 version: 4.21.0 typescript: specifier: ^5.3.3 version: 5.9.3 + vitest: + specifier: ^0.31.4 + version: 0.31.4(terser@5.44.1) apps/web: dependencies: @@ -155,15 +143,6 @@ importers: prettier-plugin-tailwindcss: specifier: ^0.7.2 version: 0.7.2(prettier@3.7.4) - serverless: - specifier: ^3.38.0 - version: 3.40.0(@types/node@20.19.27) - serverless-cloudfront-invalidate: - specifier: ^1.12.0 - version: 1.12.2 - serverless-s3-sync: - specifier: ^3.2.0 - version: 3.5.1(serverless@3.40.0(@types/node@20.19.27)) tailwindcss: specifier: ^3.3.3 version: 3.4.19(tsx@4.21.0)(yaml@1.10.2) @@ -190,28 +169,28 @@ importers: version: 9.39.2 eslint: specifier: ^9.0.0 - version: 9.39.2(jiti@1.21.7) + version: 9.39.2(jiti@2.7.0) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.2(eslint@9.39.2(jiti@1.21.7)) + version: 9.1.2(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(prettier@3.7.4) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.7.0)))(eslint@9.39.2(jiti@2.7.0))(prettier@3.7.4) eslint-plugin-react: specifier: ^7.34.1 - version: 7.37.5(eslint@9.39.2(jiti@1.21.7)) + version: 7.37.5(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-react-hooks: specifier: rc - version: 6.0.0-rc.2(eslint@9.39.2(jiti@1.21.7)) + version: 6.0.0-rc.2(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-react-refresh: specifier: ^0.4.6 - version: 0.4.26(eslint@9.39.2(jiti@1.21.7)) + version: 0.4.26(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-simple-import-sort: specifier: ^12.1.0 - version: 12.1.1(eslint@9.39.2(jiti@1.21.7)) + version: 12.1.1(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-unused-imports: specifier: ^3.2.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)) + version: 3.2.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)) globals: specifier: ^15.0.0 version: 15.15.0 @@ -223,13 +202,10 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.0.0 - version: 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) packages: - 2-thenable@1.0.0: - resolution: {integrity: sha512-HqiDzaLDFCXkcCO/SwoyhRwqYtINFHF7t9BDRq4x90TOKNAJpiqUt9X5lQ08bwxYzc067HUywDjGySpebHcUpw==} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -240,296 +216,6 @@ packages: peerDependencies: ajv: '>=8' - '@auth0/s3@1.0.1': - resolution: {integrity: sha512-NkGHLi+TmaeBUPfixpOnJ9DetY2cMCh4wJjQGgY7ouv5MLLeUu/3rENKXFyMtGIFfQ4bvPFTeC8SW3WfcmrnXg==} - engines: {node: '>=8.0.0'} - - '@aws-crypto/crc32@5.2.0': - resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} - engines: {node: '>=16.0.0'} - - '@aws-crypto/crc32c@5.2.0': - resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - - '@aws-crypto/sha1-browser@5.2.0': - resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - - '@aws-crypto/sha256-browser@5.2.0': - resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - - '@aws-crypto/sha256-js@5.2.0': - resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} - engines: {node: '>=16.0.0'} - - '@aws-crypto/supports-web-crypto@5.2.0': - resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - - '@aws-crypto/util@5.2.0': - resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - - '@aws-sdk/client-api-gateway@3.966.0': - resolution: {integrity: sha512-HIq/g7O9XXCHj6iibyc4QApOHrv4anxtajorNKh4kRR9PJ6CsPLnyKyP32BRjjDNmxlR9PFA5ZRzZuMcUssirQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-cloudformation@3.966.0': - resolution: {integrity: sha512-lmA0gdXQVZm4ihXIRhUuVwaXG091ubsg+DECGel34cVZDCOkW6v22KxQSuG3tu9Okyn4CE7Fo064vB+xk6Q/7w==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-cognito-identity-provider@3.966.0': - resolution: {integrity: sha512-NRTyPlIg/NLwWAhY20tWz3gHnxLey/Vp2FvNdWdjHw3mD1yMNlF5zZpNkKDgWLVlN6Yg+4UzMtVDEOfKY/1aXw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-dynamodb@3.965.0': - resolution: {integrity: sha512-QRVgLB6/CRum0wjjPwclHXO6KjagLqOjsdTvIw6J42+kYUoO62DT9o0NWPXXWyfxBSDpgBnW8K94iHtOM/+/jw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-eventbridge@3.966.0': - resolution: {integrity: sha512-PY9cmvd8EXi9wBPuKnU0vVIGS8+qxcc7RUorRKpXSsCVKTKhihWhZoZBbpp79KNVMyM6R+2XL01kmuHbM/aMyg==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-iam@3.966.0': - resolution: {integrity: sha512-C7WbvRiboBOmGzPv5koRouBBq3q8CSAgF03Y9rc5TZE0Zo/S0Gw1Q+0w5fvGnVEAP+mZgtRypmDGWc++bCZ2Dw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-lambda@3.966.0': - resolution: {integrity: sha512-Bs7WbbeBUk40xNgk6td46LWbo/scwNz+ZefM4JzX5fOWFgan9nH5jcBTWNTKUt8NijNTXG7lVEIw96xhsRfKbw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-s3@3.966.0': - resolution: {integrity: sha512-IckVv+A6irQyXTiJrNpfi63ZtPuk6/Iu70TnMq2DTRFK/4bD2bOvqL1IHZ2WGmZMoeWd5LI8Fn6pIwdK6g4QJQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-sso@3.965.0': - resolution: {integrity: sha512-iv2tr+n4aZ+nPUFFvG00hISPuEd4DU+1/Q8rPAYKXsM+vEPJ2nAnP5duUOa2fbOLIUCRxX3dcQaQaghVHDHzQw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-sso@3.966.0': - resolution: {integrity: sha512-hQZDQgqRJclALDo9wK+bb5O+VpO8JcjImp52w9KPSz9XveNRgE9AYfklRJd8qT2Bwhxe6IbnqYEino2wqUMA1w==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/client-sts@3.966.0': - resolution: {integrity: sha512-ucIhzW4cb4aasBtKcPR2PURquV+JCFCLIgd2sjx7G1K25FeOfT8rvZX2ph666f7GlRxex1SlGfVnIt/MGiGmsQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/core@3.965.0': - resolution: {integrity: sha512-aq9BhQxdHit8UUJ9C0im9TtuKeK0pT6NXmNJxMTCFeStI7GG7ImIsSislg3BZTIifVg1P6VLdzMyz9de85iutQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/core@3.966.0': - resolution: {integrity: sha512-QaRVBHD1prdrFXIeFAY/1w4b4S0EFyo/ytzU+rCklEjMRT7DKGXGoHXTWLGz+HD7ovlS5u+9cf8a/LeSOEMzww==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/crc64-nvme@3.965.0': - resolution: {integrity: sha512-9FbIyJ/Zz1AdEIrb0+Pn7wRi+F/0Y566ooepg0hDyHUzRV3ZXKjOlu3wJH3YwTz2UkdwQmldfUos2yDJps7RyA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-env@3.965.0': - resolution: {integrity: sha512-mdGnaIjMxTIjsb70dEj3VsWPWpoq1V5MWzBSfJq2H8zgMBXjn6d5/qHP8HMf53l9PrsgqzMpXGv3Av549A2x1g==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-env@3.966.0': - resolution: {integrity: sha512-sxVKc9PY0SH7jgN/8WxhbKQ7MWDIgaJv1AoAKJkhJ+GM5r09G5Vb2Vl8ALYpsy+r8b+iYpq5dGJj8k2VqxoQMg==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-http@3.965.0': - resolution: {integrity: sha512-YuGQel9EgA/z25oeLM+GYYQS750+8AESvr7ZEmVnRPL0sg+K3DmGqdv+9gFjFd0UkLjTlC/jtbP2cuY6UcPiHQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-http@3.966.0': - resolution: {integrity: sha512-VTJDP1jOibVtc5pn5TNE12rhqOO/n10IjkoJi8fFp9BMfmh3iqo70Ppvphz/Pe/R9LcK5Z3h0Z4EB9IXDR6kag==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-ini@3.965.0': - resolution: {integrity: sha512-xRo72Prer5s0xYVSCxCymVIRSqrVlevK5cmU0GWq9yJtaBNpnx02jwdJg80t/Ni7pgbkQyFWRMcq38c1tc6M/w==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-ini@3.966.0': - resolution: {integrity: sha512-4oQKkYMCUx0mffKuH8LQag1M4Fo5daKVmsLAnjrIqKh91xmCrcWlAFNMgeEYvI1Yy125XeNSaFMfir6oNc2ODA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-login@3.965.0': - resolution: {integrity: sha512-43/H8Qku8LHyugbhLo8kjD+eauhybCeVkmrnvWl8bXNHJP7xi1jCdtBQJKKJqiIHZws4MOEwkji8kFdAVRCe6g==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-login@3.966.0': - resolution: {integrity: sha512-wD1KlqLyh23Xfns/ZAPxebwXixoJJCuDbeJHFrLDpP4D4h3vA2S8nSFgBSFR15q9FhgRfHleClycf6g5K4Ww6w==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-node@3.965.0': - resolution: {integrity: sha512-cRxmMHF+Zh2lkkkEVduKl+8OQdtg/DhYA69+/7SPSQURlgyjFQGlRQ58B7q8abuNlrGT3sV+UzeOylZpJbV61Q==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-node@3.966.0': - resolution: {integrity: sha512-7QCOERGddMw7QbjE+LSAFgwOBpPv4px2ty0GCK7ZiPJGsni2EYmM4TtYnQb9u1WNHmHqIPWMbZR0pKDbyRyHlQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-process@3.965.0': - resolution: {integrity: sha512-gmkPmdiR0yxnTzLPDb7rwrDhGuCUjtgnj8qWP+m0gSz/W43rR4jRPVEf6DUX2iC+ImQhxo3NFhuB3V42Kzo3TQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-process@3.966.0': - resolution: {integrity: sha512-q5kCo+xHXisNbbPAh/DiCd+LZX4wdby77t7GLk0b2U0/mrel4lgy6o79CApe+0emakpOS1nPZS7voXA7vGPz4w==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-sso@3.965.0': - resolution: {integrity: sha512-N01AYvtCqG3Wo/s/LvYt19ity18/FqggiXT+elAs3X9Om/Wfx+hw9G+i7jaDmy+/xewmv8AdQ2SK5Q30dXw/Fw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-sso@3.966.0': - resolution: {integrity: sha512-Rv5aEfbpqsQZzxpX2x+FbSyVFOE3Dngome+exNA8jGzc00rrMZEUnm3J3yAsLp/I2l7wnTfI0r2zMe+T9/nZAQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-web-identity@3.965.0': - resolution: {integrity: sha512-T4gMZ2JzXnfxe1oTD+EDGLSxFfk1+WkLZdiHXEMZp8bFI1swP/3YyDFXI+Ib9Uq1JhnAmrCXtOnkicKEhDkdhQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/credential-provider-web-identity@3.966.0': - resolution: {integrity: sha512-Yv1lc9iic9xg3ywMmIAeXN1YwuvfcClLVdiF2y71LqUgIOupW8B8my84XJr6pmOQuKzZa++c2znNhC9lGsbKyw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/dynamodb-codec@3.965.0': - resolution: {integrity: sha512-YFyhNyNYTsfelCS9e28VQgc7/J3fQYgxwy0Eq9o+vIfk4r8DK2T+J+3JgZy+SpNi6S7zCA1clVNVrw38T72UFw==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.965.0 - - '@aws-sdk/endpoint-cache@3.965.0': - resolution: {integrity: sha512-7bqt08b1aVgHHRbdUK9iIMI2CKpaeYs3cLNeZ/Js7GRAQIRQ0OsRILNxGdtWrkVbpp8ITM++iWJCGUw/X19EdA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/lib-dynamodb@3.965.0': - resolution: {integrity: sha512-xVvGDZDV3nHhbfXVfcZh4MJEW8Is8/sAwFP8EK9rVTJmlWEwgfk0DhSfP6dZq0irfUDK+Pl6gd5j2AB/R2IrkA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.965.0 - - '@aws-sdk/middleware-bucket-endpoint@3.966.0': - resolution: {integrity: sha512-KMPZ7gtFXErd9pMpXJMBwFlxxlGIaIQrUBfj3ea7rlrNtoVHnSI4qsoldLq5l9/Ho64KoCiICH4+qXjze8JTDQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-endpoint-discovery@3.965.0': - resolution: {integrity: sha512-XVUV2FmJnDG0FCCeTw0wniP1EkDR+Dy3Pl91aTkRvUw9OYbTEPLGIZiABn1EKdQPOEc77Y1+FQQ6QbjSZKkJ/A==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-expect-continue@3.965.0': - resolution: {integrity: sha512-UBxVytsmhEmFwkBnt+aV0eAJ7uc+ouNokCqMBrQ7Oc5A77qhlcHfOgXIKz2SxqsiYTsDq+a0lWFM/XpyRWraqA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-flexible-checksums@3.966.0': - resolution: {integrity: sha512-0/ofXeceTH/flKhg4EGGYr4cDtaLVkR/2RI05J/hxrHIls+iM6j8++GO0TocxmZYK+8B+7XKSaV9LU26nboTUQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-host-header@3.965.0': - resolution: {integrity: sha512-SfpSYqoPOAmdb3DBsnNsZ0vix+1VAtkUkzXM79JL3R5IfacpyKE2zytOgVAQx/FjhhlpSTwuXd+LRhUEVb3MaA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-location-constraint@3.965.0': - resolution: {integrity: sha512-07T1rwAarQs33mVg5U28AsSdLB5JUXu9yBTBmspFGajKVsEahIyntf53j9mAXF1N2KR0bNdP0J4A0kst4t43UQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-logger@3.965.0': - resolution: {integrity: sha512-gjUvJRZT1bUABKewnvkj51LAynFrfz2h5DYAg5/2F4Utx6UOGByTSr9Rq8JCLbURvvzAbCtcMkkIJRxw+8Zuzw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-recursion-detection@3.965.0': - resolution: {integrity: sha512-6dvD+18Ni14KCRu+tfEoNxq1sIGVp9tvoZDZ7aMvpnA7mDXuRLrOjRQ/TAZqXwr9ENKVGyxcPl0cRK8jk1YWjA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-sdk-api-gateway@3.965.0': - resolution: {integrity: sha512-3JY8/vkIYUtaOgZ9Jf8YAUpZ+R9SUIF45TL5ut6fJ6p2Pgxkei9ketvv2uD88Sso2vLAPSTNT+Dsr1bLnejNqg==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.966.0': - resolution: {integrity: sha512-9N9zncsY5ydDCRatKdrPZcdCwNWt7TdHmqgwQM52PuA5gs1HXWwLLNDy/51H+9RTHi7v6oly+x9utJ/qypCh2g==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-ssec@3.965.0': - resolution: {integrity: sha512-dke++CTw26y+a2D1DdVuZ4+2TkgItdx6TeuE0zOl4lsqXGvTBUG4eaIZalt7ZOAW5ys2pbDOk1bPuh4opoD3pQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-user-agent@3.965.0': - resolution: {integrity: sha512-RBEYVGgu/WeAt+H/qLrGc+t8LqAUkbyvh3wBfTiuAD+uBcWsKnvnB1iSBX75FearC0fmoxzXRUc0PMxMdqpjJQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-user-agent@3.966.0': - resolution: {integrity: sha512-MvGoy0vhMluVpSB5GaGJbYLqwbZfZjwEZhneDHdPhgCgQqmCtugnYIIjpUw7kKqWGsmaMQmNEgSFf1zYYmwOyg==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/nested-clients@3.965.0': - resolution: {integrity: sha512-muNVUjUEU+/KLFrLzQ8PMXyw4+a/MP6t4GIvwLtyx/kH0rpSy5s0YmqacMXheuIe6F/5QT8uksXGNAQenitkGQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/nested-clients@3.966.0': - resolution: {integrity: sha512-FRzAWwLNoKiaEWbYhnpnfartIdOgiaBLnPcd3uG1Io+vvxQUeRPhQIy4EfKnT3AuA+g7gzSCjMG2JKoJOplDtQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/region-config-resolver@3.965.0': - resolution: {integrity: sha512-RoMhu9ly2B0coxn8ctXosPP2WmDD0MkQlZGLjoYHQUOCBmty5qmCxOqBmBDa6wbWbB8xKtMQ/4VXloQOgzjHXg==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.966.0': - resolution: {integrity: sha512-VNSpyfKtDiBg/nPwSXDvnjISaDE9mI8zhOK3C4/obqh8lK1V6j04xDlwyIWbbIM0f6VgV1FVixlghtJB79eBqA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/token-providers@3.965.0': - resolution: {integrity: sha512-aR0qxg0b8flkXJVE+CM1gzo7uJ57md50z2eyCwofC0QIz5Y0P7/7vvb9/dmUQt6eT9XRN5iRcUqq2IVxVDvJOw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/token-providers@3.966.0': - resolution: {integrity: sha512-8k5cBTicTGYJHhKaweO4gL4fud1KDnLS5fByT6/Xbiu59AxYM4E/h3ds+3jxDMnniCE3gIWpEnyfM9khtmw2lA==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/types@3.965.0': - resolution: {integrity: sha512-jvodoJdMavvg8faN7co58vVJRO5MVep4JFPRzUNCzpJ98BDqWDk/ad045aMJcmxkLzYLS2UAnUmqjJ/tUPNlzQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/util-arn-parser@3.966.0': - resolution: {integrity: sha512-WcCLdKBK2nHhtOPE8du5XjOXaOToxGF3Ge8rgK2jaRpjkzjS0/mO+Jp2H4+25hOne3sP2twBu5BrvD9KoXQ5LQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/util-dynamodb@3.965.0': - resolution: {integrity: sha512-xQ6zEAUWh2K6gxc0vjfObcj/KyKxTbTJ7S5l+b9DiiRBZPiJzBGRRLxt86BdKPwTQwZCX+i4/FmUJmMsRrp9Mg==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.965.0 - - '@aws-sdk/util-endpoints@3.965.0': - resolution: {integrity: sha512-WqSCB0XIsGUwZWvrYkuoofi2vzoVHqyeJ2kN+WyoOsxPLTiQSBIoqm/01R/qJvoxwK/gOOF7su9i84Vw2NQQpQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/util-locate-window@3.965.0': - resolution: {integrity: sha512-9LJFand4bIoOjOF4x3wx0UZYiFZRo4oUauxQSiEX2dVg+5qeBOJSjp2SeWykIE6+6frCZ5wvWm2fGLK8D32aJw==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/util-user-agent-browser@3.965.0': - resolution: {integrity: sha512-Xiza/zMntQGpkd2dETQeAK8So1pg5+STTzpcdGWxj5q0jGO5ayjqT/q1Q7BrsX5KIr6PvRkl9/V7lLCv04wGjQ==} - - '@aws-sdk/util-user-agent-node@3.965.0': - resolution: {integrity: sha512-kokIHUfNT3/P55E4fUJJrFHuuA9BbjFKUIxoLrd3UaRfdafT0ScRfg2eaZie6arf60EuhlUIZH0yALxttMEjxQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - - '@aws-sdk/util-user-agent-node@3.966.0': - resolution: {integrity: sha512-vPPe8V0GLj+jVS5EqFz2NUBgWH35favqxliUOvhp8xBdNRkEjiZm5TqitVtFlxS4RrLY3HOndrWbrP5ejbwl1Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - - '@aws-sdk/xml-builder@3.965.0': - resolution: {integrity: sha512-Tcod25/BTupraQwtb+Q+GX8bmEZfxIFjjJ/AvkhUZsZlkPeVluzq1uu3Oeqf145DCdMjzLIN6vab5MrykbDP+g==} - engines: {node: '>=18.0.0'} - - '@aws/lambda-invoke-store@0.2.2': - resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==} - engines: {node: '>=18.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -1115,6 +801,9 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} @@ -1441,107 +1130,6 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@hapi/accept@6.0.3': - resolution: {integrity: sha512-p72f9k56EuF0n3MwlBNThyVE5PXX40g+aQh+C/xbKrfzahM2Oispv3AXmOIU51t3j77zay1qrX7IIziZXspMlw==} - - '@hapi/ammo@6.0.1': - resolution: {integrity: sha512-pmL+nPod4g58kXrMcsGLp05O2jF4P2Q3GiL8qYV7nKYEh3cGf+rV4P5Jyi2Uq0agGhVU63GtaSAfBEZOlrJn9w==} - - '@hapi/b64@6.0.1': - resolution: {integrity: sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw==} - - '@hapi/boom@10.0.1': - resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==} - - '@hapi/bounce@3.0.2': - resolution: {integrity: sha512-d0XmlTi3H9HFDHhQLjg4F4auL1EY3Wqj7j7/hGDhFFe6xAbnm3qiGrXeT93zZnPH8gH+SKAFYiRzu26xkXcH3g==} - - '@hapi/bourne@3.0.0': - resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} - - '@hapi/call@9.0.1': - resolution: {integrity: sha512-uPojQRqEL1GRZR4xXPqcLMujQGaEpyVPRyBlD8Pp5rqgIwLhtveF9PkixiKru2THXvuN8mUrLeet5fqxKAAMGg==} - - '@hapi/catbox-memory@6.0.2': - resolution: {integrity: sha512-H1l4ugoFW/ZRkqeFrIo8p1rWN0PA4MDTfu4JmcoNDvnY975o29mqoZblqFTotxNHlEkMPpIiIBJTV+Mbi+aF0g==} - - '@hapi/catbox@12.1.1': - resolution: {integrity: sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw==} - - '@hapi/content@6.0.0': - resolution: {integrity: sha512-CEhs7j+H0iQffKfe5Htdak5LBOz/Qc8TRh51cF+BFv0qnuph3Em4pjGVzJMkI2gfTDdlJKWJISGWS1rK34POGA==} - - '@hapi/cryptiles@6.0.3': - resolution: {integrity: sha512-r6VKalpbMHz4ci3gFjFysBmhwCg70RpYZy6OkjEpdXzAYnYFX5XsW7n4YMJvuIYpnMwLxGUjK/cBhA7X3JDvXw==} - engines: {node: '>=14.0.0'} - - '@hapi/file@3.0.0': - resolution: {integrity: sha512-w+lKW+yRrLhJu620jT3y+5g2mHqnKfepreykvdOcl9/6up8GrQQn+l3FRTsjHTKbkbfQFkuksHpdv2EcpKcJ4Q==} - - '@hapi/h2o2@10.0.4': - resolution: {integrity: sha512-dvD8+Y/Okc0fh0blqaYCLIrcy0+1LqIhMr7hjk8elLQZ9mkw2hKFB9dFKuRfWf+1nvHpGlW+PwccqkdebynQbg==} - engines: {node: '>=14.0.0'} - - '@hapi/hapi@21.4.4': - resolution: {integrity: sha512-vI6JPLR99WZDKI1nriD0qXDPp8sKFkZfNVGrDDZafDQ8jU+3ERMwS0vPac5aGae6yyyoGZGOBiYExw4N8ScSTQ==} - engines: {node: '>=14.15.0'} - - '@hapi/heavy@8.0.1': - resolution: {integrity: sha512-gBD/NANosNCOp6RsYTsjo2vhr5eYA3BEuogk6cxY0QdhllkkTaJFYtTXv46xd6qhBVMbMMqcSdtqey+UQU3//w==} - - '@hapi/hoek@11.0.7': - resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} - - '@hapi/iron@7.0.1': - resolution: {integrity: sha512-tEZnrOujKpS6jLKliyWBl3A9PaE+ppuL/+gkbyPPDb/l2KSKQyH4lhMkVb+sBhwN+qaxxlig01JRqB8dk/mPxQ==} - - '@hapi/mimos@7.0.1': - resolution: {integrity: sha512-b79V+BrG0gJ9zcRx1VGcCI6r6GEzzZUgiGEJVoq5gwzuB2Ig9Cax8dUuBauQCFKvl2YWSWyOc8mZ8HDaJOtkew==} - - '@hapi/nigel@5.0.1': - resolution: {integrity: sha512-uv3dtYuB4IsNaha+tigWmN8mQw/O9Qzl5U26Gm4ZcJVtDdB1AVJOwX3X5wOX+A07qzpEZnOMBAm8jjSqGsU6Nw==} - engines: {node: '>=14.0.0'} - - '@hapi/pez@6.1.0': - resolution: {integrity: sha512-+FE3sFPYuXCpuVeHQ/Qag1b45clR2o54QoonE/gKHv9gukxQ8oJJZPR7o3/ydDTK6racnCJXxOyT1T93FCJMIg==} - - '@hapi/podium@5.0.2': - resolution: {integrity: sha512-T7gf2JYHQQfEfewTQFbsaXoZxSvuXO/QBIGljucUQ/lmPnTTNAepoIKOakWNVWvo2fMEDjycu77r8k6dhreqHA==} - - '@hapi/shot@6.0.2': - resolution: {integrity: sha512-WKK1ShfJTrL1oXC0skoIZQYzvLsyMDEF8lfcWuQBjpjCN29qivr9U36ld1z0nt6edvzv28etNMOqUF4klnHryw==} - - '@hapi/somever@4.1.1': - resolution: {integrity: sha512-lt3QQiDDOVRatS0ionFDNrDIv4eXz58IibQaZQDOg4DqqdNme8oa0iPWcE0+hkq/KTeBCPtEOjDOBKBKwDumVg==} - - '@hapi/statehood@8.2.1': - resolution: {integrity: sha512-xf72TG/QINW26jUu+uL5H+crE1o8GplIgfPWwPZhnAGJzetIVAQEQYvzq+C0aEVHg5/lMMtQ+L9UryuSa5Yjkg==} - - '@hapi/subtext@8.1.1': - resolution: {integrity: sha512-ex1Y2s/KuJktS8Ww0k6XJ5ysSKrzNym4i5pDVuCwlSgHHviHUsT1JNzE6FYhNU9TTHSNdyfue/t2m89bpkX9Jw==} - - '@hapi/teamwork@6.0.1': - resolution: {integrity: sha512-52OXRslUfYwXAOG8k58f2h2ngXYQGP0x5RPOo+eWA/FtyLgHjGMrE3+e9LSXP/0q2YfHAK5wj9aA9DTy1K+kyQ==} - engines: {node: '>=14.0.0'} - - '@hapi/topo@6.0.2': - resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} - - '@hapi/validate@2.0.1': - resolution: {integrity: sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==} - - '@hapi/vise@5.0.1': - resolution: {integrity: sha512-XZYWzzRtINQLedPYlIkSkUr7m5Ddwlu99V9elh8CSygXstfv3UnWIXT0QD+wmR0VAG34d2Vx3olqcEhRRoTu9A==} - - '@hapi/wreck@18.1.0': - resolution: {integrity: sha512-0z6ZRCmFEfV/MQqkQomJ7sl/hyxvcZM7LtuVqN3vdAO4vM9eBbowl0kaqQj9EJJQab+3Uuh1GxbGIBFy4NfJ4w==} - - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1558,15 +1146,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@isaacs/cliui@9.0.0': resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} engines: {node: '>=18'} @@ -1590,27 +1169,45 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jsep-plugin/assignment@1.3.0': - resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} - '@jsep-plugin/regex@1.0.4': - resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} - engines: {node: '>= 10.16.0'} + '@nestjs/common@10.4.22': + resolution: {integrity: sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==} peerDependencies: - jsep: ^0.4.0||^1.0.0 - - '@kwsites/file-exists@1.1.1': - resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true - '@kwsites/promise-deferred@1.1.1': - resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + '@nestjs/core@10.4.22': + resolution: {integrity: sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} + '@nestjs/platform-express@10.4.22': + resolution: {integrity: sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/core': ^10.0.0 '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -1624,13 +1221,45 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@paralleldrive/cuid2@2.3.1': - resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@nuxtjs/opencollective@0.3.2': + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true '@pkgr/core@0.2.9': resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@prisma/client@6.19.3': + resolution: {integrity: sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==} + engines: {node: '>=18.18'} + peerDependencies: + prisma: '*' + typescript: '>=5.1.0' + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + '@prisma/config@6.19.3': + resolution: {integrity: sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==} + + '@prisma/debug@6.19.3': + resolution: {integrity: sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==} + + '@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': + resolution: {integrity: sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==} + + '@prisma/engines@6.19.3': + resolution: {integrity: sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==} + + '@prisma/fetch-engine@6.19.3': + resolution: {integrity: sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==} + + '@prisma/get-platform@6.19.3': + resolution: {integrity: sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==} + '@reduxjs/toolkit@2.11.2': resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: @@ -1823,273 +1452,38 @@ packages: cpu: [x64] os: [win32] - '@serverless/dashboard-plugin@7.2.3': - resolution: {integrity: sha512-Vu4TKJLEQ5F8ZipfCvd8A/LMIdH8kNGe448sX9mT4/Z0JVUaYmMc3BwkQ+zkNIh3QdBKAhocGn45TYjHV6uPWQ==} - engines: {node: '>=12.0'} - - '@serverless/event-mocks@1.1.1': - resolution: {integrity: sha512-YAV5V/y+XIOfd+HEVeXfPWZb8C6QLruFk9tBivoX2roQLWVq145s4uxf8D0QioCueuRzkukHUS4JIj+KVoS34A==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@serverless/platform-client@4.5.1': - resolution: {integrity: sha512-XltmO/029X76zi0LUFmhsnanhE2wnqH1xf+WBt5K8gumQA9LnrfwLgPxj+VA+mm6wQhy+PCp7H5SS0ZPu7F2Cw==} - engines: {node: '>=10.0'} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@serverless/utils@6.15.0': - resolution: {integrity: sha512-7eDbqKv/OBd11jjdZjUwFGN8sHWkeUqLeHXHQxQ1azja2IM7WIH+z/aLgzR6LhB3/MINNwtjesDpjGqTMj2JKQ==} - engines: {node: '>=12.0'} + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@smithy/abort-controller@4.2.7': - resolution: {integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==} - engines: {node: '>=18.0.0'} + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@smithy/chunked-blob-reader-native@4.2.1': - resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} - engines: {node: '>=18.0.0'} + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 - '@smithy/chunked-blob-reader@5.2.0': - resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} - engines: {node: '>=18.0.0'} - - '@smithy/config-resolver@4.4.5': - resolution: {integrity: sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==} - engines: {node: '>=18.0.0'} - - '@smithy/core@3.20.1': - resolution: {integrity: sha512-wOboSEdQ85dbKAJ0zL+wQ6b0HTSBRhtGa0PYKysQXkRg+vK0tdCRRVruiFM2QMprkOQwSYOnwF4og96PAaEGag==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.2.7': - resolution: {integrity: sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-codec@4.2.7': - resolution: {integrity: sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-browser@4.2.7': - resolution: {integrity: sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-config-resolver@4.3.7': - resolution: {integrity: sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-node@4.2.7': - resolution: {integrity: sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==} - engines: {node: '>=18.0.0'} - - '@smithy/eventstream-serde-universal@4.2.7': - resolution: {integrity: sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.3.8': - resolution: {integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-blob-browser@4.2.8': - resolution: {integrity: sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-node@4.2.7': - resolution: {integrity: sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==} - engines: {node: '>=18.0.0'} - - '@smithy/hash-stream-node@4.2.7': - resolution: {integrity: sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==} - engines: {node: '>=18.0.0'} - - '@smithy/invalid-dependency@4.2.7': - resolution: {integrity: sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==} - engines: {node: '>=18.0.0'} - - '@smithy/is-array-buffer@2.2.0': - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} - - '@smithy/is-array-buffer@4.2.0': - resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} - engines: {node: '>=18.0.0'} - - '@smithy/md5-js@4.2.7': - resolution: {integrity: sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-content-length@4.2.7': - resolution: {integrity: sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-endpoint@4.4.2': - resolution: {integrity: sha512-mqpAdux0BNmZu/SqkFhQEnod4fX23xxTvU2LUpmKp0JpSI+kPYCiHJMmzREr8yxbNxKL2/DU1UZm9i++ayU+2g==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-retry@4.4.18': - resolution: {integrity: sha512-E5hulijA59nBk/zvcwVMaS7FG7Y4l6hWA9vrW018r+8kiZef4/ETQaPI4oY+3zsy9f6KqDv3c4VKtO4DwwgpCg==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-serde@4.2.8': - resolution: {integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==} - engines: {node: '>=18.0.0'} - - '@smithy/middleware-stack@4.2.7': - resolution: {integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==} - engines: {node: '>=18.0.0'} - - '@smithy/node-config-provider@4.3.7': - resolution: {integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.4.7': - resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==} - engines: {node: '>=18.0.0'} - - '@smithy/property-provider@4.2.7': - resolution: {integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==} - engines: {node: '>=18.0.0'} - - '@smithy/protocol-http@5.3.7': - resolution: {integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-builder@4.2.7': - resolution: {integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==} - engines: {node: '>=18.0.0'} - - '@smithy/querystring-parser@4.2.7': - resolution: {integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==} - engines: {node: '>=18.0.0'} - - '@smithy/service-error-classification@4.2.7': - resolution: {integrity: sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==} - engines: {node: '>=18.0.0'} - - '@smithy/shared-ini-file-loader@4.4.2': - resolution: {integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.3.7': - resolution: {integrity: sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==} - engines: {node: '>=18.0.0'} - - '@smithy/smithy-client@4.10.3': - resolution: {integrity: sha512-EfECiO/0fAfb590LBnUe7rI5ux7XfquQ8LBzTe7gxw0j9QW/q8UT/EHWHlxV/+jhQ3+Ssga9uUYXCQgImGMbNg==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.11.0': - resolution: {integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==} - engines: {node: '>=18.0.0'} - - '@smithy/url-parser@4.2.7': - resolution: {integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-base64@4.3.0': - resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-browser@4.2.0': - resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-body-length-node@4.2.1': - resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-buffer-from@2.2.0': - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} - - '@smithy/util-buffer-from@4.2.0': - resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} - engines: {node: '>=18.0.0'} - - '@smithy/util-config-provider@4.2.0': - resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-browser@4.3.17': - resolution: {integrity: sha512-dwN4GmivYF1QphnP3xJESXKtHvkkvKHSZI8GrSKMVoENVSKW2cFPRYC4ZgstYjUHdR3zwaDkIaTDIp26JuY7Cw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-defaults-mode-node@4.2.20': - resolution: {integrity: sha512-VD/I4AEhF1lpB3B//pmOIMBNLMrtdMXwy9yCOfa2QkJGDr63vH3RqPbSAKzoGMov3iryCxTXCxSsyGmEB8PDpg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-endpoints@3.2.7': - resolution: {integrity: sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-hex-encoding@4.2.0': - resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-middleware@4.2.7': - resolution: {integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==} - engines: {node: '>=18.0.0'} - - '@smithy/util-retry@4.2.7': - resolution: {integrity: sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==} - engines: {node: '>=18.0.0'} - - '@smithy/util-stream@4.5.8': - resolution: {integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==} - engines: {node: '>=18.0.0'} - - '@smithy/util-uri-escape@4.2.0': - resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} - engines: {node: '>=18.0.0'} - - '@smithy/util-utf8@2.3.0': - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} - - '@smithy/util-utf8@4.2.0': - resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} - engines: {node: '>=18.0.0'} - - '@smithy/util-waiter@4.2.7': - resolution: {integrity: sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==} - engines: {node: '>=18.0.0'} - - '@smithy/uuid@1.1.0': - resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} - engines: {node: '>=18.0.0'} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@standard-schema/utils@0.3.0': - resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - - '@surma/rollup-plugin-off-main-thread@2.2.3': - resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} - - '@svgr/babel-plugin-add-jsx-attribute@8.0.0': - resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': - resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': - resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': - resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 '@svgr/babel-plugin-svg-dynamic-title@8.0.0': resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} @@ -2135,20 +1529,13 @@ packages: peerDependencies: '@svgr/core': '*' - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@tootallnate/once@1.1.2': - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} - - '@types/aws-lambda@8.10.159': - resolution: {integrity: sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2161,9 +1548,6 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - '@types/chai-subset@1.3.6': resolution: {integrity: sha512-m8lERkkQj+uek18hXOZuec3W/fCRTrU4hrnXjH3qhHy96ytuPaPiWGgu7sJb7tZxZonO75vYAjCvpe/e4VUwRw==} peerDependencies: @@ -2211,18 +1595,9 @@ packages: '@types/file-saver@2.0.7': resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - - '@types/lodash@4.17.23': - resolution: {integrity: sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==} - '@types/node@20.19.27': resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} @@ -2237,9 +1612,6 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -2326,9 +1698,9 @@ packages: '@vitest/utils@0.31.4': resolution: {integrity: sha512-DobZbHacWznoGUfYU8XDPY78UubJxXfMNY1+SUdOp1NsI34eopSA6aZMeaGu10waSOeYwE8lxrd/pLfT0RMxjQ==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -2353,50 +1725,16 @@ packages: resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} engines: {node: '>=0.8'} - adm-zip@0.5.16: - resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} - engines: {node: '>=12.0'} - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -2405,10 +1743,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2416,28 +1750,12 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - archive-type@4.0.0: - resolution: {integrity: sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==} - engines: {node: '>=4'} - - archiver-utils@2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} - - archiver-utils@3.0.4: - resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} - engines: {node: '>= 10'} - - archiver@5.3.2: - resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} - engines: {node: '>= 10'} + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2445,18 +1763,13 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} - array-unflat-js@0.1.3: - resolution: {integrity: sha512-8pljkLj4vfz2i7Tf3yB31tRrszjP8/kwIyABGfcZ1GcHlvdUB0Sbx0WzQkOPMqUBxa/bu4+/NAyHEpDtZJzlJw==} - engines: {node: '>=14.18.0'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} @@ -2477,16 +1790,9 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -2494,9 +1800,6 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -2512,20 +1815,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-dynamodb-local@0.0.11: - resolution: {integrity: sha512-Vz7UAgNY3bEb7nWAQvEg5VGP0nF8nJ3QEExf/fiv4jdLdfEfobv0w/iaSjxFVK9V5emamhmqNSvnUF9EVQQa+w==} - - aws-lambda@1.0.7: - resolution: {integrity: sha512-9GNFMRrEMG5y3Jvv+V4azWvc+qNWdWLTjDdhf/zgMlz8haaaLWv0xeAIWxz9PuWUBawsVxy0zZotjCdR3Xq+2w==} - hasBin: true - - aws-sdk@2.1693.0: - resolution: {integrity: sha512-cJmb8xEnVLT+R6fBS5sn/EFJiX7tUnDaPtOPZ1vFbOJtd0fnZn/Ky2XGgsvvoeliWeH7mL3TWSX5zXXGSQV6gQ==} - engines: {node: '>= 10.0.0'} - - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - babel-plugin-polyfill-corejs2@0.4.15: resolution: {integrity: sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==} peerDependencies: @@ -2548,9 +1837,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.12: resolution: {integrity: sha512-Mij6Lij93pTAIsSYy5cyBQ975Qh9uLEc5rwGTpomiZeXZL9yIS6uORJakb3ScHgfs0serMMfIbXzokPMuEiRyw==} hasBin: true @@ -2559,24 +1845,12 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bl@1.2.3: - resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} - bowser@2.13.1: - resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} - - boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -2597,63 +1871,29 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-alloc-unsafe@1.1.0: - resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} - - buffer-alloc@1.2.0: - resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-fill@1.0.0: - resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@4.9.2: - resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - - builtins@1.0.3: - resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} - - bundle-require@5.1.0: - resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - peerDependencies: - esbuild: '>=0.18' + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + c12@3.1.0: + resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} - - cachedir@2.4.0: - resolution: {integrity: sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==} - engines: {node: '>=6'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2678,10 +1918,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - caniuse-lite@1.0.30001763: resolution: {integrity: sha512-mh/dGtq56uN98LlNX9qdbKnzINhX0QzhiWBFEkFfsFO4QyCvL8YegrJAazCwXIeqkIob8BlZPGM3xdnY+sgmvQ==} @@ -2693,31 +1929,13 @@ packages: resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - child-process-ext@2.1.1: - resolution: {integrity: sha512-0UQ55f51JBkOFa+fvR76ywRzxiPwQS3Xe8oe5bZRphpv+dIMeerW5Zn5e4cUy4COJwVtJyU0R79RMnw+aCqmGA==} - - child-process-ext@3.0.2: - resolution: {integrity: sha512-oBePsLbQpTJFxzwyCvs9yWWF0OEM6vGGepHwt1stqmX7QQqOuDc8j2ywdvAs9Tvi44TT7d9ackqhR4Q10l1u8w==} - engines: {node: '>=8.0'} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2726,48 +1944,11 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - - cli-color@2.0.4: - resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==} - engines: {node: '>=0.10'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-progress-footer@2.3.3: - resolution: {integrity: sha512-p+hyTPxSZWG1c3Qy1DLBoGZhpeA3Y6AMlKrtbGpMMSKpezbSLel8gW4e5You4FNlHb3wS/M1JU594OAWe/Totg==} - engines: {node: '>=10.0'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-sprintf-format@1.1.1: - resolution: {integrity: sha512-BbEjY9BEdA6wagVwTqPvmAwGB24U93rQPBFZUT8lNCDxXzre5LFHQUTJc70czjgUomVg8u8R5kW8oY9DYRFNeg==} - engines: {node: '>=6.0'} + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} @@ -2777,32 +1958,19 @@ packages: resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} engines: {node: '>=0.8'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} colord@2.9.3: resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@3.0.2: - resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} - commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2811,16 +1979,13 @@ packages: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - - compress-commons@4.1.2: - resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} - engines: {node: '>= 10'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + concordance@5.0.4: resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} @@ -2828,6 +1993,12 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -2836,17 +2007,26 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookiejar@2.1.4: - resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} core-js-compat@3.48.0: resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} @@ -2862,18 +2042,6 @@ packages: engines: {node: '>=0.8'} hasBin: true - crc32-stream@4.0.3: - resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} - engines: {node: '>= 10'} - - cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} - - cross-spawn@6.0.6: - resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} - engines: {node: '>=4.8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2937,18 +2105,10 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} - d@1.0.2: - resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} - engines: {node: '>=0.12'} - daisyui@3.9.4: resolution: {integrity: sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==} engines: {node: '>=16.9.0'} - data-uri-to-buffer@3.0.1: - resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} - engines: {node: '>= 6'} - data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -2968,8 +2128,13 @@ packages: resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} engines: {node: '>=6'} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -2983,30 +2148,6 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - decompress-tar@4.1.1: - resolution: {integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==} - engines: {node: '>=4'} - - decompress-tarbz2@4.1.1: - resolution: {integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==} - engines: {node: '>=4'} - - decompress-targz@4.1.1: - resolution: {integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==} - engines: {node: '>=4'} - - decompress-unzip@4.0.1: - resolution: {integrity: sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==} - engines: {node: '>=4'} - - decompress@4.2.1: - resolution: {integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==} - engines: {node: '>=4'} - deep-eql@4.1.4: resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} engines: {node: '>=6'} @@ -3014,57 +2155,39 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge-ts@7.1.5: + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - deferred@0.7.11: - resolution: {integrity: sha512-8eluCl/Blx4YOGwMapBvXRKxHXhA8ejDXYzEaK8+/gtcm8hRMhSLmXSqDmNUKNc/C8HNSmuyyp/hflhqDAvK2A==} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - degenerator@3.0.4: - resolution: {integrity: sha512-Z66uPeBfHZAHVmue3HPfyKu2Q0rC2cRxbTOsvmU/po5fvvcx27W4mIu9n0PUlQih4oUYvcG1BsbtVv8x7KDOSw==} - engines: {node: '>= 6'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - desm@1.3.1: - resolution: {integrity: sha512-vgTAOosB1aHrmzjGnzFCbjvXbk8QAOC/36JxJhcBkeAuUy8QwRFxAWBHemiDpUB3cbrBruFUdzpUS21aocvaWg==} + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - dezalgo@1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -3078,10 +2201,6 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv-expand@10.0.0: - resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} - engines: {node: '>=12'} - dotenv@16.6.1: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} @@ -3090,11 +2209,11 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - duration@0.2.2: - resolution: {integrity: sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + effect@3.21.0: + resolution: {integrity: sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==} ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} @@ -3104,14 +2223,13 @@ packages: electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.0: + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -3152,24 +2270,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es5-ext@0.10.64: - resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} - engines: {node: '>=0.10'} - - es6-iterator@2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - - es6-set@0.1.6: - resolution: {integrity: sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==} - engines: {node: '>=0.12'} - - es6-symbol@3.1.4: - resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} - engines: {node: '>=0.12'} - - es6-weak-map@2.0.3: - resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} - esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -3184,19 +2284,13 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@1.14.3: - resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} - engines: {node: '>=4.0'} - hasBin: true - eslint-config-prettier@9.1.2: resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} hasBin: true @@ -3287,23 +2381,10 @@ packages: jiti: optional: true - esniff@1.1.3: - resolution: {integrity: sha512-SLBLpfE7xWgF/HbzhVuAwqnJDRqSCNZqcqaIMVm+f+PbTp1kFRWu6BuT83SATb4Tp+ovr+S+u7vDH7/UErAOkw==} - engines: {node: '>=0.10'} - - esniff@2.0.1: - resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} - engines: {node: '>=0.10'} - espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -3312,13 +2393,6 @@ packages: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} - essentials@1.2.0: - resolution: {integrity: sha512-kP/j7Iw7KeNE8b/o7+tr9uX2s1wegElGOoGZ2Xm35qBr4BbbEcH3/bxR2nfH9l9JANCq9AUrvKw+gRuHtZp0HQ==} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -3333,38 +2407,23 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - event-emitter@0.3.5: - resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - events@1.1.1: - resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} - engines: {node: '>=0.4.x'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - ext-list@2.2.2: - resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} - engines: {node: '>=0.10.0'} + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} - ext-name@5.0.0: - resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} - engines: {node: '>=4'} + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} - ext@1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3392,26 +2451,12 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@5.2.5: - resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} - hasBin: true - - fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} - fastparse@1.1.2: resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fd-slicer@1.0.1: - resolution: {integrity: sha512-MX1ZLPIuKED51hrI4++K+1B0VX87Cs4EkybD2q12Ysuf5p4vkmHqMvQJRlDwROqFr4D2Pzyit5wGQxf30grIcw==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -3421,9 +2466,8 @@ packages: picomatch: optional: true - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} @@ -3432,84 +2476,32 @@ packages: file-saver@2.0.5: resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} - file-type@16.5.4: - resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} - engines: {node: '>=10'} - - file-type@3.9.0: - resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} - engines: {node: '>=0.10.0'} - - file-type@4.4.0: - resolution: {integrity: sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==} - engines: {node: '>=4'} - - file-type@5.2.0: - resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} - engines: {node: '>=4'} - - file-type@6.2.0: - resolution: {integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==} - engines: {node: '>=4'} - - file-uri-to-path@2.0.0: - resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==} - engines: {node: '>= 6'} + file-type@20.4.1: + resolution: {integrity: sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==} + engines: {node: '>=18'} filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} - filename-reserved-regex@2.0.0: - resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} - engines: {node: '>=4'} - - filenamify@4.3.0: - resolution: {integrity: sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==} - engines: {node: '>=8'} - - filesize@10.1.6: - resolution: {integrity: sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==} - engines: {node: '>= 10.4.0'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-requires@1.0.0: - resolution: {integrity: sha512-UME7hNwBfzeISSFQcBEDemEEskpOjI/shPrpJM5PI4DSdn6hX0dmz+2dL70blZER2z8tSnTRL+2rfzlYgtbBoQ==} - hasBin: true + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - findit2@2.2.3: - resolution: {integrity: sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==} - engines: {node: '>=0.8.22'} - - fix-dts-default-cjs-exports@1.0.1: - resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} - flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -3518,12 +2510,9 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - formidable@2.1.5: - resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} frac@1.1.2: resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} @@ -3532,45 +2521,19 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} - engines: {node: '>=14.14'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fs2@0.3.16: - resolution: {integrity: sha512-gf/9tXLWI7qKmHDrMz55TRrTj12iceKuwo30CG1+Vbae719LT4uFc++GwDG8y/4vZJ34a6pzhgY23bgg88cZDg==} - engines: {node: '>=6'} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - ftp@0.3.10: - resolution: {integrity: sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==} - engines: {node: '>=0.8.0'} - function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3603,26 +2566,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - - get-stream@2.3.1: - resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} - engines: {node: '>=0.10.0'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -3630,9 +2573,9 @@ packages: get-tsconfig@4.13.0: resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - get-uri@3.0.2: - resolution: {integrity: sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==} - engines: {node: '>= 6'} + giget@2.0.0: + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + hasBin: true glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -3642,19 +2585,12 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3667,35 +2603,17 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - - graceful-fs@4.1.15: - resolution: {integrity: sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphlib@2.1.8: - resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3725,47 +2643,17 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.11.3: - resolution: {integrity: sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==} - engines: {node: '>=16.9.0'} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} - http-proxy-agent@4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} - - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} - engines: {node: '>=0.10.0'} - idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} - ieee754@1.1.13: - resolution: {integrity: sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==} - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3777,9 +2665,6 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immer@11.1.4: resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} @@ -3791,17 +2676,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inquirer@8.2.7: - resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} - engines: {node: '>=12.0.0'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -3810,16 +2687,9 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - - ip@1.1.9: - resolution: {integrity: sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==} - - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} @@ -3860,16 +2730,6 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -3878,10 +2738,6 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -3890,15 +2746,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -3906,9 +2753,6 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-natural-number@4.0.1: - resolution: {integrity: sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==} - is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} @@ -3925,13 +2769,6 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - is-promise@2.2.2: - resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} - is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -3948,18 +2785,10 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -3972,10 +2801,6 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -3988,30 +2813,15 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} - - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - - isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} @@ -4026,27 +2836,13 @@ packages: engines: {node: '>=10'} hasBin: true - java-invoke-local@0.0.6: - resolution: {integrity: sha512-gZmQKe1QrfkkMjCn8Qv9cpyJFyogTYqkP5WCobX5RNaHsJzIV/6NvAnlnouOcwKr29QrxLGDGcqYuJ+ae98s1A==} - hasBin: true - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jmespath@0.16.0: - resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} - engines: {node: '>= 0.6.0'} - - jose@5.10.0: - resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} - - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} - - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} @@ -4055,18 +2851,10 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} - hasBin: true - js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsep@1.4.0: - resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} - engines: {node: '>= 10.16.0'} - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -4075,21 +2863,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-colorizer@2.2.2: - resolution: {integrity: sha512-56oZtwV1piXrQnRNTtJeqRv+B9Y/dXAYLqBBaYl/COcUdoZxgLBLAO88+CnkbT6MxNs0c5E9mPBIb2sFcNz3vw==} - - json-cycle@1.5.0: - resolution: {integrity: sha512-GOehvd5PO2FeZ5T4c+RxobeT5a1PiGpF4u9/3+UvrMU4bhnVqzJY7hm39wg8PDCqkU91fWGH8qjWR4bn+wgq9w==} - engines: {node: '>= 4'} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-refs@3.0.15: - resolution: {integrity: sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==} - engines: {node: '>=0.8'} - hasBin: true - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -4107,59 +2883,28 @@ packages: engines: {node: '>=6'} hasBin: true - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - jsonpath-plus@10.3.0: - resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} - engines: {node: '>=18.0.0'} - hasBin: true - jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jsonschema@1.5.0: - resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} - jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} - jszip@3.10.1: - resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - - jwt-decode@2.2.0: - resolution: {integrity: sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==} - - jwt-decode@3.1.2: - resolution: {integrity: sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - levn@0.3.0: - resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} - engines: {node: '>= 0.8.0'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -4167,10 +2912,6 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} @@ -4182,54 +2923,18 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - - lodash.difference@4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - - lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - log-node@8.0.3: - resolution: {integrity: sha512-1UBwzgYiCIDFs8A0rM2QdBFo8Wd8UQ0HrSTu/MNI+/2zN3NoHRj2fhplurAyuxTYUXu3Oohugq1jAn5s05u1MQ==} - engines: {node: '>=10.0'} - peerDependencies: - log: ^6.0.0 - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - log@6.3.2: - resolution: {integrity: sha512-ek8NRg/OPvS9ISOJNWNAz5vZcpYacWNFDWNJjj5OXsc6YuKacfey6wF04cXz/tOJIVrZ2nGSkHpAY5qKtF6ISg==} - engines: {node: '>=0.12'} - - long-timeout@0.1.1: - resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4240,10 +2945,6 @@ packages: lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - lru-cache@11.2.6: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} @@ -4251,32 +2952,17 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-queue@0.1.0: - resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==} - lucide-react@0.562.0: resolution: {integrity: sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} - engines: {node: '>=12'} - magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-dir@1.3.0: - resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} - engines: {node: '>=4'} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -4285,12 +2971,12 @@ packages: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} - memoizee@0.4.17: - resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==} - engines: {node: '>=0.12'} + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -4308,34 +2994,14 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} + hasBin: true minimatch@10.2.4: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} @@ -4344,10 +3010,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -4359,42 +3021,26 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - mnemonist@0.38.3: - resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + multer@2.0.2: + resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==} + engines: {node: '>= 10.16.0'} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -4404,31 +3050,18 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - native-promise-only@0.8.1: - resolution: {integrity: sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - ncjsm@4.3.2: - resolution: {integrity: sha512-6d1VWA7FY31CpI4Ki97Fpm36jfURkVbpktizp8aoVViTZRQgr/0ddmlKerALSSlzfwQRBeSq1qwwVcBJK4Sk7Q==} - - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - - next-tick@1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - - nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-dir@0.1.17: - resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} - engines: {node: '>= 0.10.5'} + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} @@ -4442,25 +3075,14 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - node-schedule@2.1.1: - resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} - engines: {node: '>=6'} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - npm-registry-utilities@1.0.0: - resolution: {integrity: sha512-9xYfSJy2IFQw1i6462EJzjChL9e65EfSo2Cw6kl0EFeDp05VvU+anrQk3Fc0d1MbVCq7rWIxeer89O9SUQ/uOg==} - engines: {node: '>=12.0'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + nypm@0.6.8: + resolution: {integrity: sha512-Q9K4Diu6l5u6xJQogeFSs/zKtyMSgFKFtRQV+tHP4kL7KPm2grpBU0dFIwFaXwNxN0MtfKWc43VpCugAa+LPsw==} + engines: {node: '>=18'} + hasBin: true object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -4494,56 +3116,21 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - obliterator@1.6.1: - resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - open@7.4.2: - resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} - engines: {node: '>=8'} - - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - optionator@0.8.3: - resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} - engines: {node: '>= 0.8.0'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - p-event@4.2.0: - resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} - engines: {node: '>=8'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4556,28 +3143,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-memoize@7.1.1: - resolution: {integrity: sha512-DZ/bONJILHkQ721hSr/E9wMz5Am/OTJ9P6LhLFo2Tu+jL8044tgc9LwHO8g4PiaYePnlVVRAJcKmgy8J9MVFrA==} - engines: {node: '>=14.16'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - pac-proxy-agent@5.0.0: - resolution: {integrity: sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==} - engines: {node: '>= 8'} - - pac-resolver@5.0.1: - resolution: {integrity: sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==} - engines: {node: '>= 8'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4586,29 +3154,18 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-loader@1.0.12: - resolution: {integrity: sha512-n7oDG8B+k/p818uweWrOixY9/Dsr89o2TkCm6tOTex3fpdo2+BFDgR+KpB37mGKBRsBAlR8CIJMFN0OEy/7hIQ==} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -4616,13 +3173,16 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} + path-to-regexp@0.1.13: + resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} + + path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - path2@0.1.0: - resolution: {integrity: sha512-TX+cz8Jk+ta7IvRy2FAej8rdlbrP0+uBIkP/5DTODez/AuL/vSb30KuAdDxGVREXzn8QfAiu5mJYJ1XjbOhEPA==} - pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -4632,12 +3192,8 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - peek-readable@4.1.0: - resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} - engines: {node: '>=8'} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -4654,18 +3210,6 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - - pinkie-promise@2.0.1: - resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} - engines: {node: '>=0.10.0'} - - pinkie@2.0.4: - resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} - engines: {node: '>=0.10.0'} - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -4673,6 +3217,9 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pkg-types@2.3.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -4724,10 +3271,6 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - prelude-ls@1.1.2: - resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} - engines: {node: '>= 0.8.0'} - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4808,73 +3351,51 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process-utils@4.0.0: - resolution: {integrity: sha512-fMyMQbKCxX51YxR7YGCzPjLsU3yDzXFkP4oi1/Mt5Ixnk7GO/7uUTj8mrCHUwuvozWzI+V7QSJR9cZYnwNOZPg==} - engines: {node: '>=10.0'} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - promise-queue@2.2.5: - resolution: {integrity: sha512-p/iXrPSVfnqPft24ZdNNLECw/UrtLTpT3jpAAMzl/o5/rDsGCPo3/CQS2611flL6LkoEJ3oQZw7C8Q80ZISXRQ==} - engines: {node: '>= 0.8.0'} + prisma@6.19.3: + resolution: {integrity: sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==} + engines: {node: '>=18.18'} + hasBin: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - proxy-agent@5.0.0: - resolution: {integrity: sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==} - engines: {node: '>= 8'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - punycode@1.3.2: - resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qs@6.14.1: resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} - querystring@0.2.0: - resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - - querystring@0.2.1: - resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - randomstring@1.3.1: - resolution: {integrity: sha512-lgXZa80MUkjWdE7g2+PZ1xDLzc7/RokXVEQOv5NN2UOTChW1I8A9gha5a9xYBOqgaSoI6uJikDmCU8PyRdArRQ==} - hasBin: true + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} raw-body@2.5.3: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} engines: {node: '>= 0.8'} + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -4937,27 +3458,10 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - readable-stream@1.1.14: - resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readable-web-to-node-stream@3.0.4: - resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} - engines: {node: '>=8'} - - readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -4984,6 +3488,9 @@ packages: redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5017,17 +3524,10 @@ packages: reselect@5.1.1: resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -5040,22 +3540,10 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@2.2.8: - resolution: {integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rollup@2.80.0: resolution: {integrity: sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==} engines: {node: '>=10.0.0'} @@ -5071,13 +3559,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - - run-parallel-limit@1.1.0: - resolution: {integrity: sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -5088,9 +3569,6 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5105,20 +3583,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sax@1.2.1: - resolution: {integrity: sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==} - scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - seek-bzip@1.0.6: - resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} - hasBin: true - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -5128,31 +3595,16 @@ packages: engines: {node: '>=10'} hasBin: true + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serverless-cloudfront-invalidate@1.12.2: - resolution: {integrity: sha512-z6bt3+Rhuqw3o63mDG8BR4ok7b8TBiKN1WgMzEMMINneL6nyqNsASbkzJD3CN3CPtkrI4oca47/+F0JkPa4y5A==} - - serverless-dynamodb@0.2.56: - resolution: {integrity: sha512-b378F7yUMrT+atFZteHquSg2TlnnGug7jTsOdGQ+6eApAP/CpW+5HsO+CYXGDgVBl2UqTqt6/HeQqr7vN8g9tg==} - - serverless-offline@13.9.0: - resolution: {integrity: sha512-r/GU1FcgUbrhUzPJ5BOpni6JXhHVhxksYQH8d3S4alx4IZQo3E9Q8fT2z8Mvh+d0rfQ1Y0kHvyeKJ126rleGfg==} - engines: {node: '>=18.12.0'} - peerDependencies: - serverless: ^3.2.0 - - serverless-s3-sync@3.5.1: - resolution: {integrity: sha512-AL2+2GWAdNEgbxN9cLRRw5vTebhGaHTJDK9OJCU5x/AZpubzZkuFFQwrEhyI7mtP+M16dy7VgPvCLqddaXF1dw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - peerDependencies: - serverless: 3.x || 4.x - - serverless@3.40.0: - resolution: {integrity: sha512-6vUSIUqBkhZeIpFz0howqKlT1BNjYxOrucvvSICKCEsxVS9MbTJokGkykDrpr/k4Io3WI8tcvrf25+U5Ynf3lw==} - engines: {node: '>=12.0'} - hasBin: true + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} @@ -5166,24 +3618,13 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -5207,24 +3648,10 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-git@3.30.0: - resolution: {integrity: sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smob@1.6.1: resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} engines: {node: '>=20.0.0'} @@ -5232,25 +3659,6 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - socks-proxy-agent@5.0.1: - resolution: {integrity: sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==} - engines: {node: '>= 6'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - sort-keys-length@1.0.1: - resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} - engines: {node: '>=0.10.0'} - - sort-keys@1.1.2: - resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} - engines: {node: '>=0.10.0'} - - sorted-array-functions@1.3.0: - resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -5262,10 +3670,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} @@ -5275,16 +3679,6 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead - split2@3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sprintf-kit@2.0.2: - resolution: {integrity: sha512-lnapdj6W4LflHZGKvl9eVkz5YF0xaTrqpRWVA4cNVOTedwqifIP8ooGImldzT/4IAN5KXFQAyXTdLidYVQdyag==} - engines: {node: '>=0.12'} - ssf@0.11.2: resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} engines: {node: '>=0.8'} @@ -5303,23 +3697,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - stream-buffers@3.0.3: - resolution: {integrity: sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==} - engines: {node: '>= 0.10.0'} - - stream-promise@3.2.0: - resolution: {integrity: sha512-P+7muTGs2C8yRcgJw/PPt61q7O517tDHiwYEzMWo1GSBCcZedUMT/clz7vUNsSxFphIlJ6QUL4GexQKlfJoVtA==} - - streamsink@1.2.0: - resolution: {integrity: sha512-MJ440L2+j2vmc1v8Z/BkMx3X+HsJ++V7mgDROboQKxqCLZdNbu+AeSwQbayXw3LPHVAMxw+h7ZJUnyFYl/zp2g==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} @@ -5340,12 +3720,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -5353,25 +3727,10 @@ packages: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-comments@2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} - strip-dirs@2.1.0: - resolution: {integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -5379,43 +3738,19 @@ packages: strip-literal@1.3.0: resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} - strip-outer@1.0.1: - resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} - engines: {node: '>=0.10.0'} - - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} - - strtok3@6.3.0: - resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} - engines: {node: '>=10'} + strtok3@10.3.5: + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true - superagent@7.1.6: - resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} - engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@6.1.0: - resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==} - engines: {node: '>=6'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -5432,18 +3767,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tar-stream@1.6.2: - resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==} - engines: {node: '>= 0.8.0'} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -5464,28 +3787,19 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - throat@5.0.0: - resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - time-zone@1.0.0: resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==} engines: {node: '>=4'} - timers-ext@0.1.8: - resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} - engines: {node: '>=0.12'} - tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -5499,10 +3813,6 @@ packages: resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} engines: {node: '>=14.0.0'} - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -5511,9 +3821,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@4.2.1: - resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} - engines: {node: '>=10'} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -5521,18 +3831,6 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - traverse@0.6.11: - resolution: {integrity: sha512-vxXDZg8/+p3gblxB6BhhG5yWVn1kGRlaL8O78UDXc3wRnPizB5g83dcvWV1jpDMIPnjZjOFuxlMmE82XJ4407w==} - engines: {node: '>= 0.4'} - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - trim-repeated@1.0.0: - resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} - engines: {node: '>=0.10.0'} - ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -5545,25 +3843,6 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsup@8.5.1: - resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.5.0' - peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true - tsx@4.21.0: resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} engines: {node: '>=18.0.0'} @@ -5603,10 +3882,6 @@ packages: resolution: {integrity: sha512-1q7+9UJABuBAHrcC4Sxp5lOqYS5mvxRrwa33wpIyM18hlOCpRD/fTJNxZ0vhbMcJmz15o9kkVm743mPn7p6jpQ==} hasBin: true - type-check@0.3.2: - resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} - engines: {node: '>= 0.8.0'} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -5619,20 +3894,9 @@ packages: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - - type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} - - type@2.7.3: - resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} @@ -5650,9 +3914,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typedarray.prototype.slice@1.0.5: - resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==} - engines: {node: '>= 0.4'} + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} typescript-eslint@8.52.0: resolution: {integrity: sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==} @@ -5669,19 +3932,21 @@ packages: ufo@1.6.2: resolution: {integrity: sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q==} + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + uint8array-extras@1.5.0: + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - uni-global@1.0.0: - resolution: {integrity: sha512-WWM3HP+siTxzIWPNUg7hZ4XO8clKi6NoCAJJWnuRL+BAqyFXF8gC03WNyTefGoUXYc47uYgXxpKLIEvo65PEHw==} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -5702,10 +3967,6 @@ packages: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -5714,10 +3975,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - upath@1.2.0: resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} engines: {node: '>=4'} @@ -5731,9 +3988,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url@0.10.3: - resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} - use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -5742,27 +3996,13 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - - uuid@8.0.0: - resolution: {integrity: sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==} - hasBin: true - - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - validate-npm-package-name@3.0.0: - resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} - velocityjs@2.1.5: - resolution: {integrity: sha512-QYOx2+7ICdUp2IckrJpjakPopLPDAaYwF26z/ZNsUvrlQLNkNoFvnN0SrbnEWp7tZ88+ybIpvuu/+IB9q1j+jQ==} - engines: {node: '>=16.0.0'} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} @@ -5848,18 +4088,6 @@ packages: webdriverio: optional: true - vm2@3.10.1: - resolution: {integrity: sha512-5VKPEiEzLPiIKxfHoGoafr1yL5PKeALbbwwYhiwyEgWmslnao7e1UxKGL93ekGGt45ETs+zFB0tTYLADzCWH8g==} - engines: {node: '>=6.0'} - hasBin: true - - watchpack@2.5.0: - resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} - engines: {node: '>=10.13.0'} - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -5892,10 +4120,6 @@ packages: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -5906,10 +4130,6 @@ packages: engines: {node: '>=8'} hasBin: true - widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} - wmf@1.0.2: resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} engines: {node: '>=0.8'} @@ -5971,61 +4191,11 @@ packages: workbox-window@7.4.0: resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xlsx@0.18.5: resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} engines: {node: '>=0.8'} hasBin: true - xml2js@0.6.2: - resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - - xregexp@2.0.0: - resolution: {integrity: sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==} - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -6033,23 +4203,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml-ast-parser@0.0.43: - resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==} - yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yamljs@0.3.0: - resolution: {integrity: sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==} - hasBin: true - - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -6058,10 +4215,6 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} - zip-stream@4.1.1: - resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} - engines: {node: '>= 10'} - zod-validation-error@3.5.4: resolution: {integrity: sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==} engines: {node: '>=18.0.0'} @@ -6082,11 +4235,6 @@ packages: snapshots: - 2-thenable@1.0.0: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - '@alloc/quick-lru@5.2.0': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.18.0)': @@ -6096,1150 +4244,6 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 - '@auth0/s3@1.0.1': - dependencies: - aws-sdk: 2.1693.0 - fd-slicer: 1.0.1 - findit2: 2.2.3 - graceful-fs: 4.1.15 - mime: 2.6.0 - mkdirp: 0.5.6 - pend: 1.2.0 - rimraf: 2.2.8 - streamsink: 1.2.0 - - '@aws-crypto/crc32@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.965.0 - tslib: 2.8.1 - - '@aws-crypto/crc32c@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.965.0 - tslib: 2.8.1 - - '@aws-crypto/sha1-browser@5.2.0': - dependencies: - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-locate-window': 3.965.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-crypto/sha256-browser@5.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.965.0 - '@aws-sdk/util-locate-window': 3.965.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-crypto/sha256-js@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.965.0 - tslib: 2.8.1 - - '@aws-crypto/supports-web-crypto@5.2.0': - dependencies: - tslib: 2.8.1 - - '@aws-crypto/util@5.2.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-sdk/client-api-gateway@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-sdk-api-gateway': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-cloudformation@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-cognito-identity-provider@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-dynamodb@3.965.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.965.0 - '@aws-sdk/credential-provider-node': 3.965.0 - '@aws-sdk/dynamodb-codec': 3.965.0(@aws-sdk/client-dynamodb@3.965.0) - '@aws-sdk/middleware-endpoint-discovery': 3.965.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.965.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.965.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-eventbridge@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/signature-v4-multi-region': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-iam@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-lambda@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/eventstream-serde-config-resolver': 4.3.7 - '@smithy/eventstream-serde-node': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-s3@3.966.0': - dependencies: - '@aws-crypto/sha1-browser': 5.2.0 - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-bucket-endpoint': 3.966.0 - '@aws-sdk/middleware-expect-continue': 3.965.0 - '@aws-sdk/middleware-flexible-checksums': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-location-constraint': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-sdk-s3': 3.966.0 - '@aws-sdk/middleware-ssec': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/signature-v4-multi-region': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/eventstream-serde-config-resolver': 4.3.7 - '@smithy/eventstream-serde-node': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-blob-browser': 4.2.8 - '@smithy/hash-node': 4.2.7 - '@smithy/hash-stream-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/md5-js': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sso@3.965.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.965.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.965.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.965.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sso@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-node': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@aws-sdk/xml-builder': 3.965.0 - '@smithy/core': 3.20.1 - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/core@3.966.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@aws-sdk/xml-builder': 3.965.0 - '@smithy/core': 3.20.1 - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/crc64-nvme@3.965.0': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/node-http-handler': 4.4.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-stream': 4.5.8 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/node-http-handler': 4.4.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-stream': 4.5.8 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-ini@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/credential-provider-env': 3.965.0 - '@aws-sdk/credential-provider-http': 3.965.0 - '@aws-sdk/credential-provider-login': 3.965.0 - '@aws-sdk/credential-provider-process': 3.965.0 - '@aws-sdk/credential-provider-sso': 3.965.0 - '@aws-sdk/credential-provider-web-identity': 3.965.0 - '@aws-sdk/nested-clients': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-ini@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/credential-provider-env': 3.966.0 - '@aws-sdk/credential-provider-http': 3.966.0 - '@aws-sdk/credential-provider-login': 3.966.0 - '@aws-sdk/credential-provider-process': 3.966.0 - '@aws-sdk/credential-provider-sso': 3.966.0 - '@aws-sdk/credential-provider-web-identity': 3.966.0 - '@aws-sdk/nested-clients': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-login@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/nested-clients': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-login@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/nested-clients': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-node@3.965.0': - dependencies: - '@aws-sdk/credential-provider-env': 3.965.0 - '@aws-sdk/credential-provider-http': 3.965.0 - '@aws-sdk/credential-provider-ini': 3.965.0 - '@aws-sdk/credential-provider-process': 3.965.0 - '@aws-sdk/credential-provider-sso': 3.965.0 - '@aws-sdk/credential-provider-web-identity': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-node@3.966.0': - dependencies: - '@aws-sdk/credential-provider-env': 3.966.0 - '@aws-sdk/credential-provider-http': 3.966.0 - '@aws-sdk/credential-provider-ini': 3.966.0 - '@aws-sdk/credential-provider-process': 3.966.0 - '@aws-sdk/credential-provider-sso': 3.966.0 - '@aws-sdk/credential-provider-web-identity': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-process@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-process@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-sso@3.965.0': - dependencies: - '@aws-sdk/client-sso': 3.965.0 - '@aws-sdk/core': 3.965.0 - '@aws-sdk/token-providers': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-sso@3.966.0': - dependencies: - '@aws-sdk/client-sso': 3.966.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/token-providers': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-web-identity@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/nested-clients': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-web-identity@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/nested-clients': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/dynamodb-codec@3.965.0(@aws-sdk/client-dynamodb@3.965.0)': - dependencies: - '@aws-sdk/client-dynamodb': 3.965.0 - '@aws-sdk/core': 3.965.0 - '@smithy/core': 3.20.1 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - tslib: 2.8.1 - - '@aws-sdk/endpoint-cache@3.965.0': - dependencies: - mnemonist: 0.38.3 - tslib: 2.8.1 - - '@aws-sdk/lib-dynamodb@3.965.0(@aws-sdk/client-dynamodb@3.965.0)': - dependencies: - '@aws-sdk/client-dynamodb': 3.965.0 - '@aws-sdk/core': 3.965.0 - '@aws-sdk/util-dynamodb': 3.965.0(@aws-sdk/client-dynamodb@3.965.0) - '@smithy/core': 3.20.1 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-bucket-endpoint@3.966.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-arn-parser': 3.966.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-endpoint-discovery@3.965.0': - dependencies: - '@aws-sdk/endpoint-cache': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-expect-continue@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-flexible-checksums@3.966.0': - dependencies: - '@aws-crypto/crc32': 5.2.0 - '@aws-crypto/crc32c': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/crc64-nvme': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-host-header@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-location-constraint@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-logger@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-recursion-detection@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@aws/lambda-invoke-store': 0.2.2 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-api-gateway@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-sdk-s3@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-arn-parser': 3.966.0 - '@smithy/core': 3.20.1 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-ssec@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-user-agent@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@smithy/core': 3.20.1 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-user-agent@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@smithy/core': 3.20.1 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.965.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.965.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.965.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.965.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/nested-clients@3.966.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.966.0 - '@aws-sdk/middleware-host-header': 3.965.0 - '@aws-sdk/middleware-logger': 3.965.0 - '@aws-sdk/middleware-recursion-detection': 3.965.0 - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/region-config-resolver': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@aws-sdk/util-endpoints': 3.965.0 - '@aws-sdk/util-user-agent-browser': 3.965.0 - '@aws-sdk/util-user-agent-node': 3.966.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.1 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-retry': 4.4.18 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.17 - '@smithy/util-defaults-mode-node': 4.2.20 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/region-config-resolver@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/signature-v4-multi-region@3.966.0': - dependencies: - '@aws-sdk/middleware-sdk-s3': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/token-providers@3.965.0': - dependencies: - '@aws-sdk/core': 3.965.0 - '@aws-sdk/nested-clients': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/token-providers@3.966.0': - dependencies: - '@aws-sdk/core': 3.966.0 - '@aws-sdk/nested-clients': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/types@3.965.0': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/util-arn-parser@3.966.0': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-dynamodb@3.965.0(@aws-sdk/client-dynamodb@3.965.0)': - dependencies: - '@aws-sdk/client-dynamodb': 3.965.0 - tslib: 2.8.1 - - '@aws-sdk/util-endpoints@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-endpoints': 3.2.7 - tslib: 2.8.1 - - '@aws-sdk/util-locate-window@3.965.0': - dependencies: - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-browser@3.965.0': - dependencies: - '@aws-sdk/types': 3.965.0 - '@smithy/types': 4.11.0 - bowser: 2.13.1 - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-node@3.965.0': - dependencies: - '@aws-sdk/middleware-user-agent': 3.965.0 - '@aws-sdk/types': 3.965.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/util-user-agent-node@3.966.0': - dependencies: - '@aws-sdk/middleware-user-agent': 3.966.0 - '@aws-sdk/types': 3.965.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.965.0': - dependencies: - '@smithy/types': 4.11.0 - fast-xml-parser: 5.2.5 - tslib: 2.8.1 - - '@aws/lambda-invoke-store@0.2.2': {} - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -7269,7 +4273,7 @@ snapshots: '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7289,7 +4293,7 @@ snapshots: '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7370,7 +4374,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.11 transitivePeerDependencies: @@ -8004,7 +5008,7 @@ snapshots: '@babel/parser': 7.28.5 '@babel/template': 7.27.2 '@babel/types': 7.28.5 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8016,7 +5020,7 @@ snapshots: '@babel/parser': 7.29.0 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8030,6 +5034,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@borewit/text-codec@0.2.2': {} + '@esbuild/aix-ppc64@0.27.2': optional: true @@ -8179,12 +5185,17 @@ snapshots: eslint: 9.39.2(jiti@1.21.7) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.7.0))': + dependencies: + eslint: 9.39.2(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.2': {} '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -8200,7 +5211,7 @@ snapshots: '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -8220,216 +5231,35 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@hapi/accept@6.0.3': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 - - '@hapi/ammo@6.0.1': - dependencies: - '@hapi/hoek': 11.0.7 + '@humanfs/core@0.19.1': {} - '@hapi/b64@6.0.1': + '@humanfs/node@0.16.7': dependencies: - '@hapi/hoek': 11.0.7 + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 - '@hapi/boom@10.0.1': - dependencies: - '@hapi/hoek': 11.0.7 + '@humanwhocodes/module-importer@1.0.1': {} - '@hapi/bounce@3.0.2': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 + '@humanwhocodes/retry@0.4.3': {} - '@hapi/bourne@3.0.0': {} + '@isaacs/cliui@9.0.0': {} - '@hapi/call@9.0.1': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - '@hapi/catbox-memory@6.0.2': + '@jridgewell/remapping@2.3.5': dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - '@hapi/catbox@12.1.1': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 - '@hapi/podium': 5.0.2 - '@hapi/validate': 2.0.1 + '@jridgewell/resolve-uri@3.1.2': {} - '@hapi/content@6.0.0': + '@jridgewell/source-map@0.3.11': dependencies: - '@hapi/boom': 10.0.1 - - '@hapi/cryptiles@6.0.3': - dependencies: - '@hapi/boom': 10.0.1 - - '@hapi/file@3.0.0': {} - - '@hapi/h2o2@10.0.4': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 - '@hapi/validate': 2.0.1 - '@hapi/wreck': 18.1.0 - - '@hapi/hapi@21.4.4': - dependencies: - '@hapi/accept': 6.0.3 - '@hapi/ammo': 6.0.1 - '@hapi/boom': 10.0.1 - '@hapi/bounce': 3.0.2 - '@hapi/call': 9.0.1 - '@hapi/catbox': 12.1.1 - '@hapi/catbox-memory': 6.0.2 - '@hapi/heavy': 8.0.1 - '@hapi/hoek': 11.0.7 - '@hapi/mimos': 7.0.1 - '@hapi/podium': 5.0.2 - '@hapi/shot': 6.0.2 - '@hapi/somever': 4.1.1 - '@hapi/statehood': 8.2.1 - '@hapi/subtext': 8.1.1 - '@hapi/teamwork': 6.0.1 - '@hapi/topo': 6.0.2 - '@hapi/validate': 2.0.1 - - '@hapi/heavy@8.0.1': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/hoek': 11.0.7 - '@hapi/validate': 2.0.1 - - '@hapi/hoek@11.0.7': {} - - '@hapi/iron@7.0.1': - dependencies: - '@hapi/b64': 6.0.1 - '@hapi/boom': 10.0.1 - '@hapi/bourne': 3.0.0 - '@hapi/cryptiles': 6.0.3 - '@hapi/hoek': 11.0.7 - - '@hapi/mimos@7.0.1': - dependencies: - '@hapi/hoek': 11.0.7 - mime-db: 1.54.0 - - '@hapi/nigel@5.0.1': - dependencies: - '@hapi/hoek': 11.0.7 - '@hapi/vise': 5.0.1 - - '@hapi/pez@6.1.0': - dependencies: - '@hapi/b64': 6.0.1 - '@hapi/boom': 10.0.1 - '@hapi/content': 6.0.0 - '@hapi/hoek': 11.0.7 - '@hapi/nigel': 5.0.1 - - '@hapi/podium@5.0.2': - dependencies: - '@hapi/hoek': 11.0.7 - '@hapi/teamwork': 6.0.1 - '@hapi/validate': 2.0.1 - - '@hapi/shot@6.0.2': - dependencies: - '@hapi/hoek': 11.0.7 - '@hapi/validate': 2.0.1 - - '@hapi/somever@4.1.1': - dependencies: - '@hapi/bounce': 3.0.2 - '@hapi/hoek': 11.0.7 - - '@hapi/statehood@8.2.1': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/bounce': 3.0.2 - '@hapi/bourne': 3.0.0 - '@hapi/cryptiles': 6.0.3 - '@hapi/hoek': 11.0.7 - '@hapi/iron': 7.0.1 - '@hapi/validate': 2.0.1 - - '@hapi/subtext@8.1.1': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/bourne': 3.0.0 - '@hapi/content': 6.0.0 - '@hapi/file': 3.0.0 - '@hapi/hoek': 11.0.7 - '@hapi/pez': 6.1.0 - '@hapi/wreck': 18.1.0 - - '@hapi/teamwork@6.0.1': {} - - '@hapi/topo@6.0.2': - dependencies: - '@hapi/hoek': 11.0.7 - - '@hapi/validate@2.0.1': - dependencies: - '@hapi/hoek': 11.0.7 - '@hapi/topo': 6.0.2 - - '@hapi/vise@5.0.1': - dependencies: - '@hapi/hoek': 11.0.7 - - '@hapi/wreck@18.1.0': - dependencies: - '@hapi/boom': 10.0.1 - '@hapi/bourne': 3.0.0 - '@hapi/hoek': 11.0.7 - - '@hono/node-server@1.19.7(hono@4.11.3)': - dependencies: - hono: 4.11.3 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@inquirer/external-editor@1.0.3(@types/node@20.19.27)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 - optionalDependencies: - '@types/node': 20.19.27 - - '@isaacs/cliui@9.0.0': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.5': {} @@ -8438,24 +5268,47 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': + '@lukeed/csprng@1.1.0': {} + + '@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - jsep: 1.4.0 + file-type: 20.4.1 + iterare: 1.2.1 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + transitivePeerDependencies: + - supports-color - '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': + '@nestjs/core@10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.22)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - jsep: 1.4.0 + '@nestjs/common': 10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.3.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.2 + tslib: 2.8.1 + uid: 2.0.2 + optionalDependencies: + '@nestjs/platform-express': 10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.22) + transitivePeerDependencies: + - encoding - '@kwsites/file-exists@1.1.1(supports-color@8.1.1)': + '@nestjs/platform-express@10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.22)': dependencies: - debug: 4.4.3(supports-color@8.1.1) + '@nestjs/common': 10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.22)(reflect-metadata@0.2.2)(rxjs@7.8.2) + body-parser: 1.20.4 + cors: 2.8.5 + express: 4.22.1 + multer: 2.0.2 + tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@kwsites/promise-deferred@1.1.1': {} - - '@noble/hashes@1.8.0': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8468,12 +5321,51 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@paralleldrive/cuid2@2.3.1': + '@nuxtjs/opencollective@0.3.2': dependencies: - '@noble/hashes': 1.8.0 + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding '@pkgr/core@0.2.9': {} + '@prisma/client@6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)': + optionalDependencies: + prisma: 6.19.3(typescript@5.9.3) + typescript: 5.9.3 + + '@prisma/config@6.19.3': + dependencies: + c12: 3.1.0 + deepmerge-ts: 7.1.5 + effect: 3.21.0 + empathic: 2.0.0 + transitivePeerDependencies: + - magicast + + '@prisma/debug@6.19.3': {} + + '@prisma/engines-version@7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7': {} + + '@prisma/engines@6.19.3': + dependencies: + '@prisma/debug': 6.19.3 + '@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7 + '@prisma/fetch-engine': 6.19.3 + '@prisma/get-platform': 6.19.3 + + '@prisma/fetch-engine@6.19.3': + dependencies: + '@prisma/debug': 6.19.3 + '@prisma/engines-version': 7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7 + '@prisma/get-platform': 6.19.3 + + '@prisma/get-platform@6.19.3': + dependencies: + '@prisma/debug': 6.19.3 + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': dependencies: '@standard-schema/spec': 1.1.0 @@ -8623,447 +5515,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.55.1': optional: true - '@serverless/dashboard-plugin@7.2.3(@types/node@20.19.27)(supports-color@8.1.1)': - dependencies: - '@aws-sdk/client-cloudformation': 3.966.0 - '@aws-sdk/client-sts': 3.966.0 - '@serverless/event-mocks': 1.1.1 - '@serverless/platform-client': 4.5.1(supports-color@8.1.1) - '@serverless/utils': 6.15.0(@types/node@20.19.27) - child-process-ext: 3.0.2 - chokidar: 3.6.0 - flat: 5.0.2 - fs-extra: 9.1.0 - js-yaml: 4.1.1 - jszip: 3.10.1 - lodash: 4.17.21 - memoizee: 0.4.17 - ncjsm: 4.3.2 - node-dir: 0.1.17 - node-fetch: 2.7.0 - open: 7.4.2 - semver: 7.7.3 - simple-git: 3.30.0(supports-color@8.1.1) - timers-ext: 0.1.8 - type: 2.7.3 - uuid: 8.3.2 - yamljs: 0.3.0 - transitivePeerDependencies: - - '@types/node' - - aws-crt - - bufferutil - - debug - - encoding - - supports-color - - utf-8-validate - - '@serverless/event-mocks@1.1.1': - dependencies: - '@types/lodash': 4.17.23 - lodash: 4.17.21 - - '@serverless/platform-client@4.5.1(supports-color@8.1.1)': - dependencies: - adm-zip: 0.5.16 - archiver: 5.3.2 - axios: 1.13.2 - fast-glob: 3.3.3 - https-proxy-agent: 5.0.1(supports-color@8.1.1) - ignore: 5.3.2 - isomorphic-ws: 4.0.1(ws@7.5.10) - js-yaml: 3.14.2 - jwt-decode: 2.2.0 - minimatch: 3.1.2 - querystring: 0.2.1 - run-parallel-limit: 1.1.0 - throat: 5.0.0 - traverse: 0.6.11 - ws: 7.5.10 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - - '@serverless/utils@6.15.0(@types/node@20.19.27)': - dependencies: - archive-type: 4.0.0 - chalk: 4.1.2 - ci-info: 3.9.0 - cli-progress-footer: 2.3.3 - content-disposition: 0.5.4 - d: 1.0.2 - decompress: 4.2.1 - event-emitter: 0.3.5 - ext: 1.7.0 - ext-name: 5.0.0 - file-type: 16.5.4 - filenamify: 4.3.0 - get-stream: 6.0.1 - got: 11.8.6 - inquirer: 8.2.7(@types/node@20.19.27) - js-yaml: 4.1.1 - jwt-decode: 3.1.2 - lodash: 4.17.21 - log: 6.3.2 - log-node: 8.0.3(log@6.3.2) - make-dir: 4.0.0 - memoizee: 0.4.17 - ms: 2.1.3 - ncjsm: 4.3.2 - node-fetch: 2.7.0 - open: 8.4.2 - p-event: 4.2.0 - supports-color: 8.1.1 - timers-ext: 0.1.8 - type: 2.7.3 - uni-global: 1.0.0 - uuid: 8.3.2 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - '@types/node' - - encoding - - '@sindresorhus/is@4.6.0': {} - - '@smithy/abort-controller@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader-native@4.2.1': - dependencies: - '@smithy/util-base64': 4.3.0 - tslib: 2.8.1 - - '@smithy/chunked-blob-reader@5.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/config-resolver@4.4.5': - dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - tslib: 2.8.1 - - '@smithy/core@3.20.1': - dependencies: - '@smithy/middleware-serde': 4.2.8 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - '@smithy/uuid': 1.1.0 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.2.7': - dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - tslib: 2.8.1 - - '@smithy/eventstream-codec@4.2.7': - dependencies: - '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.11.0 - '@smithy/util-hex-encoding': 4.2.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-browser@4.2.7': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-config-resolver@4.3.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-node@4.2.7': - dependencies: - '@smithy/eventstream-serde-universal': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/eventstream-serde-universal@4.2.7': - dependencies: - '@smithy/eventstream-codec': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.3.8': - dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/querystring-builder': 4.2.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - tslib: 2.8.1 - - '@smithy/hash-blob-browser@4.2.8': - dependencies: - '@smithy/chunked-blob-reader': 5.2.0 - '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/hash-node@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/hash-stream-node@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/invalid-dependency@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/is-array-buffer@2.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/is-array-buffer@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/md5-js@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/middleware-content-length@4.2.7': - dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/middleware-endpoint@4.4.2': - dependencies: - '@smithy/core': 3.20.1 - '@smithy/middleware-serde': 4.2.8 - '@smithy/node-config-provider': 4.3.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-middleware': 4.2.7 - tslib: 2.8.1 - - '@smithy/middleware-retry@4.4.18': - dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/service-error-classification': 4.2.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/uuid': 1.1.0 - tslib: 2.8.1 - - '@smithy/middleware-serde@4.2.8': - dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/middleware-stack@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/node-config-provider@4.3.7': - dependencies: - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.4.7': - dependencies: - '@smithy/abort-controller': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/querystring-builder': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/property-provider@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/protocol-http@5.3.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/querystring-builder@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-uri-escape': 4.2.0 - tslib: 2.8.1 - - '@smithy/querystring-parser@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/service-error-classification@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - - '@smithy/shared-ini-file-loader@4.4.2': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/signature-v4@5.3.7': - dependencies: - '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-uri-escape': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/smithy-client@4.10.3': - dependencies: - '@smithy/core': 3.20.1 - '@smithy/middleware-endpoint': 4.4.2 - '@smithy/middleware-stack': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-stream': 4.5.8 - tslib: 2.8.1 - - '@smithy/types@4.11.0': - dependencies: - tslib: 2.8.1 - - '@smithy/url-parser@4.2.7': - dependencies: - '@smithy/querystring-parser': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-base64@4.3.0': - dependencies: - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-body-length-browser@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-body-length-node@4.2.1': - dependencies: - tslib: 2.8.1 - - '@smithy/util-buffer-from@2.2.0': - dependencies: - '@smithy/is-array-buffer': 2.2.0 - tslib: 2.8.1 - - '@smithy/util-buffer-from@4.2.0': - dependencies: - '@smithy/is-array-buffer': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-config-provider@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-defaults-mode-browser@4.3.17': - dependencies: - '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-defaults-mode-node@4.2.20': - dependencies: - '@smithy/config-resolver': 4.4.5 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.3 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-endpoints@3.2.7': - dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-hex-encoding@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-middleware@4.2.7': - dependencies: - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-retry@4.2.7': - dependencies: - '@smithy/service-error-classification': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/util-stream@4.5.8': - dependencies: - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/node-http-handler': 4.4.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-uri-escape@4.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/util-utf8@2.3.0': - dependencies: - '@smithy/util-buffer-from': 2.2.0 - tslib: 2.8.1 - - '@smithy/util-utf8@4.2.0': - dependencies: - '@smithy/util-buffer-from': 4.2.0 - tslib: 2.8.1 - - '@smithy/util-waiter@4.2.7': - dependencies: - '@smithy/abort-controller': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@smithy/uuid@1.1.0': - dependencies: - tslib: 2.8.1 - '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -9145,16 +5596,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@szmarczak/http-timer@4.0.6': + '@tokenizer/inflate@0.2.7': dependencies: - defer-to-connect: 2.0.1 + debug: 4.4.3 + fflate: 0.8.3 + token-types: 6.1.2 + transitivePeerDependencies: + - supports-color '@tokenizer/token@0.3.0': {} - '@tootallnate/once@1.1.2': {} - - '@types/aws-lambda@8.10.159': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.5 @@ -9176,13 +5627,6 @@ snapshots: dependencies: '@babel/types': 7.28.5 - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.0.4 - '@types/keyv': 3.1.4 - '@types/node': 20.19.27 - '@types/responselike': 1.0.3 - '@types/chai-subset@1.3.6(@types/chai@4.3.20)': dependencies: '@types/chai': 4.3.20 @@ -9225,16 +5669,8 @@ snapshots: '@types/file-saver@2.0.7': {} - '@types/http-cache-semantics@4.0.4': {} - '@types/json-schema@7.0.15': {} - '@types/keyv@3.1.4': - dependencies: - '@types/node': 20.19.27 - - '@types/lodash@4.17.23': {} - '@types/node@20.19.27': dependencies: undici-types: 6.21.0 @@ -9249,23 +5685,19 @@ snapshots: '@types/resolve@1.20.2': {} - '@types/responselike@1.0.3': - dependencies: - '@types/node': 20.19.27 - '@types/trusted-types@2.0.7': {} '@types/use-sync-external-store@0.0.6': {} - '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.52.0 - '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.52.0 - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -9273,14 +5705,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 8.52.0 '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) '@typescript-eslint/visitor-keys': 8.52.0 - debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.2(jiti@1.21.7) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -9289,7 +5721,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) '@typescript-eslint/types': 8.52.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -9303,13 +5735,13 @@ snapshots: dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.2(jiti@1.21.7) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@2.7.0) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -9323,7 +5755,7 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) '@typescript-eslint/types': 8.52.0 '@typescript-eslint/visitor-keys': 8.52.0 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 @@ -9332,13 +5764,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/utils@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.52.0 '@typescript-eslint/types': 8.52.0 '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -9389,9 +5821,10 @@ snapshots: loupe: 2.3.7 pretty-format: 27.5.1 - abort-controller@3.0.0: + accepts@1.3.8: dependencies: - event-target-shim: 5.0.1 + mime-types: 2.1.35 + negotiator: 0.6.3 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -9407,18 +5840,6 @@ snapshots: adler-32@1.3.1: {} - adm-zip@0.5.16: {} - - agent-base@6.0.2(supports-color@8.1.1): - dependencies: - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - ajv-formats@2.1.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -9426,13 +5847,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 @@ -9440,30 +5854,14 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-align@3.0.1: - dependencies: - string-width: 4.2.3 - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} - ansi-styles@6.2.3: {} - any-promise@1.3.0: {} anymatch@3.1.3: @@ -9471,52 +5869,10 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - archive-type@4.0.0: - dependencies: - file-type: 4.4.0 - - archiver-utils@2.1.0: - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - lodash.defaults: 4.2.0 - lodash.difference: 4.5.0 - lodash.flatten: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.union: 4.6.0 - normalize-path: 3.0.0 - readable-stream: 2.3.8 - - archiver-utils@3.0.4: - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - lodash.defaults: 4.2.0 - lodash.difference: 4.5.0 - lodash.flatten: 4.4.0 - lodash.isplainobject: 4.0.6 - lodash.union: 4.6.0 - normalize-path: 3.0.0 - readable-stream: 3.6.2 - - archiver@5.3.2: - dependencies: - archiver-utils: 2.1.0 - async: 3.2.6 - buffer-crc32: 0.2.13 - readable-stream: 3.6.2 - readdir-glob: 1.1.3 - tar-stream: 2.2.0 - zip-stream: 4.1.1 + append-field@1.0.0: {} arg@5.0.2: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} array-buffer-byte-length@1.0.2: @@ -9524,6 +5880,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-flatten@1.1.1: {} + array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -9535,10 +5893,6 @@ snapshots: is-string: 1.1.1 math-intrinsics: 1.1.0 - array-unflat-js@0.1.3: {} - - array-union@2.1.0: {} - array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 @@ -9580,20 +5934,12 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 - asap@2.0.6: {} - assertion-error@1.1.0: {} - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - async-function@1.0.0: {} async@3.2.6: {} - asynckit@0.4.0: {} - at-least-node@1.0.0: {} autoprefixer@10.4.23(postcss@8.5.6): @@ -9609,38 +5955,6 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - aws-dynamodb-local@0.0.11: - dependencies: - tar: 6.2.1 - - aws-lambda@1.0.7: - dependencies: - aws-sdk: 2.1693.0 - commander: 3.0.2 - js-yaml: 3.14.2 - watchpack: 2.5.0 - - aws-sdk@2.1693.0: - dependencies: - buffer: 4.9.2 - events: 1.1.1 - ieee754: 1.1.13 - jmespath: 0.16.0 - querystring: 0.2.0 - sax: 1.2.1 - url: 0.10.3 - util: 0.12.5 - uuid: 8.0.0 - xml2js: 0.6.2 - - axios@1.13.2: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.29.0): dependencies: '@babel/compat-data': 7.29.0 @@ -9669,39 +5983,28 @@ snapshots: balanced-match@4.0.4: {} - base64-js@1.5.1: {} - baseline-browser-mapping@2.9.12: {} binary-extensions@2.3.0: {} - bl@1.2.3: - dependencies: - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - bluebird@3.7.2: {} - blueimp-md5@2.19.0: {} - bowser@2.13.1: {} - - boxen@7.1.1: + body-parser@1.20.4: dependencies: - ansi-align: 3.0.1 - camelcase: 7.0.1 - chalk: 5.6.2 - cli-boxes: 3.0.0 - string-width: 5.1.2 - type-fest: 2.19.0 - widest-line: 4.0.1 - wrap-ansi: 8.1.0 + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.1 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color brace-expansion@1.1.12: dependencies: @@ -9728,61 +6031,30 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) - buffer-alloc-unsafe@1.1.0: {} - - buffer-alloc@1.2.0: - dependencies: - buffer-alloc-unsafe: 1.1.0 - buffer-fill: 1.0.0 - - buffer-crc32@0.2.13: {} - - buffer-fill@1.0.0: {} - buffer-from@1.1.2: {} - buffer@4.9.2: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - isarray: 1.0.0 - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - builtin-modules@3.3.0: {} - - builtins@1.0.3: {} - - bundle-require@5.1.0(esbuild@0.27.2): + busboy@1.6.0: dependencies: - esbuild: 0.27.2 - load-tsconfig: 0.2.5 + streamsearch: 1.1.0 bytes@3.1.2: {} - cac@6.7.14: {} - - cacheable-lookup@5.0.4: {} - - cacheable-request@7.0.4: + c12@3.1.0: dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 + chokidar: 4.0.3 + confbox: 0.2.4 + defu: 6.1.7 + dotenv: 16.6.1 + exsolve: 1.1.0 + giget: 2.0.0 + jiti: 2.7.0 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + pkg-types: 2.3.1 + rc9: 2.1.2 - cachedir@2.4.0: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: dependencies: @@ -9807,8 +6079,6 @@ snapshots: camelcase@6.3.0: {} - camelcase@7.0.1: {} - caniuse-lite@1.0.30001763: {} cfb@1.2.2: @@ -9826,41 +6096,15 @@ snapshots: pathval: 1.1.1 type-detect: 4.1.0 - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.2: {} - - chardet@2.1.1: {} - check-error@1.0.3: dependencies: get-func-name: 2.0.2 - child-process-ext@2.1.1: - dependencies: - cross-spawn: 6.0.6 - es5-ext: 0.10.64 - log: 6.3.2 - split2: 3.2.2 - stream-promise: 3.2.0 - - child-process-ext@3.0.2: - dependencies: - cross-spawn: 7.0.6 - es5-ext: 0.10.64 - log: 6.3.2 - split2: 3.2.2 - stream-promise: 3.2.0 - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -9877,91 +6121,38 @@ snapshots: dependencies: readdirp: 4.1.2 - chownr@2.0.0: {} - - ci-info@3.9.0: {} - - cli-boxes@3.0.0: {} - - cli-color@2.0.4: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-iterator: 2.0.3 - memoizee: 0.4.17 - timers-ext: 0.1.8 - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-progress-footer@2.3.3: - dependencies: - cli-color: 2.0.4 - d: 1.0.2 - es5-ext: 0.10.64 - mute-stream: 0.0.8 - process-utils: 4.0.0 - timers-ext: 0.1.8 - type: 2.7.3 - - cli-spinners@2.9.2: {} - - cli-sprintf-format@1.1.1: - dependencies: - cli-color: 2.0.4 - es5-ext: 0.10.64 - sprintf-kit: 2.0.2 - supports-color: 6.1.0 - - cli-width@3.0.0: {} - - clone-response@1.0.3: + citty@0.1.6: dependencies: - mimic-response: 1.0.1 + consola: 3.4.2 - clone@1.0.4: {} + citty@0.2.2: {} clsx@2.1.1: {} codepage@1.15.0: {} - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} colord@2.9.3: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - commander@2.20.3: {} - commander@3.0.2: {} - commander@4.1.1: {} common-tags@1.8.2: {} - component-emitter@1.3.1: {} + concat-map@0.0.1: {} - compress-commons@4.1.2: + concat-stream@2.0.0: dependencies: - buffer-crc32: 0.2.13 - crc32-stream: 4.0.3 - normalize-path: 3.0.0 + buffer-from: 1.1.2 + inherits: 2.0.4 readable-stream: 3.6.2 - - concat-map@0.0.1: {} + typedarray: 0.0.6 concordance@5.0.4: dependencies: @@ -9969,28 +6160,39 @@ snapshots: esutils: 2.0.3 fast-diff: 1.3.0 js-string-escape: 1.0.1 - lodash: 4.17.21 + lodash: 4.17.23 md5-hex: 3.0.1 semver: 7.7.3 well-known-symbols: 2.0.0 confbox@0.1.8: {} + confbox@0.2.4: {} + + consola@2.15.3: {} + consola@3.4.2: {} content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 + content-type@1.0.5: {} + convert-source-map@2.0.0: {} - cookiejar@2.1.4: {} + cookie-signature@1.0.7: {} + + cookie@0.7.2: {} core-js-compat@3.48.0: dependencies: browserslist: 4.28.1 - core-util-is@1.0.3: {} + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 cosmiconfig@8.3.6(typescript@5.9.3): dependencies: @@ -10003,23 +6205,6 @@ snapshots: crc-32@1.2.2: {} - crc32-stream@4.0.3: - dependencies: - crc-32: 1.2.2 - readable-stream: 3.6.2 - - cron-parser@4.9.0: - dependencies: - luxon: 3.7.2 - - cross-spawn@6.0.6: - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.2 - shebang-command: 1.2.0 - which: 1.3.1 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -10075,11 +6260,6 @@ snapshots: d3-timer@3.0.1: {} - d@1.0.2: - dependencies: - es5-ext: 0.10.64 - type: 2.7.3 - daisyui@3.9.4(tsx@4.21.0)(yaml@1.10.2): dependencies: colord: 2.9.3 @@ -10091,8 +6271,6 @@ snapshots: - tsx - yaml - data-uri-to-buffer@3.0.1: {} - data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -10117,79 +6295,25 @@ snapshots: dependencies: time-zone: 1.0.0 - dayjs@1.11.19: {} + debug@2.6.9: + dependencies: + ms: 2.0.0 - debug@4.4.3(supports-color@8.1.1): + debug@4.4.3: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 decimal.js-light@2.5.1: {} - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - decompress-tar@4.1.1: - dependencies: - file-type: 5.2.0 - is-stream: 1.1.0 - tar-stream: 1.6.2 - - decompress-tarbz2@4.1.1: - dependencies: - decompress-tar: 4.1.1 - file-type: 6.2.0 - is-stream: 1.1.0 - seek-bzip: 1.0.6 - unbzip2-stream: 1.4.3 - - decompress-targz@4.1.1: - dependencies: - decompress-tar: 4.1.1 - file-type: 5.2.0 - is-stream: 1.1.0 - - decompress-unzip@4.0.1: - dependencies: - file-type: 3.9.0 - get-stream: 2.3.1 - pify: 2.3.0 - yauzl: 2.10.0 - - decompress@4.2.1: - dependencies: - decompress-tar: 4.1.1 - decompress-tarbz2: 4.1.1 - decompress-targz: 4.1.1 - decompress-unzip: 4.0.1 - graceful-fs: 4.2.11 - make-dir: 1.3.0 - pify: 2.3.0 - strip-dirs: 2.1.0 - deep-eql@4.1.4: dependencies: type-detect: 4.1.0 deep-is@0.1.4: {} - deepmerge@4.3.1: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - defer-to-connect@2.0.1: {} + deepmerge-ts@7.1.5: {} - deferred@0.7.11: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - next-tick: 1.1.0 - timers-ext: 0.1.8 + deepmerge@4.3.1: {} define-data-property@1.1.4: dependencies: @@ -10197,38 +6321,22 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - define-lazy-prop@2.0.0: {} - define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - degenerator@3.0.4: - dependencies: - ast-types: 0.13.4 - escodegen: 1.14.3 - esprima: 4.0.1 - vm2: 3.10.1 - - delayed-stream@1.0.0: {} + defu@6.1.7: {} depd@2.0.0: {} - desm@1.3.1: {} + destr@2.0.5: {} - dezalgo@1.0.4: - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 + destroy@1.2.0: {} didyoumean@1.2.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} doctrine@2.1.0: @@ -10245,8 +6353,6 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 - dotenv-expand@10.0.0: {} - dotenv@16.6.1: {} dunder-proto@1.0.1: @@ -10255,12 +6361,12 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - duration@0.2.2: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 + ee-first@1.1.1: {} - eastasianwidth@0.2.0: {} + effect@3.21.0: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 ejs@3.1.10: dependencies: @@ -10268,13 +6374,9 @@ snapshots: electron-to-chromium@1.5.267: {} - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} + empathic@2.0.0: {} - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 + encodeurl@2.0.0: {} entities@4.5.0: {} @@ -10383,40 +6485,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es5-ext@0.10.64: - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - esniff: 2.0.1 - next-tick: 1.1.0 - - es6-iterator@2.0.3: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-symbol: 3.1.4 - - es6-set@0.1.6: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - event-emitter: 0.3.5 - type: 2.7.3 - - es6-symbol@3.1.4: - dependencies: - d: 1.0.2 - ext: 1.7.0 - - es6-weak-map@2.0.3: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 @@ -10473,61 +6541,52 @@ snapshots: escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} + escape-html@1.0.3: {} escape-string-regexp@4.0.0: {} - escodegen@1.14.3: - dependencies: - esprima: 4.0.1 - estraverse: 4.3.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@1.21.7)): + eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.7.0)): dependencies: - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(prettier@3.7.4): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.2(jiti@2.7.0)))(eslint@9.39.2(jiti@2.7.0))(prettier@3.7.4): dependencies: - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) prettier: 3.7.4 prettier-linter-helpers: 1.0.1 synckit: 0.11.11 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 9.1.2(eslint@9.39.2(jiti@1.21.7)) + eslint-config-prettier: 9.1.2(eslint@9.39.2(jiti@2.7.0)) - eslint-plugin-react-hooks@6.0.0-rc.2(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-react-hooks@6.0.0-rc.2(eslint@9.39.2(jiti@2.7.0)): dependencies: '@babel/core': 7.28.5 '@babel/parser': 7.28.5 '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.28.5) - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) hermes-parser: 0.25.1 zod: 3.25.76 zod-validation-error: 3.5.4(zod@3.25.76) transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks@7.1.0-canary-d6cae440-20260106(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-react-hooks@7.1.0-canary-d6cae440-20260106(eslint@9.39.2(jiti@2.7.0)): dependencies: '@babel/core': 7.28.5 '@babel/parser': 7.28.5 - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) hermes-parser: 0.25.1 zod: 4.3.5 zod-validation-error: 4.0.2(zod@4.3.5) transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@2.7.0)): dependencies: - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) - eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@2.7.0)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -10535,7 +6594,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.2 - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -10549,9 +6608,9 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-simple-import-sort@12.1.1(eslint@9.39.2(jiti@2.7.0)): dependencies: - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.19(tsx@4.21.0)(yaml@1.10.2)): dependencies: @@ -10559,12 +6618,12 @@ snapshots: postcss: 8.5.6 tailwindcss: 3.4.19(tsx@4.21.0)(yaml@1.10.2) - eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)): dependencies: - eslint: 9.39.2(jiti@1.21.7) + eslint: 9.39.2(jiti@2.7.0) eslint-rule-composer: 0.3.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) eslint-rule-composer@0.3.0: {} @@ -10594,7 +6653,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -10618,17 +6677,46 @@ snapshots: transitivePeerDependencies: - supports-color - esniff@1.1.3: + eslint@9.39.2(jiti@2.7.0): dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - - esniff@2.0.1: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - type: 2.7.3 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color espree@10.4.0: dependencies: @@ -10636,8 +6724,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - esprima@4.0.1: {} - esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -10646,12 +6732,6 @@ snapshots: dependencies: estraverse: 5.3.0 - essentials@1.2.0: - dependencies: - uni-global: 1.0.0 - - estraverse@4.3.0: {} - estraverse@5.3.0: {} estree-walker@1.0.1: {} @@ -10660,43 +6740,51 @@ snapshots: esutils@2.0.3: {} - event-emitter@0.3.5: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - - event-target-shim@5.0.1: {} + etag@1.8.1: {} eventemitter3@4.0.7: {} - events@1.1.1: {} - - events@3.3.0: {} - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - ext-list@2.2.2: + express@4.22.1: dependencies: - mime-db: 1.54.0 + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.13 + proxy-addr: 2.0.7 + qs: 6.14.1 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color - ext-name@5.0.0: - dependencies: - ext-list: 2.2.2 - sort-keys-length: 1.0.1 + exsolve@1.1.0: {} - ext@1.7.0: + fast-check@3.23.2: dependencies: - type: 2.7.3 + pure-rand: 6.1.0 fast-deep-equal@3.1.3: {} @@ -10720,33 +6808,17 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@5.2.5: - dependencies: - strnum: 2.1.2 - - fastest-levenshtein@1.0.16: {} - fastparse@1.1.2: {} fastq@1.20.1: dependencies: reusify: 1.1.0 - fd-slicer@1.0.1: - dependencies: - pend: 1.2.0 - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - figures@3.2.0: - dependencies: - escape-string-regexp: 1.0.5 + fflate@0.8.3: {} file-entry-cache@8.0.0: dependencies: @@ -10754,69 +6826,47 @@ snapshots: file-saver@2.0.5: {} - file-type@16.5.4: + file-type@20.4.1: dependencies: - readable-web-to-node-stream: 3.0.4 - strtok3: 6.3.0 - token-types: 4.2.1 - - file-type@3.9.0: {} - - file-type@4.4.0: {} - - file-type@5.2.0: {} - - file-type@6.2.0: {} - - file-uri-to-path@2.0.0: {} + '@tokenizer/inflate': 0.2.7 + strtok3: 10.3.5 + token-types: 6.1.2 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color filelist@1.0.6: dependencies: minimatch: 5.1.9 - filename-reserved-regex@2.0.0: {} - - filenamify@4.3.0: - dependencies: - filename-reserved-regex: 2.0.0 - strip-outer: 1.0.1 - trim-repeated: 1.0.0 - - filesize@10.1.6: {} - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-requires@1.0.0: + finalhandler@1.3.2: dependencies: - es5-ext: 0.10.64 - esniff: 1.1.3 + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - findit2@2.2.3: {} - - fix-dts-default-cjs-exports@1.0.1: - dependencies: - magic-string: 0.30.21 - mlly: 1.8.0 - rollup: 4.55.1 - flat-cache@4.0.1: dependencies: flatted: 3.3.3 keyv: 4.5.4 - flat@5.0.2: {} - flatted@3.3.3: {} - follow-redirects@1.15.11: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -10826,44 +6876,13 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - formidable@2.1.5: - dependencies: - '@paralleldrive/cuid2': 2.3.1 - dezalgo: 1.0.4 - once: 1.4.0 - qs: 6.14.1 - - frac@1.1.2: {} - - fraction.js@5.3.4: {} - - fs-constants@1.0.0: {} - - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 + forwarded@0.2.0: {} - fs-extra@11.3.3: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 + frac@1.1.2: {} - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 + fraction.js@5.3.4: {} + + fresh@0.5.2: {} fs-extra@9.1.0: dependencies: @@ -10872,31 +6891,9 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs.realpath@1.0.0: {} - - fs2@0.3.16: - dependencies: - d: 1.0.2 - deferred: 0.7.11 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - ext: 1.7.0 - ignore: 5.3.2 - memoizee: 0.4.17 - type: 2.7.3 - fsevents@2.3.3: optional: true - ftp@0.3.10: - dependencies: - readable-stream: 1.1.14 - xregexp: 2.0.0 - function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -10936,21 +6933,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stdin@8.0.0: {} - - get-stream@2.3.1: - dependencies: - object-assign: 4.1.1 - pinkie-promise: 2.0.1 - - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - - get-stream@6.0.1: {} - - get-stream@8.0.1: {} - get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -10961,16 +6943,14 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 - get-uri@3.0.2: + giget@2.0.0: dependencies: - '@tootallnate/once': 1.1.2 - data-uri-to-buffer: 3.0.1 - debug: 4.4.3(supports-color@8.1.1) - file-uri-to-path: 2.0.0 - fs-extra: 8.1.0 - ftp: 0.3.10 - transitivePeerDependencies: - - supports-color + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.7 + node-fetch-native: 1.6.7 + nypm: 0.6.8 + pathe: 2.0.3 glob-parent@5.1.2: dependencies: @@ -10980,8 +6960,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob-to-regexp@0.4.1: {} - glob@11.1.0: dependencies: foreground-child: 3.3.1 @@ -10991,15 +6969,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.2 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - globals@14.0.0: {} globals@15.15.0: {} @@ -11009,43 +6978,12 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - graceful-fs@4.1.15: {} - graceful-fs@4.2.11: {} - graphlib@2.1.8: - dependencies: - lodash: 4.17.21 - has-bigints@1.1.0: {} - has-flag@3.0.0: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -11072,10 +7010,6 @@ snapshots: dependencies: hermes-estree: 0.25.1 - hono@4.11.3: {} - - http-cache-semantics@4.2.0: {} - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -11084,48 +7018,18 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@4.0.1: - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - http2-wrapper@1.0.3: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - https-proxy-agent@5.0.1(supports-color@8.1.1): - dependencies: - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - human-signals@5.0.0: {} - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: - dependencies: - safer-buffer: 2.1.2 - idb@7.1.1: {} - ieee754@1.1.13: {} - ieee754@1.2.1: {} ignore@5.3.2: {} ignore@7.0.5: {} - immediate@3.0.6: {} - immer@11.1.4: {} import-fresh@3.3.1: @@ -11135,33 +7039,8 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} - inquirer@8.2.7(@types/node@20.19.27): - dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@20.19.27) - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - ora: 5.4.1 - run-async: 2.4.1 - rxjs: 7.8.2 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - wrap-ansi: 6.2.0 - transitivePeerDependencies: - - '@types/node' - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -11170,14 +7049,7 @@ snapshots: internmap@2.0.3: {} - ip-address@10.1.0: {} - - ip@1.1.9: {} - - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + ipaddr.js@1.9.1: {} is-array-buffer@3.0.5: dependencies: @@ -11225,18 +7097,12 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-docker@2.2.1: {} - - is-docker@3.0.0: {} - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -11249,18 +7115,10 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-interactive@1.0.0: {} - is-map@2.0.3: {} is-module@1.0.0: {} - is-natural-number@4.0.1: {} - is-negative-zero@2.0.3: {} is-number-object@1.1.1: @@ -11272,10 +7130,6 @@ snapshots: is-obj@1.0.1: {} - is-plain-obj@1.1.0: {} - - is-promise@2.2.2: {} - is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -11291,12 +7145,8 @@ snapshots: dependencies: call-bound: 1.0.4 - is-stream@1.1.0: {} - is-stream@2.0.1: {} - is-stream@3.0.0: {} - is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -11312,8 +7162,6 @@ snapshots: dependencies: which-typed-array: 1.1.19 - is-unicode-supported@0.1.0: {} - is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -11325,25 +7173,11 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-wsl@2.2.0: - dependencies: - is-docker: 2.2.1 - - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - - isarray@0.0.1: {} - - isarray@1.0.0: {} - isarray@2.0.5: {} isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@7.5.10): - dependencies: - ws: 7.5.10 + iterare@1.2.1: {} iterator.prototype@1.1.5: dependencies: @@ -11364,59 +7198,24 @@ snapshots: filelist: 1.0.6 picocolors: 1.1.1 - java-invoke-local@0.0.6: {} - jiti@1.21.7: {} - jmespath@0.16.0: {} - - jose@5.10.0: {} - - jose@6.1.3: {} - - joycon@3.1.1: {} + jiti@2.7.0: {} js-string-escape@1.0.1: {} js-tokens@4.0.0: {} - js-yaml@3.14.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.1: dependencies: argparse: 2.0.1 - jsep@1.4.0: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} - json-colorizer@2.2.2: - dependencies: - chalk: 2.4.2 - lodash.get: 4.4.2 - - json-cycle@1.5.0: {} - json-parse-even-better-errors@2.3.1: {} - json-refs@3.0.15(supports-color@8.1.1): - dependencies: - commander: 4.1.1 - graphlib: 2.1.8 - js-yaml: 3.14.2 - lodash: 4.17.21 - native-promise-only: 0.8.1 - path-loader: 1.0.12(supports-color@8.1.1) - slash: 3.0.0 - uri-js: 4.4.1 - transitivePeerDependencies: - - supports-color - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -11427,26 +7226,14 @@ snapshots: json5@2.2.3: {} - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - jsonpath-plus@10.3.0: - dependencies: - '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) - '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) - jsep: 1.4.0 - jsonpointer@5.0.1: {} - jsonschema@1.5.0: {} - jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -11454,47 +7241,21 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 - jszip@3.10.1: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - - jwt-decode@2.2.0: {} - - jwt-decode@3.1.2: {} - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - lazystream@1.0.1: - dependencies: - readable-stream: 2.3.8 - leven@3.1.0: {} - levn@0.3.0: - dependencies: - prelude-ls: 1.1.2 - type-check: 0.3.2 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - lie@3.3.0: - dependencies: - immediate: 3.0.6 - lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} - load-tsconfig@0.2.5: {} - local-pkg@0.4.3: {} locate-path@6.0.0: @@ -11503,55 +7264,14 @@ snapshots: lodash.debounce@4.0.8: {} - lodash.defaults@4.2.0: {} - - lodash.difference@4.5.0: {} - - lodash.flatten@4.4.0: {} - - lodash.get@4.4.2: {} - - lodash.isplainobject@4.0.6: {} - lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} - lodash.union@4.6.0: {} - lodash@4.17.21: {} lodash@4.17.23: {} - log-node@8.0.3(log@6.3.2): - dependencies: - ansi-regex: 5.0.1 - cli-color: 2.0.4 - cli-sprintf-format: 1.1.1 - d: 1.0.2 - es5-ext: 0.10.64 - log: 6.3.2 - sprintf-kit: 2.0.2 - supports-color: 8.1.1 - type: 2.7.3 - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - log@6.3.2: - dependencies: - d: 1.0.2 - duration: 0.2.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - sprintf-kit: 2.0.2 - type: 2.7.3 - uni-global: 1.0.0 - - long-timeout@0.1.1: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -11564,24 +7284,16 @@ snapshots: dependencies: tslib: 2.8.1 - lowercase-keys@2.0.0: {} - lru-cache@11.2.6: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-queue@0.1.0: - dependencies: - es5-ext: 0.10.64 - lucide-react@0.562.0(react@19.2.4): dependencies: react: 19.2.4 - luxon@3.7.2: {} - magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 @@ -11590,32 +7302,15 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-dir@1.3.0: - dependencies: - pify: 3.0.0 - - make-dir@4.0.0: - dependencies: - semver: 7.7.3 - math-intrinsics@1.1.0: {} md5-hex@3.0.1: dependencies: blueimp-md5: 2.19.0 - memoizee@0.4.17: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-weak-map: 2.0.3 - event-emitter: 0.3.5 - is-promise: 2.2.2 - lru-queue: 0.1.0 - next-tick: 1.1.0 - timers-ext: 0.1.8 + media-typer@0.3.0: {} - merge-stream@2.0.0: {} + merge-descriptors@1.0.3: {} merge2@1.4.1: {} @@ -11628,21 +7323,11 @@ snapshots: mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mime@2.6.0: {} - - mimic-fn@2.1.0: {} - - mimic-fn@4.0.0: {} - - mimic-response@1.0.1: {} - - mimic-response@3.1.0: {} + mime@1.6.0: {} minimatch@10.2.4: dependencies: @@ -11652,10 +7337,6 @@ snapshots: dependencies: brace-expansion: 1.1.12 - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - minimatch@5.1.9: dependencies: brace-expansion: 2.0.2 @@ -11666,25 +7347,12 @@ snapshots: minimist@1.2.8: {} - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - minipass@7.1.3: {} - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - mkdirp@0.5.6: dependencies: minimist: 1.2.8 - mkdirp@1.0.4: {} - mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -11692,13 +7360,19 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.2 - mnemonist@0.38.3: - dependencies: - obliterator: 1.6.1 + ms@2.0.0: {} ms@2.1.3: {} - mute-stream@0.0.8: {} + multer@2.0.2: + dependencies: + append-field: 1.0.0 + busboy: 1.6.0 + concat-stream: 2.0.0 + mkdirp: 0.5.6 + object-assign: 4.1.1 + type-is: 1.6.18 + xtend: 4.0.2 mz@2.7.0: dependencies: @@ -11708,35 +7382,16 @@ snapshots: nanoid@3.3.11: {} - native-promise-only@0.8.1: {} - natural-compare@1.4.0: {} - ncjsm@4.3.2: - dependencies: - builtin-modules: 3.3.0 - deferred: 0.7.11 - es5-ext: 0.10.64 - es6-set: 0.1.6 - ext: 1.7.0 - find-requires: 1.0.0 - fs2: 0.3.16 - type: 2.7.3 - - netmask@2.0.2: {} - - next-tick@1.1.0: {} - - nice-try@1.0.5: {} + negotiator@0.6.3: {} no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.8.1 - node-dir@0.1.17: - dependencies: - minimatch: 3.1.2 + node-fetch-native@1.6.7: {} node-fetch@2.7.0: dependencies: @@ -11744,31 +7399,13 @@ snapshots: node-releases@2.0.27: {} - node-schedule@2.1.1: - dependencies: - cron-parser: 4.9.0 - long-timeout: 0.1.1 - sorted-array-functions: 1.3.0 - normalize-path@3.0.0: {} - normalize-url@6.1.0: {} - - npm-registry-utilities@1.0.0: - dependencies: - ext: 1.7.0 - fs2: 0.3.16 - memoizee: 0.4.17 - node-fetch: 2.7.0 - semver: 7.7.3 - type: 2.7.3 - validate-npm-package-name: 3.0.0 - transitivePeerDependencies: - - encoding - - npm-run-path@5.3.0: + nypm@0.6.8: dependencies: - path-key: 4.0.0 + citty: 0.2.2 + pathe: 2.0.3 + tinyexec: 1.2.4 object-assign@4.1.1: {} @@ -11808,39 +7445,11 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - obliterator@1.6.1: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - - open@7.4.2: - dependencies: - is-docker: 2.2.1 - is-wsl: 2.2.0 - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 + ohash@2.0.11: {} - optionator@0.8.3: + on-finished@2.4.1: dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.3.0 - prelude-ls: 1.1.2 - type-check: 0.3.2 - word-wrap: 1.2.5 + ee-first: 1.1.1 optionator@0.9.4: dependencies: @@ -11851,32 +7460,12 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 - p-cancelable@2.1.1: {} - - p-event@4.2.0: - dependencies: - p-timeout: 3.2.0 - - p-finally@1.0.0: {} - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -11889,39 +7478,8 @@ snapshots: dependencies: p-limit: 3.1.0 - p-memoize@7.1.1: - dependencies: - mimic-fn: 4.0.0 - type-fest: 3.13.1 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - pac-proxy-agent@5.0.0: - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - get-uri: 3.0.2 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1(supports-color@8.1.1) - pac-resolver: 5.0.1 - raw-body: 2.5.3 - socks-proxy-agent: 5.0.1 - transitivePeerDependencies: - - supports-color - - pac-resolver@5.0.1: - dependencies: - degenerator: 3.0.4 - ip: 1.1.9 - netmask: 2.0.2 - package-json-from-dist@1.0.1: {} - pako@1.0.11: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -11933,23 +7491,12 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} + parseurl@1.3.3: {} - path-key@2.0.1: {} + path-exists@4.0.0: {} path-key@3.1.1: {} - path-key@4.0.0: {} - - path-loader@1.0.12(supports-color@8.1.1): - dependencies: - native-promise-only: 0.8.1 - superagent: 7.1.6(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - path-parse@1.0.7: {} path-scurry@2.0.2: @@ -11957,9 +7504,11 @@ snapshots: lru-cache: 11.2.6 minipass: 7.1.3 - path-type@4.0.0: {} + path-to-regexp@0.1.13: {} + + path-to-regexp@3.3.0: {} - path2@0.1.0: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -11967,9 +7516,7 @@ snapshots: pathval@1.1.1: {} - peek-readable@4.1.0: {} - - pend@1.2.0: {} + perfect-debounce@1.0.0: {} picocolors@1.1.1: {} @@ -11979,14 +7526,6 @@ snapshots: pify@2.3.0: {} - pify@3.0.0: {} - - pinkie-promise@2.0.1: - dependencies: - pinkie: 2.0.4 - - pinkie@2.0.4: {} - pirates@4.0.7: {} pkg-types@1.3.1: @@ -11995,6 +7534,12 @@ snapshots: mlly: 1.8.0 pathe: 2.0.3 + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.1.0 + pathe: 2.0.3 + possible-typed-array-names@1.1.0: {} postcss-import@15.1.0(postcss@8.5.6): @@ -12036,8 +7581,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prelude-ls@1.1.2: {} - prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.1: @@ -12060,18 +7603,14 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 - process-nextick-args@2.0.1: {} - - process-utils@4.0.0: + prisma@6.19.3(typescript@5.9.3): dependencies: - ext: 1.7.0 - fs2: 0.3.16 - memoizee: 0.4.17 - type: 2.7.3 - - process@0.11.10: {} - - promise-queue@2.2.5: {} + '@prisma/config': 6.19.3 + '@prisma/engines': 6.19.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - magicast prop-types@15.8.1: dependencies: @@ -12079,49 +7618,26 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 - proxy-agent@5.0.0: - dependencies: - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1(supports-color@8.1.1) - lru-cache: 5.1.1 - pac-proxy-agent: 5.0.0 - proxy-from-env: 1.1.0 - socks-proxy-agent: 5.0.1 - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} - - pump@3.0.3: + proxy-addr@2.0.7: dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@1.3.2: {} + forwarded: 0.2.0 + ipaddr.js: 1.9.1 punycode@2.3.1: {} + pure-rand@6.1.0: {} + qs@6.14.1: dependencies: side-channel: 1.1.0 - querystring@0.2.0: {} - - querystring@0.2.1: {} - queue-microtask@1.2.3: {} - quick-lru@5.1.1: {} - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - randomstring@1.3.1: - dependencies: - randombytes: 2.1.0 + range-parser@1.2.1: {} raw-body@2.5.3: dependencies: @@ -12130,6 +7646,11 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + rc9@2.1.2: + dependencies: + defu: 6.1.7 + destr: 2.0.5 + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -12187,45 +7708,12 @@ snapshots: dependencies: pify: 2.3.0 - readable-stream@1.1.14: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readable-web-to-node-stream@3.0.4: - dependencies: - readable-stream: 4.7.0 - - readdir-glob@1.1.3: - dependencies: - minimatch: 5.1.6 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -12255,6 +7743,8 @@ snapshots: redux@5.0.1: {} + reflect-metadata@0.2.2: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -12300,12 +7790,8 @@ snapshots: reselect@5.1.1: {} - resolve-alpn@1.2.1: {} - resolve-from@4.0.0: {} - resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -12320,19 +7806,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - responselike@2.0.1: - dependencies: - lowercase-keys: 2.0.0 - - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - reusify@1.1.0: {} - rimraf@2.2.8: {} - rollup@2.80.0: optionalDependencies: fsevents: 2.3.3 @@ -12371,12 +7846,7 @@ snapshots: '@rollup/rollup-win32-x64-gnu': 4.55.1 '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 - - run-async@2.4.1: {} - - run-parallel-limit@1.1.0: - dependencies: - queue-microtask: 1.2.3 + optional: true run-parallel@1.2.0: dependencies: @@ -12394,8 +7864,6 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -12411,151 +7879,42 @@ snapshots: safer-buffer@2.1.2: {} - sax@1.2.1: {} - scheduler@0.27.0: {} - seek-bzip@1.0.6: - dependencies: - commander: 2.20.3 - - semver@5.7.2: {} - semver@6.3.1: {} semver@7.7.3: {} - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 - - serverless-cloudfront-invalidate@1.12.2: + send@0.19.2: dependencies: - aws-sdk: 2.1693.0 - chalk: 2.4.2 - proxy-agent: 5.0.0 - randomstring: 1.3.1 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color - serverless-dynamodb@0.2.56: + serialize-javascript@6.0.2: dependencies: - '@aws-sdk/client-dynamodb': 3.965.0 - '@aws-sdk/lib-dynamodb': 3.965.0(@aws-sdk/client-dynamodb@3.965.0) - aws-dynamodb-local: 0.0.11 - transitivePeerDependencies: - - aws-crt - - serverless-offline@13.9.0(serverless@3.40.0(@types/node@20.19.27)): - dependencies: - '@aws-sdk/client-lambda': 3.966.0 - '@hapi/boom': 10.0.1 - '@hapi/h2o2': 10.0.4 - '@hapi/hapi': 21.4.4 - array-unflat-js: 0.1.3 - boxen: 7.1.1 - chalk: 5.6.2 - desm: 1.3.1 - execa: 8.0.1 - fs-extra: 11.3.3 - is-wsl: 3.1.0 - java-invoke-local: 0.0.6 - jose: 5.10.0 - js-string-escape: 1.0.1 - jsonpath-plus: 10.3.0 - jsonschema: 1.5.0 - jszip: 3.10.1 - luxon: 3.7.2 - node-schedule: 2.1.1 - p-memoize: 7.1.1 - serverless: 3.40.0(@types/node@20.19.27) - velocityjs: 2.1.5 - ws: 8.19.0 - transitivePeerDependencies: - - aws-crt - - bufferutil - - supports-color - - utf-8-validate + randombytes: 2.1.0 - serverless-s3-sync@3.5.1(serverless@3.40.0(@types/node@20.19.27)): + serve-static@1.16.3: dependencies: - '@auth0/s3': 1.0.1 - bluebird: 3.7.2 - mime: 2.6.0 - minimatch: 3.1.2 - serverless: 3.40.0(@types/node@20.19.27) - - serverless@3.40.0(@types/node@20.19.27): - dependencies: - '@aws-sdk/client-api-gateway': 3.966.0 - '@aws-sdk/client-cognito-identity-provider': 3.966.0 - '@aws-sdk/client-eventbridge': 3.966.0 - '@aws-sdk/client-iam': 3.966.0 - '@aws-sdk/client-lambda': 3.966.0 - '@aws-sdk/client-s3': 3.966.0 - '@serverless/dashboard-plugin': 7.2.3(@types/node@20.19.27)(supports-color@8.1.1) - '@serverless/platform-client': 4.5.1(supports-color@8.1.1) - '@serverless/utils': 6.15.0(@types/node@20.19.27) - abort-controller: 3.0.0 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - archiver: 5.3.2 - aws-sdk: 2.1693.0 - bluebird: 3.7.2 - cachedir: 2.4.0 - chalk: 4.1.2 - child-process-ext: 2.1.1 - ci-info: 3.9.0 - cli-progress-footer: 2.3.3 - d: 1.0.2 - dayjs: 1.11.19 - decompress: 4.2.1 - dotenv: 16.6.1 - dotenv-expand: 10.0.0 - essentials: 1.2.0 - ext: 1.7.0 - fastest-levenshtein: 1.0.16 - filesize: 10.1.6 - fs-extra: 10.1.0 - get-stdin: 8.0.0 - globby: 11.1.0 - graceful-fs: 4.2.11 - https-proxy-agent: 5.0.1(supports-color@8.1.1) - is-docker: 2.2.1 - js-yaml: 4.1.1 - json-colorizer: 2.2.2 - json-cycle: 1.5.0 - json-refs: 3.0.15(supports-color@8.1.1) - lodash: 4.17.21 - memoizee: 0.4.17 - micromatch: 4.0.8 - node-fetch: 2.7.0 - npm-registry-utilities: 1.0.0 - object-hash: 3.0.0 - open: 8.4.2 - path2: 0.1.0 - process-utils: 4.0.0 - promise-queue: 2.2.5 - require-from-string: 2.0.2 - semver: 7.7.3 - signal-exit: 3.0.7 - stream-buffers: 3.0.3 - strip-ansi: 6.0.1 - supports-color: 8.1.1 - tar: 6.2.1 - timers-ext: 0.1.8 - type: 2.7.3 - untildify: 4.0.0 - uuid: 9.0.1 - ws: 7.5.10 - yaml-ast-parser: 0.0.43 + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 transitivePeerDependencies: - - '@types/node' - - aws-crt - - bufferutil - - debug - - encoding - - utf-8-validate + - supports-color set-function-length@1.2.2: dependencies: @@ -12579,20 +7938,12 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 - setimmediate@1.0.5: {} - setprototypeof@1.2.0: {} - shebang-command@1.2.0: - dependencies: - shebang-regex: 1.0.0 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@1.0.0: {} - shebang-regex@3.0.0: {} side-channel-list@1.0.0: @@ -12625,22 +7976,8 @@ snapshots: siginfo@2.0.0: {} - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} - simple-git@3.30.0(supports-color@8.1.1): - dependencies: - '@kwsites/file-exists': 1.1.1(supports-color@8.1.1) - '@kwsites/promise-deferred': 1.1.1 - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - slash@3.0.0: {} - - smart-buffer@4.2.0: {} - smob@1.6.1: {} snake-case@3.0.4: @@ -12648,29 +7985,6 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - socks-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2(supports-color@8.1.1) - debug: 4.4.3(supports-color@8.1.1) - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - - sort-keys-length@1.0.1: - dependencies: - sort-keys: 1.1.2 - - sort-keys@1.1.2: - dependencies: - is-plain-obj: 1.1.0 - - sorted-array-functions@1.3.0: {} - source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -12680,24 +7994,12 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.6: {} - source-map@0.8.0-beta.0: dependencies: whatwg-url: 7.1.0 sourcemap-codec@1.4.8: {} - split2@3.2.2: - dependencies: - readable-stream: 3.6.2 - - sprintf-js@1.0.3: {} - - sprintf-kit@2.0.2: - dependencies: - es5-ext: 0.10.64 - ssf@0.11.2: dependencies: frac: 1.1.2 @@ -12713,27 +8015,7 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - stream-buffers@3.0.3: {} - - stream-promise@3.2.0: - dependencies: - 2-thenable: 1.0.0 - es5-ext: 0.10.64 - is-stream: 1.1.0 - - streamsink@1.2.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 + streamsearch@1.1.0: {} string.prototype.matchall@4.0.12: dependencies: @@ -12779,12 +8061,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - string_decoder@0.10.31: {} - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -12795,38 +8071,17 @@ snapshots: is-obj: 1.0.1 is-regexp: 1.0.0 - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-comments@2.0.1: {} - strip-dirs@2.1.0: - dependencies: - is-natural-number: 4.0.1 - - strip-final-newline@3.0.0: {} - strip-json-comments@3.1.1: {} strip-literal@1.3.0: dependencies: acorn: 8.15.0 - strip-outer@1.0.1: - dependencies: - escape-string-regexp: 1.0.5 - - strnum@2.1.2: {} - - strtok3@6.3.0: + strtok3@10.3.5: dependencies: '@tokenizer/token': 0.3.0 - peek-readable: 4.1.0 sucrase@3.35.1: dependencies: @@ -12838,38 +8093,10 @@ snapshots: tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 - superagent@7.1.6(supports-color@8.1.1): - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.4.3(supports-color@8.1.1) - fast-safe-stringify: 2.1.1 - form-data: 4.0.5 - formidable: 2.1.5 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.14.1 - readable-stream: 3.6.2 - semver: 7.7.3 - transitivePeerDependencies: - - supports-color - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@6.1.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} svg-parser@2.0.4: {} @@ -12906,33 +8133,6 @@ snapshots: - tsx - yaml - tar-stream@1.6.2: - dependencies: - bl: 1.2.3 - buffer-alloc: 1.2.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - readable-stream: 2.3.8 - to-buffer: 1.2.2 - xtend: 4.0.2 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - temp-dir@2.0.0: {} tempy@0.6.0: @@ -12957,22 +8157,13 @@ snapshots: dependencies: any-promise: 1.3.0 - throat@5.0.0: {} - - through@2.3.8: {} - time-zone@1.0.0: {} - timers-ext@0.1.8: - dependencies: - es5-ext: 0.10.64 - next-tick: 1.1.0 - tiny-invariant@1.3.3: {} tinybench@2.9.0: {} - tinyexec@0.3.2: {} + tinyexec@1.2.4: {} tinyglobby@0.2.15: dependencies: @@ -12983,20 +8174,15 @@ snapshots: tinyspy@2.2.1: {} - to-buffer@1.2.2: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 toidentifier@1.0.1: {} - token-types@4.2.1: + token-types@6.1.2: dependencies: + '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -13006,18 +8192,6 @@ snapshots: dependencies: punycode: 2.3.1 - traverse@0.6.11: - dependencies: - gopd: 1.2.0 - typedarray.prototype.slice: 1.0.5 - which-typed-array: 1.1.19 - - tree-kill@1.2.2: {} - - trim-repeated@1.0.0: - dependencies: - escape-string-regexp: 1.0.5 - ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -13026,34 +8200,6 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@1.10.2): - dependencies: - bundle-require: 5.1.0(esbuild@0.27.2) - cac: 6.7.14 - chokidar: 4.0.3 - consola: 3.4.2 - debug: 4.4.3(supports-color@8.1.1) - esbuild: 0.27.2 - fix-dts-default-cjs-exports: 1.0.1 - joycon: 3.1.1 - picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.21.0)(yaml@1.10.2) - resolve-from: 5.0.0 - rollup: 4.55.1 - source-map: 0.7.6 - sucrase: 3.35.1 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 - optionalDependencies: - postcss: 8.5.6 - typescript: 5.9.3 - transitivePeerDependencies: - - jiti - - supports-color - - tsx - - yaml - tsx@4.21.0: dependencies: esbuild: 0.27.2 @@ -13088,10 +8234,6 @@ snapshots: turbo-windows-64: 1.13.4 turbo-windows-arm64: 1.13.4 - type-check@0.3.2: - dependencies: - prelude-ls: 1.1.2 - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -13100,13 +8242,10 @@ snapshots: type-fest@0.16.0: {} - type-fest@0.21.3: {} - - type-fest@2.19.0: {} - - type-fest@3.13.1: {} - - type@2.7.3: {} + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 typed-array-buffer@1.0.3: dependencies: @@ -13141,24 +8280,15 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typedarray.prototype.slice@1.0.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - math-intrinsics: 1.1.0 - typed-array-buffer: 1.0.3 - typed-array-byte-offset: 1.0.4 + typedarray@0.0.6: {} - typescript-eslint@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + typescript-eslint@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -13167,6 +8297,12 @@ snapshots: ufo@1.6.2: {} + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + uint8array-extras@1.5.0: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -13174,17 +8310,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 - undici-types@6.21.0: {} - uni-global@1.0.0: - dependencies: - type: 2.7.3 - unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -13200,14 +8327,10 @@ snapshots: dependencies: crypto-random-string: 2.0.0 - universalify@0.1.2: {} - universalify@2.0.1: {} unpipe@1.0.0: {} - untildify@4.0.0: {} - upath@1.2.0: {} update-browserslist-db@1.2.3(browserslist@4.28.1): @@ -13220,40 +8343,15 @@ snapshots: dependencies: punycode: 2.3.1 - url@0.10.3: - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - use-sync-external-store@1.6.0(react@19.2.4): dependencies: react: 19.2.4 util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.19 - - uuid@8.0.0: {} - - uuid@8.3.2: {} - - uuid@9.0.1: {} - - validate-npm-package-name@3.0.0: - dependencies: - builtins: 1.0.3 + utils-merge@1.0.1: {} - velocityjs@2.1.5: - dependencies: - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color + vary@1.1.2: {} victory-vendor@36.9.2: dependencies: @@ -13275,7 +8373,7 @@ snapshots: vite-node@0.31.4(@types/node@20.19.27)(terser@5.44.1): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 mlly: 1.8.0 pathe: 1.1.2 picocolors: 1.1.1 @@ -13292,7 +8390,7 @@ snapshots: vite-plugin-pwa@1.2.0(vite@4.5.14(@types/node@20.19.27)(terser@5.44.1))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.15 vite: 4.5.14(@types/node@20.19.27)(terser@5.44.1) @@ -13332,12 +8430,12 @@ snapshots: '@vitest/snapshot': 0.31.4 '@vitest/spy': 0.31.4 '@vitest/utils': 0.31.4 - acorn: 8.15.0 + acorn: 8.16.0 acorn-walk: 8.3.4 cac: 6.7.14 chai: 4.5.0 concordance: 5.0.4 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.3 local-pkg: 0.4.3 magic-string: 0.30.21 pathe: 1.1.2 @@ -13358,20 +8456,6 @@ snapshots: - supports-color - terser - vm2@3.10.1: - dependencies: - acorn: 8.15.0 - acorn-walk: 8.3.4 - - watchpack@2.5.0: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - webidl-conversions@3.0.1: {} webidl-conversions@4.0.2: {} @@ -13430,10 +8514,6 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 - which@1.3.1: - dependencies: - isexe: 2.0.0 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -13443,10 +8523,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - widest-line@4.0.1: - dependencies: - string-width: 5.1.2 - wmf@1.0.2: {} word-wrap@1.2.5: {} @@ -13566,29 +8642,6 @@ snapshots: '@types/trusted-types': 2.0.7 workbox-core: 7.4.0 - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - - wrappy@1.0.2: {} - - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - ws@7.5.10: {} - - ws@8.19.0: {} - xlsx@0.18.5: dependencies: adler-32: 1.3.1 @@ -13599,46 +8652,17 @@ snapshots: wmf: 1.0.2 word: 0.3.0 - xml2js@0.6.2: - dependencies: - sax: 1.2.1 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - - xregexp@2.0.0: {} - xtend@4.0.2: {} yallist@3.1.1: {} - yallist@4.0.0: {} - - yaml-ast-parser@0.0.43: {} - yaml@1.10.2: optional: true - yamljs@0.3.0: - dependencies: - argparse: 1.0.10 - glob: 7.2.3 - - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} - zip-stream@4.1.1: - dependencies: - archiver-utils: 3.0.4 - compress-commons: 4.1.2 - readable-stream: 3.6.2 - zod-validation-error@3.5.4(zod@3.25.76): dependencies: zod: 3.25.76 diff --git a/scripts/check-porkbun-dns.sh b/scripts/check-porkbun-dns.sh deleted file mode 100644 index 0c59114..0000000 --- a/scripts/check-porkbun-dns.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env bash -# 역할: Porkbun DNS 레코드를 조회하고 이름/타입 조건으로 필터링해 출력한다. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -source "${ROOT_DIR}/scripts/lib/deploy-common.sh" - -STAGE="prod" -DOMAIN_ARG="" -RECORD_NAME_FILTER="" -RECORD_TYPE_FILTER="" -OUTPUT_FORMAT="table" - -# check-porkbun-dns.sh 사용법을 출력한다. -usage() { - cat <<'EOF' -Usage: check-porkbun-dns.sh [prod|dev] [options] - -Options: - --domain Root domain override (default: PORKBUN_ROOT_DOMAIN or inferred) - --name Filter by record name (e.g. my-commit.com, dev.my-commit.com, @) - --type Filter by record type (A|AAAA|CNAME|ALIAS|TXT|...) - --json Output as JSON - --table Output as table (default) - -h, --help Show this help - -Examples: - check-porkbun-dns.sh prod - check-porkbun-dns.sh prod --name my-commit.com - check-porkbun-dns.sh dev --name dev.my-commit.com --type CNAME --json -EOF -} - -# 문자열을 소문자로 정규화한다. -to_lower() { - printf '%s' "$1" | tr '[:upper:]' '[:lower:]' -} - -# 도메인 문자열을 끝점 없이 소문자로 정규화한다. -normalize_domain() { - local value="$1" - value="$(to_lower "${value}")" - printf '%s' "${value}" | sed 's/[.]*$//' -} - -# FQDN으로부터 루트 도메인을 단순 추론한다. -infer_root_domain_from_fqdn() { - local fqdn="$1" - IFS='.' read -r -a parts <<< "${fqdn}" - local count="${#parts[@]}" - if [[ "${count}" -lt 2 ]]; then - return 1 - fi - printf '%s.%s' "${parts[count-2]}" "${parts[count-1]}" -} - -# CLI 인자를 파싱한다. -parse_args() { - local stage_arg_count=0 - - if [[ "${1:-}" == "--" ]]; then - shift - fi - - while [[ $# -gt 0 ]]; do - case "$1" in - --) - shift - ;; - prod | dev) - STAGE="$1" - stage_arg_count=$((stage_arg_count + 1)) - if [[ ${stage_arg_count} -gt 1 ]]; then - echo "Stage can only be specified once." - usage - exit 1 - fi - shift - ;; - --domain) - DOMAIN_ARG="${2:-}" - if [[ -z "${DOMAIN_ARG}" ]]; then - echo "Missing value for --domain" - usage - exit 1 - fi - shift 2 - ;; - --name) - RECORD_NAME_FILTER="${2:-}" - if [[ -z "${RECORD_NAME_FILTER}" ]]; then - echo "Missing value for --name" - usage - exit 1 - fi - shift 2 - ;; - --type) - RECORD_TYPE_FILTER="${2:-}" - if [[ -z "${RECORD_TYPE_FILTER}" ]]; then - echo "Missing value for --type" - usage - exit 1 - fi - shift 2 - ;; - --json) - OUTPUT_FORMAT="json" - shift - ;; - --table) - OUTPUT_FORMAT="table" - shift - ;; - -h | --help) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" - usage - exit 1 - ;; - esac - done -} - -# Porkbun API로 루트 도메인 레코드 목록을 조회한다. -retrieve_records() { - local root_domain="$1" - curl -sS -X POST "https://api.porkbun.com/api/json/v3/dns/retrieve/${root_domain}" \ - -H "content-type: application/json" \ - -d "{\"apikey\":\"${PORKBUN_API_KEY}\",\"secretapikey\":\"${PORKBUN_SECRET_API_KEY}\"}" -} - -# Node.js로 필터링/출력을 처리한다. -render_records() { - ROOT_DOMAIN="${1}" \ - RECORD_NAME_FILTER="${RECORD_NAME_FILTER}" \ - RECORD_TYPE_FILTER="${RECORD_TYPE_FILTER}" \ - OUTPUT_FORMAT="${OUTPUT_FORMAT}" \ - PORKBUN_RESPONSE="${2}" \ - node - <<'NODE' -const body = JSON.parse(process.env.PORKBUN_RESPONSE || "{}"); -if (body.status !== "SUCCESS") { - const message = body.message || "Unknown error"; - throw new Error(`Porkbun API error: ${message}`); -} - -const rootDomain = normalizeDomain(process.env.ROOT_DOMAIN || ""); -const outputFormat = (process.env.OUTPUT_FORMAT || "table").toLowerCase(); -const typeFilter = (process.env.RECORD_TYPE_FILTER || "").trim().toUpperCase(); -const nameFilterRaw = (process.env.RECORD_NAME_FILTER || "").trim(); - -const records = Array.isArray(body.records) ? body.records : []; -const normalizedNameFilter = nameFilterRaw - ? normalizeFilterName(nameFilterRaw, rootDomain) - : ""; - -const filtered = records.filter((record) => { - if (normalizedNameFilter) { - const fqdn = recordNameToFqdn(record.name, rootDomain); - if (fqdn !== normalizedNameFilter) { - return false; - } - } - if (typeFilter) { - const type = String(record.type || "").toUpperCase(); - if (type !== typeFilter) { - return false; - } - } - return true; -}); - -if (outputFormat === "json") { - console.log(JSON.stringify(filtered, null, 2)); - process.exit(0); -} - -if (filtered.length === 0) { - console.log("No DNS records matched."); - process.exit(0); -} - -console.table( - filtered.map((record) => ({ - id: record.id, - type: record.type, - name: recordNameToFqdn(record.name, rootDomain), - content: record.content, - ttl: record.ttl, - prio: record.prio, - notes: record.notes || "", - })) -); - -function normalizeDomain(value) { - return String(value || "") - .trim() - .toLowerCase() - .replace(/\.+$/, ""); -} - -function normalizeFilterName(raw, rootDomain) { - const value = normalizeDomain(raw); - if (value === "@" || value === "") { - return rootDomain; - } - if (value === rootDomain) { - return rootDomain; - } - if (value.endsWith(`.${rootDomain}`)) { - return value; - } - if (value.includes(".")) { - return value; - } - return `${value}.${rootDomain}`; -} - -function recordNameToFqdn(name, rootDomain) { - const raw = normalizeDomain(name); - if (raw === "" || raw === "@") { - return rootDomain; - } - if (raw === rootDomain) { - return rootDomain; - } - if (raw.endsWith(`.${rootDomain}`)) { - return raw; - } - return `${raw}.${rootDomain}`; -} -NODE -} - -# 메인 실행 흐름: -# [1] 인자/환경 확인 -> [2] API 조회 -> [3] 필터링 출력 -main() { - parse_args "$@" - deploy_validate_stage "${STAGE}" - deploy_load_stage_env "${ROOT_DIR}" "${STAGE}" - deploy_require_env "PORKBUN_API_KEY" - deploy_require_env "PORKBUN_SECRET_API_KEY" - - local root_domain - if [[ -n "${DOMAIN_ARG}" ]]; then - root_domain="$(normalize_domain "${DOMAIN_ARG}")" - elif [[ -n "${PORKBUN_ROOT_DOMAIN:-}" ]]; then - root_domain="$(normalize_domain "${PORKBUN_ROOT_DOMAIN}")" - elif [[ -n "${WEB_DOMAIN_NAME:-}" ]]; then - root_domain="$(infer_root_domain_from_fqdn "$(normalize_domain "${WEB_DOMAIN_NAME}")" || true)" - if [[ -z "${root_domain}" ]]; then - echo "Failed to infer root domain from WEB_DOMAIN_NAME=${WEB_DOMAIN_NAME}" - echo "Set PORKBUN_ROOT_DOMAIN or pass --domain" - exit 1 - fi - else - echo "Root domain is required. Set PORKBUN_ROOT_DOMAIN or pass --domain" - exit 1 - fi - - echo "[1/3] Resolve root domain: ${root_domain}" - echo "[2/3] Retrieve DNS records from Porkbun" - local response - response="$(retrieve_records "${root_domain}")" - - echo "[3/3] Render records (${OUTPUT_FORMAT})" - render_records "${root_domain}" "${response}" -} - -main "$@" diff --git a/scripts/create-aws-acm.sh b/scripts/create-aws-acm.sh deleted file mode 100644 index 1be9e08..0000000 --- a/scripts/create-aws-acm.sh +++ /dev/null @@ -1,293 +0,0 @@ -#!/usr/bin/env bash -# 역할: ACM 인증서를 생성하고, 선택적으로 DNS provider(Porkbun)로 검증 레코드를 자동 동기화한다. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -source "${ROOT_DIR}/scripts/lib/deploy-common.sh" - -STAGE="prod" -DOMAIN="" -DNS_PROVIDER="${DNS_PROVIDER:-none}" -ACM_REGION="${ACM_REGION:-us-east-1}" -WAIT_FOR_VALIDATION="false" -SAN_CSV="${ACM_SUBJECT_ALTERNATIVE_NAMES:-}" - -# create-aws-acm.sh 사용법을 출력한다. -usage() { - cat <<'EOF' -Usage: create-aws-acm.sh [prod|dev] [options] - -Options: - --domain Target domain (default: WEB_DOMAIN_NAME from env) - --dns-provider DNS provider for validation record sync (default: none) - --region ACM region (default: us-east-1) - --sans Subject Alternative Names, comma-separated - --wait Wait until certificate status becomes ISSUED - -h, --help Show this help - -Examples: - create-aws-acm.sh prod --domain my-commit.com - create-aws-acm.sh prod --domain my-commit.com --dns-provider porkbun --wait -EOF -} - -# 문자열을 소문자로 정규화한다. -to_lower() { - printf '%s' "$1" | tr '[:upper:]' '[:lower:]' -} - -# CLI 인자를 파싱한다. -parse_args() { - local stage_arg_count=0 - - if [[ "${1:-}" == "--" ]]; then - shift - fi - - while [[ $# -gt 0 ]]; do - case "$1" in - --) - shift - ;; - prod | dev) - STAGE="$1" - stage_arg_count=$((stage_arg_count + 1)) - if [[ ${stage_arg_count} -gt 1 ]]; then - echo "Stage can only be specified once." - usage - exit 1 - fi - shift - ;; - --domain) - DOMAIN="${2:-}" - if [[ -z "${DOMAIN}" ]]; then - echo "Missing value for --domain" - usage - exit 1 - fi - shift 2 - ;; - --dns-provider) - DNS_PROVIDER="${2:-}" - if [[ -z "${DNS_PROVIDER}" ]]; then - echo "Missing value for --dns-provider" - usage - exit 1 - fi - shift 2 - ;; - --region) - ACM_REGION="${2:-}" - if [[ -z "${ACM_REGION}" ]]; then - echo "Missing value for --region" - usage - exit 1 - fi - shift 2 - ;; - --sans) - SAN_CSV="${2:-}" - if [[ -z "${SAN_CSV}" ]]; then - echo "Missing value for --sans" - usage - exit 1 - fi - shift 2 - ;; - --wait) - WAIT_FOR_VALIDATION="true" - shift - ;; - -h | --help) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" - usage - exit 1 - ;; - esac - done -} - -# ACM API가 검증 레코드를 노출할 때까지 대기 후 텍스트 목록을 반환한다. -fetch_validation_records() { - local cert_arn="$1" - local records="" - local attempt - for attempt in $(seq 1 30); do - records="$( - aws acm describe-certificate \ - --region "${ACM_REGION}" \ - --certificate-arn "${cert_arn}" \ - --query 'Certificate.DomainValidationOptions[?ResourceRecord!=null].[ResourceRecord.Name,ResourceRecord.Type,ResourceRecord.Value]' \ - --output text - )" - if [[ -n "${records}" ]]; then - printf '%s' "${records}" - return 0 - fi - sleep 2 - done - echo "Failed to fetch validation ResourceRecord from ACM." >&2 - return 1 -} - -# 검증 레코드를 DNS provider에 반영하거나, 수동 적용용으로 출력한다. -apply_validation_records() { - local records="$1" - local record_name="" - local record_type="" - local record_value="" - - while IFS=$'\t' read -r record_name record_type record_value; do - [[ -z "${record_name}" ]] && continue - if [[ "${DNS_PROVIDER}" == "none" ]]; then - echo "Manual DNS record: type=${record_type} name=${record_name} value=${record_value}" - continue - fi - - echo "Sync validation record via ${DNS_PROVIDER}: ${record_name} -> ${record_value}" - DNS_PROVIDER="${DNS_PROVIDER}" \ - DNS_RECORD_NAME="${record_name}" \ - DNS_RECORD_CONTENT="${record_value}" \ - PORKBUN_RECORD_TYPE="${record_type}" \ - node "${ROOT_DIR}/scripts/dns-sync.mjs" - done <<< "${records}" -} - -# ACM 인증서를 생성하고 ARN을 반환한다. -request_certificate() { - local domain="$1" - local token - token="$(to_lower "${domain}" | tr -cd 'a-z0-9' | cut -c1-32)" - if [[ -z "${token}" ]]; then - token="mycommitacm$(date +%s)" - fi - - local -a args - args=( - acm request-certificate - --region "${ACM_REGION}" - --domain-name "${domain}" - --validation-method DNS - --idempotency-token "${token}" - --query CertificateArn - --output text - ) - - if [[ -n "${SAN_CSV}" ]]; then - IFS=',' read -r -a san_items <<< "${SAN_CSV}" - local san - local -a sans - sans=() - for san in "${san_items[@]}"; do - san="${san#"${san%%[![:space:]]*}"}" - san="${san%"${san##*[![:space:]]}"}" - [[ -z "${san}" ]] && continue - sans+=("${san}") - done - if [[ ${#sans[@]} -gt 0 ]]; then - args+=(--subject-alternative-names "${sans[@]}") - fi - fi - - aws "${args[@]}" -} - -# 현재 인증서 상태를 출력한다. -print_certificate_status() { - local cert_arn="$1" - local status - status="$( - aws acm describe-certificate \ - --region "${ACM_REGION}" \ - --certificate-arn "${cert_arn}" \ - --query 'Certificate.Status' \ - --output text - )" - echo "Certificate status: ${status}" -} - -# 생성된 ACM ARN을 어떤 env 파일에 반영해야 하는지 박스 형태로 안내한다. -print_env_update_box() { - local cert_arn="$1" - local env_file="" - if [[ "${STAGE}" == "prod" ]]; then - env_file=".env.production" - else - env_file=".env.development" - fi - - cat < [2] 검증 레코드 반영 -> [3] 선택적 발급 대기 -main() { - parse_args "$@" - deploy_validate_stage "${STAGE}" - deploy_load_stage_env "${ROOT_DIR}" "${STAGE}" - - if [[ -z "${DOMAIN}" ]]; then - DOMAIN="${WEB_DOMAIN_NAME:-}" - fi - if [[ -z "${DOMAIN}" ]]; then - echo "Domain is required. Set --domain or WEB_DOMAIN_NAME." - exit 1 - fi - - DNS_PROVIDER="$(to_lower "${DNS_PROVIDER}")" - if [[ "${DNS_PROVIDER}" != "none" && "${DNS_PROVIDER}" != "porkbun" ]]; then - echo "Invalid dns provider: ${DNS_PROVIDER}" - echo "Allowed: none | porkbun" - exit 1 - fi - - if [[ "${ACM_REGION}" != "us-east-1" ]]; then - echo "Warning: CloudFront custom domain certificates should be in us-east-1." - fi - - echo "[1/3] Request ACM certificate for ${DOMAIN} (${ACM_REGION})" - local cert_arn - cert_arn="$(request_certificate "${DOMAIN}")" - if [[ -z "${cert_arn}" || "${cert_arn}" == "None" ]]; then - echo "Failed to request ACM certificate." - exit 1 - fi - echo "Certificate ARN: ${cert_arn}" - - echo "[2/3] Resolve and apply ACM DNS validation records" - local records - records="$(fetch_validation_records "${cert_arn}")" - apply_validation_records "${records}" - - echo "[3/3] Finalize" - if [[ "${WAIT_FOR_VALIDATION}" == "true" ]]; then - echo "Waiting for certificate validation..." - aws acm wait certificate-validated \ - --region "${ACM_REGION}" \ - --certificate-arn "${cert_arn}" - echo "Certificate validated." - else - echo "Skip wait. Use --wait to block until ISSUED." - fi - - print_certificate_status "${cert_arn}" - print_env_update_box "${cert_arn}" -} - -main "$@" diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100644 index 63245c6..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -# 역할: 전체 배포 오케스트레이션(API -> Web -> DNS 후처리) 실행 엔트리포인트. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -source "${ROOT_DIR}/scripts/lib/deploy-common.sh" - -# 배포 오케스트레이션 엔트리포인트: -# [1] API 배포 -> [2] Web 배포 -> [3] DNS 후처리 -main() { - deploy_parse_args "$@" - if [[ "${DEPLOY_SHOW_HELP}" == "true" ]]; then - deploy_usage - exit 0 - fi - - deploy_validate_stage "${DEPLOY_STAGE}" - deploy_load_stage_env "${ROOT_DIR}" "${DEPLOY_STAGE}" - - deploy_require_env "JWT_SECRET" - deploy_require_env "WEB_DOMAIN_NAME" - deploy_require_env "ACM_CERTIFICATE_ARN" - deploy_validate_cloudfront_acm_arn "${ACM_CERTIFICATE_ARN}" - - local stage="${DEPLOY_STAGE}" - local region="${AWS_REGION:-ap-northeast-2}" - local api_stack_name="${API_STACK_NAME:-${stage}-my-commit-api}" - local web_stack_name="${WEB_STACK_NAME:-${stage}-my-commit-web}" - local resolved_dns_provider="${DEPLOY_DNS_PROVIDER_ARG:-${DNS_PROVIDER:-none}}" - local web_origin="https://${WEB_DOMAIN_NAME}" - local -a sls_profile_args=() - - if [[ -n "${AWS_PROFILE:-}" ]]; then - sls_profile_args=(--aws-profile "${AWS_PROFILE}") - fi - - # [1] API Serverless 배포 - echo "[1/3] Deploy API: pnpm --filter my-commit-api run sls:deploy" - MY_COMMIT_ROOT_DEPLOY=1 WEB_ORIGIN="${web_origin}" JWT_SECRET="${JWT_SECRET}" \ - pnpm --filter my-commit-api run sls:deploy --stage "${stage}" --region "${region}" "${sls_profile_args[@]}" - - local api_url - api_url="$(deploy_resolve_api_url "${api_stack_name}" "${region}")" - - # [2] S3, CloudFront 배포 (배포된 API 주소 주입) - echo "[2/3] Deploy Web: pnpm --filter my-commit-client run deploy" - MY_COMMIT_ROOT_DEPLOY=1 VITE_API_URL="${api_url}" \ - WEB_DOMAIN_NAME="${WEB_DOMAIN_NAME}" \ - ACM_CERTIFICATE_ARN="${ACM_CERTIFICATE_ARN}" \ - pnpm --filter my-commit-client run deploy --stage "${stage}" --region "${region}" "${sls_profile_args[@]}" - - # [3] DNS Provider API 활용해서 CloudFront 주소에 DNS 설정(등록/갱신) - echo "[3/3] Post deploy DNS sync" - UPDATE_DNS_RECORD="${UPDATE_DNS_RECORD:-false}" \ - DNS_PROVIDER="${resolved_dns_provider}" \ - WEB_DOMAIN_NAME="${WEB_DOMAIN_NAME}" \ - WEB_STACK_NAME="${web_stack_name}" \ - AWS_REGION="${region}" \ - bash "${ROOT_DIR}/scripts/sync-dns-after-deploy.sh" -} - -main "$@" diff --git a/scripts/dns-providers/porkbun.mjs b/scripts/dns-providers/porkbun.mjs deleted file mode 100644 index 4bbe51c..0000000 --- a/scripts/dns-providers/porkbun.mjs +++ /dev/null @@ -1,240 +0,0 @@ -// 역할: Porkbun DNS API를 사용해 대상 도메인 레코드를 조회/정리/생성한다. -export async function syncPorkbunDns({ - recordName, - recordContent, - env = process.env, - fetchImpl = fetch, -}) { - const apiKey = requireEnv(env, "PORKBUN_API_KEY"); - const secretApiKey = requireEnv(env, "PORKBUN_SECRET_API_KEY"); - const inferredRoot = inferRootDomain(recordName); - const rootDomain = normalizeDomain( - env.PORKBUN_ROOT_DOMAIN || inferredRoot, - "PORKBUN_ROOT_DOMAIN" - ); - const hostLabel = getHostLabel(recordName, rootDomain); - - const defaultRecordType = hostLabel === "" ? "ALIAS" : "CNAME"; - const recordType = (env.PORKBUN_RECORD_TYPE || defaultRecordType) - .trim() - .toUpperCase(); - const ttl = parseTtl(env.PORKBUN_TTL); - const dryRun = parseBoolean(env.PORKBUN_DRY_RUN); - - if (hostLabel === "" && recordType === "CNAME") { - throw new Error("Apex domain cannot use CNAME. Use ALIAS/A/AAAA."); - } - - if (!env.PORKBUN_ROOT_DOMAIN) { - console.log(`[dns-sync] PORKBUN_ROOT_DOMAIN inferred as ${rootDomain}`); - } - - const porkbunRequest = createPorkbunRequest({ - apiKey, - secretApiKey, - fetchImpl, - }); - - const existingResponse = await porkbunRequest(`/dns/retrieve/${rootDomain}`); - const existingRecords = Array.isArray(existingResponse.records) - ? existingResponse.records - : []; - - const conflictingTypes = new Set(["A", "AAAA", "CNAME", "ALIAS"]); - const conflicts = existingRecords.filter((record) => { - const type = String(record.type || "").toUpperCase(); - if (!conflictingTypes.has(type)) { - return false; - } - const fqdn = recordNameToFqdn(record.name, rootDomain); - return fqdn === recordName; - }); - - const expectedContent = normalizeRecordContent(recordContent); - let keptMatchingRecord = null; - const recordsToDelete = []; - - for (const record of conflicts) { - if (isSameTargetRecord(record, recordType, expectedContent)) { - // Keep one matching record and delete duplicates/other conflicting records only. - if (keptMatchingRecord === null) { - keptMatchingRecord = record; - continue; - } - } - recordsToDelete.push(record); - } - - if (recordsToDelete.length > 0) { - console.log( - `[dns-sync] Removing ${recordsToDelete.length} existing DNS record(s).` - ); - } - - for (const record of recordsToDelete) { - const id = record.id; - if (!id) { - throw new Error("Porkbun API returned a record without id."); - } - console.log( - `[dns-sync] delete id=${id} type=${String(record.type || "").toUpperCase()} name=${String(record.name || "")}` - ); - if (!dryRun) { - await porkbunRequest(`/dns/delete/${rootDomain}/${id}`); - } - } - - if (keptMatchingRecord) { - const keptId = keptMatchingRecord.id ? `id=${keptMatchingRecord.id} ` : ""; - console.log( - `[dns-sync] Existing record already matches target (${keptId}type=${recordType}).` - ); - } else { - console.log( - `[dns-sync] create type=${recordType} name=${hostLabel || "(apex)"} content=${recordContent} ttl=${ttl}` - ); - if (!dryRun) { - await porkbunRequest(`/dns/create/${rootDomain}`, { - name: hostLabel, - type: recordType, - content: recordContent, - ttl, - }); - } - } - - if (dryRun) { - console.log("[dns-sync] dry-run enabled: no changes were applied."); - } else { - if (keptMatchingRecord && recordsToDelete.length === 0) { - console.log("[dns-sync] Porkbun DNS sync skipped: DNS record already up to date."); - } else { - console.log("[dns-sync] Porkbun DNS sync completed."); - } - } -} - -// Porkbun API 요청 함수를 생성한다. -function createPorkbunRequest({ apiKey, secretApiKey, fetchImpl }) { - return async (path, payload = {}) => { - const url = `https://api.porkbun.com/api/json/v3${path}`; - const response = await fetchImpl(url, { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify({ - apikey: apiKey, - secretapikey: secretApiKey, - ...payload, - }), - }); - - const body = await response.json().catch(() => ({})); - if (!response.ok) { - throw new Error( - `Porkbun API request failed (${response.status}): ${path}` - ); - } - if (body.status !== "SUCCESS") { - const message = body.message || "Unknown error"; - throw new Error(`Porkbun API error for ${path}: ${message}`); - } - return body; - }; -} - -// 도메인 문자열을 소문자/FQDN 형식으로 정규화한다. -function normalizeDomain(value, envName) { - const normalized = String(value || "") - .trim() - .toLowerCase() - .replace(/\.+$/, ""); - if (!normalized.includes(".")) { - throw new Error(`${envName} must be a valid domain.`); - } - return normalized; -} - -// FQDN에서 루트 도메인을 단순 추론한다. -function inferRootDomain(fqdn) { - const parts = fqdn.split(".").filter(Boolean); - if (parts.length < 2) { - throw new Error("DNS_RECORD_NAME must include a root domain."); - } - return `${parts[parts.length - 2]}.${parts[parts.length - 1]}`; -} - -// FQDN과 루트 도메인을 비교해 host 라벨(@ 제외)을 반환한다. -function getHostLabel(fqdn, rootDomain) { - if (fqdn === rootDomain) { - return ""; - } - const suffix = `.${rootDomain}`; - if (!fqdn.endsWith(suffix)) { - throw new Error( - `DNS_RECORD_NAME (${fqdn}) does not match PORKBUN_ROOT_DOMAIN (${rootDomain}).` - ); - } - return fqdn.slice(0, -suffix.length); -} - -// Porkbun record name을 절대 FQDN 형태로 변환한다. -function recordNameToFqdn(name, rootDomain) { - const raw = String(name || "") - .trim() - .toLowerCase() - .replace(/\.+$/, ""); - if (raw === "" || raw === "@") { - return rootDomain; - } - if (raw === rootDomain) { - return rootDomain; - } - if (raw.endsWith(`.${rootDomain}`)) { - return raw; - } - return `${raw}.${rootDomain}`; -} - -// TTL 입력값을 검증하고 기본값(600)을 적용한다. -function parseTtl(value) { - if (value === undefined || value === null || value === "") { - return 600; - } - const ttl = Number(value); - if (!Number.isInteger(ttl) || ttl <= 0) { - throw new Error("PORKBUN_TTL must be a positive integer."); - } - return ttl; -} - -// 문자열 기반 불리언 표현을 true/false로 파싱한다. -function parseBoolean(value) { - return ["1", "true", "yes", "on"].includes(String(value || "").toLowerCase()); -} - -// DNS 레코드 content 비교를 위해 소문자/끝점(.) 기준으로 정규화한다. -function normalizeRecordContent(value) { - return String(value || "") - .trim() - .toLowerCase() - .replace(/\.+$/, ""); -} - -// 대상 타입/값과 정확히 일치하는 기존 레코드인지 판단한다. -function isSameTargetRecord(record, expectedType, expectedContent) { - const type = String(record.type || "").toUpperCase(); - if (type !== expectedType) { - return false; - } - const currentContent = normalizeRecordContent(record.content); - return currentContent === expectedContent; -} - -// provider 환경변수에서 필수 키를 읽고 누락 시 오류를 던진다. -function requireEnv(env, name) { - const value = env[name]; - if (!value || value.trim() === "") { - throw new Error(`${name} is required.`); - } - return value; -} diff --git a/scripts/dns-sync.mjs b/scripts/dns-sync.mjs deleted file mode 100644 index d64ee13..0000000 --- a/scripts/dns-sync.mjs +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env node -// 역할: DNS provider를 선택해 공통 입력을 검증한 뒤 provider 구현을 호출한다. - -import { syncPorkbunDns } from "./dns-providers/porkbun.mjs"; - -// [1] Provider 선택 -const provider = (process.env.DNS_PROVIDER || "none").trim().toLowerCase(); - -if (provider === "none") { - console.log("DNS sync skipped (DNS_PROVIDER=none)."); - process.exit(0); -} - -const dnsRecordName = normalizeDomain( - requireEnv("DNS_RECORD_NAME"), - "DNS_RECORD_NAME" -); -const dnsRecordContent = normalizeDomain( - requireEnv("DNS_RECORD_CONTENT"), - "DNS_RECORD_CONTENT" -); - -// [2] Provider 구현 호출 -try { - if (provider === "porkbun") { - await syncPorkbunDns({ - recordName: dnsRecordName, - recordContent: dnsRecordContent, - env: process.env, - }); - } else { - throw new Error(`Unsupported DNS_PROVIDER: ${provider}`); - } -} catch (error) { - console.error(`[dns-sync] ${error.message}`); - process.exit(1); -} - -// 도메인 문자열을 소문자/FQDN 형식으로 정규화한다. -function normalizeDomain(value, envName) { - const normalized = String(value || "") - .trim() - .toLowerCase() - .replace(/\.+$/, ""); - if (!normalized.includes(".")) { - throw new Error(`${envName} must be a valid domain.`); - } - return normalized; -} - -// 필수 환경변수를 읽고 누락 시 오류를 던진다. -function requireEnv(name) { - const value = process.env[name]; - if (!value || value.trim() === "") { - throw new Error(`${name} is required.`); - } - return value; -} diff --git a/scripts/lib/deploy-common.sh b/scripts/lib/deploy-common.sh deleted file mode 100644 index 63eecf1..0000000 --- a/scripts/lib/deploy-common.sh +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env bash -# 역할: deploy.sh에서 사용하는 인자 파싱/환경 로드/검증/API URL 조회 공통 함수 모음. - -DEPLOY_STAGE="prod" -DEPLOY_DNS_PROVIDER_ARG="" -DEPLOY_SHOW_HELP="false" - -# deploy.sh 사용법/예시를 출력한다. -deploy_usage() { - cat <<'EOF' -Usage: deploy.sh [prod|dev] [--dns-provider ] -DNS sync runs only when UPDATE_DNS_RECORD=true. - -Examples: - deploy.sh - deploy.sh prod - deploy.sh dev --dns-provider porkbun - deploy.sh --dns-provider porkbun prod -EOF -} - -# CLI 인자를 파싱해 stage, dns-provider, help 플래그를 결정한다. -deploy_parse_args() { - DEPLOY_STAGE="prod" - DEPLOY_DNS_PROVIDER_ARG="" - DEPLOY_SHOW_HELP="false" - - if [[ "${1:-}" == "--" ]]; then - shift - fi - - local stage_arg_count=0 - while [[ $# -gt 0 ]]; do - case "$1" in - --) - shift - ;; - prod | dev) - DEPLOY_STAGE="$1" - stage_arg_count=$((stage_arg_count + 1)) - if [[ ${stage_arg_count} -gt 1 ]]; then - echo "Stage can only be specified once." - deploy_usage - exit 1 - fi - shift - ;; - --dns-provider) - if [[ -z "${2:-}" ]]; then - echo "Missing value for --dns-provider" - deploy_usage - exit 1 - fi - DEPLOY_DNS_PROVIDER_ARG="$2" - shift 2 - ;; - --dns-provider=*) - DEPLOY_DNS_PROVIDER_ARG="${1#*=}" - shift - ;; - -h | --help) - DEPLOY_SHOW_HELP="true" - shift - ;; - *) - echo "Unknown argument: $1" - deploy_usage - exit 1 - ;; - esac - done -} - -# stage 값이 허용된 범위(prod/dev)인지 검증한다. -deploy_validate_stage() { - local stage="$1" - if [[ "${stage}" != "prod" && "${stage}" != "dev" ]]; then - echo "Invalid stage: ${stage}" - echo "Allowed: prod | dev" - exit 1 - fi -} - -# 주어진 env 파일이 있으면 로드한다. -deploy_load_env_file() { - local file="$1" - if [[ -f "${file}" ]]; then - echo "Load env: ${file}" - set -a - # shellcheck disable=SC1090 - source "${file}" - set +a - fi -} - -# stage(prod/dev)에 맞는 루트 .env 계열 파일을 순서대로 로드한다. -deploy_load_stage_env() { - local root_dir="$1" - local stage="$2" - local env_name - - if [[ "${stage}" == "prod" ]]; then - env_name="production" - else - env_name="development" - fi - - # Later files override earlier values. - deploy_load_env_file "${root_dir}/.env" - deploy_load_env_file "${root_dir}/.env.local" - deploy_load_env_file "${root_dir}/.env.${env_name}" - deploy_load_env_file "${root_dir}/.env.${env_name}.local" -} - -# 필수 환경변수가 비어 있지 않은지 확인한다. -deploy_require_env() { - local name="$1" - if [[ -z "${!name:-}" ]]; then - echo "${name} is required" - exit 1 - fi -} - -# ARN에서 region을 추출한다. 파싱 실패 시 빈 문자열을 반환한다. -deploy_extract_arn_region() { - local arn="$1" - local service="" - local region="" - # arn:partition:service:region:account-id:resource - IFS=':' read -r _ _ service region _ _ <<< "${arn}" - if [[ -z "${service}" || -z "${region}" ]]; then - printf '%s' "" - return 0 - fi - printf '%s' "${region}" -} - -# CloudFront custom domain에 필요한 ACM ARN 조건(us-east-1)을 검증한다. -deploy_validate_cloudfront_acm_arn() { - local acm_arn="$1" - local acm_region="" - acm_region="$(deploy_extract_arn_region "${acm_arn}")" - if [[ -z "${acm_region}" ]]; then - echo "Invalid ACM_CERTIFICATE_ARN format: ${acm_arn}" - exit 1 - fi - if [[ "${acm_region}" != "us-east-1" ]]; then - echo "Invalid ACM_CERTIFICATE_ARN region for CloudFront: ${acm_region}" - echo "CloudFront custom domain requires ACM certificate in us-east-1." - echo "Update ACM_CERTIFICATE_ARN to a us-east-1 certificate ARN." - exit 1 - fi -} - -# API 스택 Output에서 HttpApiUrl을 조회해 반환한다. -deploy_resolve_api_url() { - local api_stack_name="$1" - local region="$2" - local api_url - - api_url="$( - aws cloudformation describe-stacks \ - --stack-name "${api_stack_name}" \ - --region "${region}" \ - --query 'Stacks[0].Outputs[?OutputKey==`HttpApiUrl`].OutputValue' \ - --output text - )" - - if [[ -z "${api_url}" || "${api_url}" == "None" ]]; then - echo "Failed to resolve HttpApiUrl from ${api_stack_name}" - exit 1 - fi - printf '%s' "${api_url}" -} diff --git a/scripts/reset-aws.sh b/scripts/reset-aws.sh deleted file mode 100644 index 83d9140..0000000 --- a/scripts/reset-aws.sh +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env bash -# 역할: stage 기준 API/Web 스택과 관련 버킷을 제거하고(기본) ACM까지 삭제해 AWS 리소스를 초기화한다. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -source "${ROOT_DIR}/scripts/lib/deploy-common.sh" - -STAGE="prod" -REMOVE_ACM="true" -ACM_ARN_ARG="" - -# reset-aws.sh 사용법을 출력한다. -usage() { - cat <<'EOF' -Usage: reset-aws.sh [prod|dev] [options] - -Options: - --remove-acm Delete ACM certificate (default behavior) - --keep-acm Keep ACM certificate (skip deletion) - --acm-arn ACM ARN override (default: ACM_CERTIFICATE_ARN from env) - -h, --help Show this help - -Examples: - reset-aws.sh prod - reset-aws.sh dev --keep-acm - reset-aws.sh prod --remove-acm --acm-arn arn:aws:acm:us-east-1:... -EOF -} - -# CLI 인자를 파싱한다. -parse_args() { - local stage_arg_count=0 - - if [[ "${1:-}" == "--" ]]; then - shift - fi - - while [[ $# -gt 0 ]]; do - case "$1" in - --) - shift - ;; - prod | dev) - STAGE="$1" - stage_arg_count=$((stage_arg_count + 1)) - if [[ ${stage_arg_count} -gt 1 ]]; then - echo "Stage can only be specified once." - usage - exit 1 - fi - shift - ;; - --remove-acm) - REMOVE_ACM="true" - shift - ;; - --keep-acm) - REMOVE_ACM="false" - shift - ;; - --acm-arn) - ACM_ARN_ARG="${2:-}" - if [[ -z "${ACM_ARN_ARG}" ]]; then - echo "Missing value for --acm-arn" - usage - exit 1 - fi - shift 2 - ;; - -h | --help) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" - usage - exit 1 - ;; - esac - done -} - -# CloudFormation 스택이 현재 존재하는지 확인한다. -stack_exists() { - local stack_name="$1" - local region="$2" - local output - if output="$( - aws cloudformation describe-stacks \ - --stack-name "${stack_name}" \ - --region "${region}" 2>&1 - )"; then - return 0 - fi - - if printf '%s' "${output}" | grep -qi "Stack with id ${stack_name} does not exist"; then - # describe-stacks가 false-negative를 내는 환경(권한/정책/일시 이슈)을 대비해 list-stacks로 한 번 더 확인. - if stack_exists_via_list "${stack_name}" "${region}"; then - echo "Warning: describe-stacks reported not found, but list-stacks found ${stack_name}." >&2 - return 0 - fi - return 1 - fi - - echo "Failed to query stack (${stack_name}) in region ${region}." >&2 - echo "AWS CLI error: ${output}" >&2 - return 2 -} - -# list-stacks 기반으로 스택 존재 여부를 보조 확인한다. -stack_exists_via_list() { - local stack_name="$1" - local region="$2" - local count - count="$( - aws cloudformation list-stacks \ - --region "${region}" \ - --query "length(StackSummaries[?StackName=='${stack_name}' && StackStatus!='DELETE_COMPLETE'])" \ - --output text 2>/dev/null || echo "0" - )" - [[ "${count}" != "0" && "${count}" != "None" ]] -} - -# 현재 실행되는 AWS 컨텍스트(profile/account)를 출력한다. -print_aws_context() { - local profile="${AWS_PROFILE:-default}" - local caller - caller="$(aws sts get-caller-identity --output text --query 'join(`|`, [Account, Arn])' 2>/dev/null || true)" - echo "AWS context: profile=${profile} region=${AWS_REGION:-ap-northeast-2}" - if [[ -n "${AWS_ACCESS_KEY_ID:-}" ]]; then - echo "AWS credential source: env(AWS_ACCESS_KEY_ID) overrides profile selection" - else - echo "AWS credential source: profile/default chain" - fi - if [[ -n "${caller}" ]]; then - echo "AWS caller: ${caller}" - fi -} - -# AWS 인증/권한 상태를 먼저 확인한다. -assert_aws_access() { - if aws sts get-caller-identity >/dev/null 2>&1; then - return 0 - fi - echo "AWS credentials are not available or not authorized." >&2 - echo "Check aws login/profile/env before running undeploy." >&2 - return 1 -} - -# ACM ARN에서 region을 추출한다. 실패 시 빈 문자열을 반환한다. -extract_region_from_acm_arn() { - local acm_arn="$1" - local region="" - # arn:partition:service:region:account-id:resource - IFS=':' read -r _ _ service region _ _ <<< "${acm_arn}" - if [[ "${service}" != "acm" || -z "${region}" ]]; then - printf '%s' "" - return 0 - fi - printf '%s' "${region}" -} - -# ACM ARN에서 account id를 추출한다. 실패 시 빈 문자열을 반환한다. -extract_account_from_acm_arn() { - local acm_arn="$1" - local account="" - # arn:partition:service:region:account-id:resource - IFS=':' read -r _ _ service _ account _ <<< "${acm_arn}" - if [[ "${service}" != "acm" || -z "${account}" ]]; then - printf '%s' "" - return 0 - fi - printf '%s' "${account}" -} - -# 호출자 계정과 ACM ARN 계정이 다르면 즉시 중단해 오삭제를 방지한다. -validate_acm_account_alignment() { - local acm_arn="$1" - local caller_account="$2" - local arn_account="" - if [[ -z "${acm_arn}" || -z "${caller_account}" ]]; then - return 0 - fi - arn_account="$(extract_account_from_acm_arn "${acm_arn}")" - if [[ -z "${arn_account}" ]]; then - return 0 - fi - if [[ "${arn_account}" != "${caller_account}" ]]; then - echo "ACM ARN account mismatch." >&2 - echo "caller account: ${caller_account}" >&2 - echo "acm arn account: ${arn_account}" >&2 - return 1 - fi -} - -# 스택 Output에서 특정 키 값을 조회한다. -get_stack_output() { - local stack_name="$1" - local output_key="$2" - local region="$3" - local value - - value="$( - aws cloudformation describe-stacks \ - --stack-name "${stack_name}" \ - --region "${region}" \ - --query "Stacks[0].Outputs[?OutputKey=='${output_key}'].OutputValue | [0]" \ - --output text 2>/dev/null || true - )" - - if [[ "${value}" == "None" ]]; then - value="" - fi - printf '%s' "${value}" -} - -# 버킷이 비어있지 않아 스택 삭제가 막히지 않도록 객체를 먼저 비운다. -empty_bucket_if_present() { - local bucket_name="$1" - local region="$2" - if [[ -z "${bucket_name}" ]]; then - return 0 - fi - echo "Empty S3 bucket: s3://${bucket_name}" - aws s3 rm "s3://${bucket_name}" --recursive --region "${region}" >/dev/null 2>&1 || true -} - -# 스택을 삭제하고, 버킷 이슈가 나면 한 번 더 정리 후 재시도한다. -delete_stack_with_cleanup() { - local stack_name="$1" - local region="$2" - - if stack_exists "${stack_name}" "${region}"; then - : - else - local exists_status=$? - if [[ ${exists_status} -eq 1 ]]; then - echo "Skip: stack not found (${stack_name}) [region=${region}, profile=${AWS_PROFILE:-default}]" - return 0 - fi - return 1 - fi - - local deploy_bucket - local website_bucket - deploy_bucket="$(get_stack_output "${stack_name}" "ServerlessDeploymentBucketName" "${region}")" - website_bucket="$(get_stack_output "${stack_name}" "WebsiteBucketName" "${region}")" - - empty_bucket_if_present "${deploy_bucket}" "${region}" - empty_bucket_if_present "${website_bucket}" "${region}" - - echo "Delete stack: ${stack_name}" - aws cloudformation delete-stack --stack-name "${stack_name}" --region "${region}" || true - if aws cloudformation wait stack-delete-complete --stack-name "${stack_name}" --region "${region}"; then - echo "Deleted: ${stack_name}" - return 0 - fi - - echo "Retry stack deletion after bucket cleanup: ${stack_name}" - empty_bucket_if_present "${deploy_bucket}" "${region}" - empty_bucket_if_present "${website_bucket}" "${region}" - aws cloudformation delete-stack --stack-name "${stack_name}" --region "${region}" || true - if aws cloudformation wait stack-delete-complete --stack-name "${stack_name}" --region "${region}"; then - echo "Deleted: ${stack_name}" - return 0 - fi - - if stack_exists "${stack_name}" "${region}"; then - : - else - local exists_status=$? - if [[ ${exists_status} -eq 1 ]]; then - echo "Deleted: ${stack_name}" - return 0 - fi - return 1 - fi - - echo "Failed to delete stack: ${stack_name}" - aws cloudformation describe-stack-events \ - --stack-name "${stack_name}" \ - --region "${region}" \ - --max-items 20 \ - --query 'StackEvents[].[Timestamp,LogicalResourceId,ResourceStatus,ResourceStatusReason]' \ - --output table || true - return 1 -} - -# 설정에 따라 ACM 인증서를 제거하거나 유지한다. -delete_acm_if_requested() { - local acm_arn="$1" - local acm_region - local describe_output - if [[ "${REMOVE_ACM}" != "true" ]]; then - echo "Skip ACM deletion (--keep-acm set)" - return 0 - fi - if [[ -z "${acm_arn}" ]]; then - echo "ACM ARN is required for ACM deletion." - echo "Use --acm-arn or set ACM_CERTIFICATE_ARN in env." - return 1 - fi - acm_region="$(extract_region_from_acm_arn "${acm_arn}")" - if [[ -z "${acm_region}" ]]; then - acm_region="us-east-1" - echo "Warning: failed to parse ACM region from ARN. Fallback region=${acm_region}" - fi - - if describe_output="$( - aws acm describe-certificate \ - --region "${acm_region}" \ - --certificate-arn "${acm_arn}" 2>&1 - )"; then - : - else - if printf '%s' "${describe_output}" | grep -qi 'ResourceNotFoundException'; then - echo "Skip ACM deletion: certificate not found (region=${acm_region})" - return 0 - fi - echo "Failed to query ACM certificate before deletion." >&2 - echo "AWS CLI error: ${describe_output}" >&2 - return 1 - fi - - echo "Delete ACM certificate (${acm_region}): ${acm_arn}" - aws acm delete-certificate --region "${acm_region}" --certificate-arn "${acm_arn}" - echo "Deleted ACM certificate: ${acm_arn}" -} - -# 메인 실행 흐름: -# [1] API 스택 제거 -> [2] Web 스택 제거 -> [3] ACM 제거(기본) -main() { - parse_args "$@" - deploy_validate_stage "${STAGE}" - deploy_load_stage_env "${ROOT_DIR}" "${STAGE}" - - assert_aws_access - - local region="${AWS_REGION:-ap-northeast-2}" - local api_stack_name="${API_STACK_NAME:-${STAGE}-my-commit-api}" - local web_stack_name="${WEB_STACK_NAME:-${STAGE}-my-commit-web}" - local acm_arn="${ACM_ARN_ARG:-${ACM_CERTIFICATE_ARN:-}}" - local caller_account - caller_account="$(aws sts get-caller-identity --query 'Account' --output text 2>/dev/null || true)" - - print_aws_context - validate_acm_account_alignment "${acm_arn}" "${caller_account}" - - echo "[1/3] Remove API stack" - delete_stack_with_cleanup "${api_stack_name}" "${region}" - - echo "[2/3] Remove Web stack" - delete_stack_with_cleanup "${web_stack_name}" "${region}" - - echo "[3/3] Remove ACM (default)" - delete_acm_if_requested "${acm_arn}" - - echo "Reset complete for stage=${STAGE}" -} - -main "$@" diff --git a/scripts/sync-dns-after-deploy.sh b/scripts/sync-dns-after-deploy.sh deleted file mode 100644 index 8726c83..0000000 --- a/scripts/sync-dns-after-deploy.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash -# 역할: 배포 완료 후 UPDATE_DNS_RECORD/DNS_PROVIDER 조건에 따라 DNS 동기화를 수행. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" - -# 문자열을 소문자로 정규화한다. -to_lower() { - printf '%s' "$1" | tr '[:upper:]' '[:lower:]' -} - -# 다양한 true/false 표현을 표준 불리언 문자열로 변환한다. -parse_bool() { - local value - value="$(to_lower "${1:-}")" - case "${value}" in - 1 | true | yes | on) - echo "true" - ;; - 0 | false | no | off | "") - echo "false" - ;; - *) - echo "invalid" - ;; - esac -} - -# [1] UPDATE_DNS_RECORD 플래그 검증/판정 -UPDATE_DNS_RECORD_ENABLED="$(parse_bool "${UPDATE_DNS_RECORD:-false}")" -if [[ "${UPDATE_DNS_RECORD_ENABLED}" == "invalid" ]]; then - echo "Invalid UPDATE_DNS_RECORD value: ${UPDATE_DNS_RECORD:-}" - echo "Allowed: true/false" - exit 1 -fi - -if [[ "${UPDATE_DNS_RECORD_ENABLED}" != "true" ]]; then - echo "Skip DNS sync (UPDATE_DNS_RECORD=false)" - exit 0 -fi - -# [2] DNS provider 검증 -DNS_PROVIDER_NORMALIZED="$(to_lower "${DNS_PROVIDER:-none}")" -if [[ "${DNS_PROVIDER_NORMALIZED}" != "porkbun" ]]; then - if [[ "${DNS_PROVIDER_NORMALIZED}" == "none" ]]; then - echo "UPDATE_DNS_RECORD=true but DNS_PROVIDER=none" - echo "Set DNS_PROVIDER to a supported provider (e.g. porkbun)." - else - echo "Invalid dns provider: ${DNS_PROVIDER:-}" - echo "Allowed: porkbun" - fi - exit 1 -fi - -REGION="${AWS_REGION:-ap-northeast-2}" -WEB_STACK_NAME="${WEB_STACK_NAME:-}" -WEB_DOMAIN_NAME="${WEB_DOMAIN_NAME:-}" - -# [3] 실행 입력값 검증 -if [[ -z "${WEB_STACK_NAME}" ]]; then - echo "WEB_STACK_NAME is required for DNS sync" - exit 1 -fi - -if [[ -z "${WEB_DOMAIN_NAME}" ]]; then - echo "WEB_DOMAIN_NAME is required for DNS sync" - exit 1 -fi - -# [4] Web 스택에서 CloudFront URL 조회 -WEB_CLOUDFRONT_URL="$( - aws cloudformation describe-stacks \ - --stack-name "${WEB_STACK_NAME}" \ - --region "${REGION}" \ - --query 'Stacks[0].Outputs[?OutputKey==`CloudFrontURL`].OutputValue' \ - --output text -)" - -if [[ -z "${WEB_CLOUDFRONT_URL}" || "${WEB_CLOUDFRONT_URL}" == "None" ]]; then - echo "Failed to resolve CloudFrontURL from ${WEB_STACK_NAME}" - exit 1 -fi - -# [5] DNS provider 실행기 호출 -echo "Sync DNS via ${DNS_PROVIDER_NORMALIZED}: ${WEB_DOMAIN_NAME} -> ${WEB_CLOUDFRONT_URL}" -DNS_PROVIDER="${DNS_PROVIDER_NORMALIZED}" \ -DNS_RECORD_NAME="${WEB_DOMAIN_NAME}" \ -DNS_RECORD_CONTENT="${WEB_CLOUDFRONT_URL}" \ -node "${ROOT_DIR}/scripts/dns-sync.mjs" diff --git a/scripts/sync-dns.sh b/scripts/sync-dns.sh deleted file mode 100644 index f28bd54..0000000 --- a/scripts/sync-dns.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash -# 역할: 이미 배포된 Web 스택 Output(CloudFrontURL)을 읽어 DNS(Porkbun)만 동기화한다. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" -source "${ROOT_DIR}/scripts/lib/deploy-common.sh" - -STAGE="prod" -DNS_PROVIDER_ARG="" -SHOW_HELP="false" - -# sync-dns.sh 사용법/예시를 출력한다. -usage() { - cat <<'EOF' -Usage: sync-dns.sh [prod|dev] --dns-provider - -Examples: - sync-dns.sh prod --dns-provider porkbun - sync-dns.sh dev --dns-provider porkbun -EOF -} - -# CLI 인자를 파싱한다. -parse_args() { - local stage_arg_count=0 - - if [[ "${1:-}" == "--" ]]; then - shift - fi - - while [[ $# -gt 0 ]]; do - case "$1" in - --) - shift - ;; - prod | dev) - STAGE="$1" - stage_arg_count=$((stage_arg_count + 1)) - if [[ ${stage_arg_count} -gt 1 ]]; then - echo "Stage can only be specified once." - usage - exit 1 - fi - shift - ;; - --dns-provider) - DNS_PROVIDER_ARG="${2:-}" - if [[ -z "${DNS_PROVIDER_ARG}" ]]; then - echo "Missing value for --dns-provider" - usage - exit 1 - fi - shift 2 - ;; - --dns-provider=*) - DNS_PROVIDER_ARG="${1#*=}" - shift - ;; - -h | --help) - SHOW_HELP="true" - shift - ;; - *) - echo "Unknown argument: $1" - usage - exit 1 - ;; - esac - done -} - -# 메인 실행 흐름: -# [1] stage env 로드 -> [2] CloudFront URL 기반 DNS 동기화 실행 -main() { - parse_args "$@" - if [[ "${SHOW_HELP}" == "true" ]]; then - usage - exit 0 - fi - - deploy_validate_stage "${STAGE}" - deploy_load_stage_env "${ROOT_DIR}" "${STAGE}" - - deploy_require_env "WEB_DOMAIN_NAME" - - local region="${AWS_REGION:-ap-northeast-2}" - local web_stack_name="${WEB_STACK_NAME:-${STAGE}-my-commit-web}" - local dns_provider="${DNS_PROVIDER_ARG:-}" - - if [[ -z "${dns_provider}" ]]; then - echo "DNS provider is required." - echo "Pass --dns-provider ." - exit 1 - fi - - echo "Sync DNS only: stage=${STAGE} provider=${dns_provider}" - UPDATE_DNS_RECORD=true \ - DNS_PROVIDER="${dns_provider}" \ - WEB_DOMAIN_NAME="${WEB_DOMAIN_NAME}" \ - WEB_STACK_NAME="${web_stack_name}" \ - AWS_REGION="${region}" \ - bash "${ROOT_DIR}/scripts/sync-dns-after-deploy.sh" -} - -main "$@"