diff --git a/.claude/commands/bmad/bmm/agents/architect.md b/.claude/commands/bmad/bmm/agents/architect.md index 8db5959..1314c59 100644 --- a/.claude/commands/bmad/bmm/agents/architect.md +++ b/.claude/commands/bmad/bmm/agents/architect.md @@ -34,6 +34,13 @@ You must fully embody this agent's persona and follow all activation instruction 4. Execute workflow.xml instructions precisely following all steps 5. Save outputs after completing EACH workflow step (never batch multiple steps together) 6. If workflow.yaml path is "todo", inform user the workflow hasn't been implemented yet + + + When command has: validate-workflow="path/to/workflow.yaml" + 1. You MUST LOAD the file at: {project-root}/bmad/core/tasks/validate-workflow.xml + 2. READ its entire contents and EXECUTE all instructions in that file + 3. Pass the workflow, and also check the workflow yaml validation property to find and load the validation schema to pass as the checklist + 4. The workflow should try to identify the file to validate based on checklist context or else you will ask the user to specify @@ -58,6 +65,7 @@ You must fully embody this agent's persona and follow all activation instruction Check workflow status and get recommendations Course Correction Analysis Produce a Scale Adaptive Architecture + Validate Architecture Document Validate solutioning complete, ready for Phase 4 (Level 2-4 only) Exit with confirmation diff --git a/.claude/commands/bmad/bmm/agents/pm.md b/.claude/commands/bmad/bmm/agents/pm.md index f9368c5..770b62b 100644 --- a/.claude/commands/bmad/bmm/agents/pm.md +++ b/.claude/commands/bmad/bmm/agents/pm.md @@ -35,12 +35,13 @@ You must fully embody this agent's persona and follow all activation instruction 5. Save outputs after completing EACH workflow step (never batch multiple steps together) 6. If workflow.yaml path is "todo", inform user the workflow hasn't been implemented yet - - When menu item has: exec="path/to/file.md" - Actually LOAD and EXECUTE the file at that path - do not improvise - Read the complete file and follow all instructions within it - - + + When command has: validate-workflow="path/to/workflow.yaml" + 1. You MUST LOAD the file at: {project-root}/bmad/core/tasks/validate-workflow.xml + 2. READ its entire contents and EXECUTE all instructions in that file + 3. Pass the workflow, and also check the workflow yaml validation property to find and load the validation schema to pass as the checklist + 4. The workflow should try to identify the file to validate based on checklist context or else you will ask the user to specify + @@ -65,8 +66,8 @@ You must fully embody this agent's persona and follow all activation instruction Check workflow status and get recommendations (START HERE!) Create Product Requirements Document (PRD) for Level 2-4 projects Create Tech Spec for Level 0-1 (sometimes Level 2) projects + Validate Technical Specification Document Course Correction Analysis - Validate any document against its workflow checklist Exit with confirmation diff --git a/bmad/bmm/agents/ux-expert.md b/.claude/commands/bmad/bmm/agents/ux-designer.md similarity index 80% rename from bmad/bmm/agents/ux-expert.md rename to .claude/commands/bmad/bmm/agents/ux-designer.md index 4052e49..1da55bd 100644 --- a/bmad/bmm/agents/ux-expert.md +++ b/.claude/commands/bmad/bmm/agents/ux-designer.md @@ -1,12 +1,12 @@ --- -name: "ux expert" -description: "UX Expert" +name: "ux designer" +description: "UX Designer" --- You must fully embody this agent's persona and follow all activation instructions exactly as specified. NEVER break character until given an exit command. ```xml - + Load persona from this current agent file (already in context) 🚨 IMMEDIATE ACTION REQUIRED - BEFORE ANY OUTPUT: @@ -34,6 +34,13 @@ You must fully embody this agent's persona and follow all activation instruction 4. Execute workflow.xml instructions precisely following all steps 5. Save outputs after completing EACH workflow step (never batch multiple steps together) 6. If workflow.yaml path is "todo", inform user the workflow hasn't been implemented yet + + + When command has: validate-workflow="path/to/workflow.yaml" + 1. You MUST LOAD the file at: {project-root}/bmad/core/tasks/validate-workflow.xml + 2. READ its entire contents and EXECUTE all instructions in that file + 3. Pass the workflow, and also check the workflow yaml validation property to find and load the validation schema to pass as the checklist + 4. The workflow should try to identify the file to validate based on checklist context or else you will ask the user to specify @@ -56,7 +63,8 @@ You must fully embody this agent's persona and follow all activation instruction Show numbered menu Check workflow status and get recommendations (START HERE!) - Create UX/UI Specification and AI Frontend Prompts + Conduct Design Thinking Workshop to Define the User Specification + Validate UX Specification and Design Artifacts Exit with confirmation diff --git a/.claude/commands/bmad/bmm/workflows/README.md b/.claude/commands/bmad/bmm/workflows/README.md index 5a299c2..cc985d3 100644 --- a/.claude/commands/bmad/bmm/workflows/README.md +++ b/.claude/commands/bmad/bmm/workflows/README.md @@ -26,6 +26,10 @@ - Path: `bmad/bmm/workflows/1-analysis/research/workflow.yaml` - Adaptive research workflow supporting multiple research types: market research, deep research prompt generation, technical/architecture evaluation, competitive intelligence, user research, and domain analysis +**create-ux-design** +- Path: `bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml` +- Collaborative UX design facilitation workflow that creates exceptional user experiences through visual exploration and informed decision-making. Unlike template-driven approaches, this workflow facilitates discovery, generates visual options, and collaboratively designs the UX with the user at every step. + **gdd** - Path: `bmad/bmm/workflows/2-plan-workflows/gdd/workflow.yaml` - Game Design Document workflow for all game project levels - from small prototypes to full AAA games. Generates comprehensive GDD with game mechanics, systems, progression, and implementation guidance. @@ -42,10 +46,6 @@ - Path: `bmad/bmm/workflows/2-plan-workflows/tech-spec/workflow.yaml` - Technical specification workflow for Level 0 projects (single atomic changes). Creates focused tech spec for bug fixes, single endpoint additions, or small isolated changes. Tech-spec only - no PRD needed. -**ux-spec** -- Path: `bmad/bmm/workflows/2-plan-workflows/ux/workflow.yaml` -- UX/UI specification workflow for defining user experience and interface design. Creates comprehensive UX documentation including wireframes, user flows, component specifications, and design system guidelines. - **architecture** - Path: `bmad/bmm/workflows/3-solutioning/architecture/workflow.yaml` - Collaborative architectural decision facilitation for AI-agent consistency. Replaces template-driven architecture with intelligent, adaptive conversation that produces a decision-focused architecture document optimized for preventing agent conflicts. @@ -94,10 +94,6 @@ - Path: `bmad/bmm/workflows/4-implementation/story-ready/workflow.yaml` - Marks a drafted story as ready for development and moves it from TODO → IN PROGRESS in the status file. Simple status-update workflow with no searching required. -**sprint-status** -- Path: `bmad/bmm/workflows/helpers/sprint-status/workflow.yaml` -- Helper workflow for reading and updating sprint-status.yaml tracking file. Provides query and update operations for Phase 4 implementation workflows. - **testarch-atdd** - Path: `bmad/bmm/workflows/testarch/atdd/workflow.yaml` - Generate failing acceptance tests before implementation using TDD red-green-refactor cycle diff --git a/.claude/commands/bmad/bmm/workflows/create-ux-design.md b/.claude/commands/bmad/bmm/workflows/create-ux-design.md new file mode 100644 index 0000000..e0e5eb3 --- /dev/null +++ b/.claude/commands/bmad/bmm/workflows/create-ux-design.md @@ -0,0 +1,11 @@ +# create-ux-design + +IT IS CRITICAL THAT YOU FOLLOW THESE STEPS - while staying in character as the current agent persona you may have loaded: + + +1. Always LOAD the FULL {project-root}/bmad/core/tasks/workflow.xml +2. READ its entire contents - this is the CORE OS for EXECUTING the specific workflow-config bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml +3. Pass the yaml path bmad/bmm/workflows/2-plan-workflows/create-ux-design/workflow.yaml as 'workflow-config' parameter to the workflow.xml instructions +4. Follow workflow.xml instructions EXACTLY as written +5. Save outputs after EACH section when generating any documents from templates + diff --git a/.gemini/commands/agents/ux-designer.toml b/.gemini/commands/agents/ux-designer.toml new file mode 100644 index 0000000..c3c2eba --- /dev/null +++ b/.gemini/commands/agents/ux-designer.toml @@ -0,0 +1,8 @@ +description = "Activates the UX Designer agent from the BMad Method." +prompt = """ +CRITICAL: You are now the BMad 'UX Designer' agent. Adopt its persona and capabilities as defined in the following configuration. + +Read and internalize the full agent definition, following all instructions and maintaining this persona until explicitly told to switch or exit. + +@../code/cs/bmad/bmm/agents/ux-designer.md +""" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c9cc6ab --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,17 @@ +## Summary + +- CI: adds Quick Check gate to lint/typecheck only changed packages before heavy jobs. +- Developer perf runbook added. + +## Developer Performance + +- See docs/runbooks/developer-performance.md for: + - pnpm quick-check (fast lint/typecheck on changed packages) + - pnpm build:quick (low-concurrency build on changed packages) + - Local resource limit tips (TURBO_CONCURRENCY, NODE_OPTIONS) + +## Validation Notes + +- Quick Check passes locally: `pnpm quick-check`. +- Lint/typecheck warnings only; no blocking errors expected. + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5559214..cf8c1fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,13 +19,72 @@ concurrency: # Environment variables env: NODE_VERSION: "20" - PNPM_VERSION: "9" + PNPM_VERSION: "10.18.2" jobs: + # Fast gate to block heavy jobs early on PRs + quick-check: + name: Quick Check (Changed Packages) + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + env: + TURBO_TELEMETRY_DISABLED: '1' + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Ensure origin/main is available + shell: bash + run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/main:refs/remotes/origin/main + git rev-parse --abbrev-ref HEAD + git rev-parse origin/main + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Setup Turbo cache + uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}-turbo-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/turbo.json') }} + restore-keys: | + ${{ runner.os }}-turbo- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run lint+typecheck only for changed packages + run: | + echo "Running quick checks for changed packages vs origin/main" + npx turbo run lint typecheck --filter=...[origin/main] # Lint and typecheck job lint: name: Lint & Typecheck runs-on: ubuntu-latest + needs: [quick-check] steps: - name: Checkout code @@ -75,6 +134,7 @@ jobs: test: name: Unit Tests runs-on: ubuntu-latest + needs: [quick-check] strategy: matrix: @@ -149,7 +209,7 @@ jobs: build: name: Build Verification runs-on: ubuntu-latest - needs: [lint] + needs: [quick-check, lint] steps: - name: Checkout code @@ -279,22 +339,160 @@ jobs: id: deploy env: VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_STAGING_PROJECT_ID }} + VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} + VERCEL_STAGING_PROJECT_ID: ${{ secrets.VERCEL_STAGING_PROJECT_ID }} + VERCEL_STAGING_PROJECT_NAME: ${{ secrets.VERCEL_STAGING_PROJECT_NAME }} run: | echo "🚀 Deploying preview to Vercel..." - # Deploy using Vercel CLI with prebuilt artifacts - vercel_output=$(npx vercel deploy --prebuilt --json --team ${{ secrets.VERCEL_TEAM_ID }} 2>/dev/null || echo '{}') + # Gracefully skip if token is missing + if [[ -z "${VERCEL_TOKEN:-}" ]]; then + echo "â„šī¸ Skipping Vercel preview: VERCEL_TOKEN not set" + echo "url=" >> $GITHUB_OUTPUT + echo "deployment_id=" >> $GITHUB_OUTPUT + exit 0 + fi - # Parse deployment response - preview_url=$(echo "$vercel_output" | jq -r '.url // empty') - deployment_id=$(echo "$vercel_output" | jq -r '.id // empty') + TEAM_ARG="" + if [[ -n "${VERCEL_TEAM_ID:-}" ]]; then + TEAM_ARG="--scope ${VERCEL_TEAM_ID}" + elif [[ -f apps/storefront/.vercel/project.json ]]; then + ORG_ID=$(jq -r '.orgId // empty' apps/storefront/.vercel/project.json 2>/dev/null || true) + if [[ -n "$ORG_ID" ]]; then + echo "âš ī¸ Derived orgId '$ORG_ID' does not include a team slug; skipping --scope auto-fill" + fi + fi - # Validate deployment succeeded - if [[ -z "$preview_url" ]]; then - echo "❌ Preview deployment failed - no URL returned" - exit 1 + # Ensure local Vercel project settings are available for CLI commands + npx vercel pull --cwd apps/storefront --yes --environment=preview ${TEAM_ARG} --token "${VERCEL_TOKEN}" + + node <<'PATCH' + const fs = require('fs'); + const path = 'apps/storefront/.vercel/project.json'; + const data = JSON.parse(fs.readFileSync(path, 'utf8')); + data.settings = data.settings || {}; + data.settings.framework = data.settings.framework || 'nextjs'; + data.settings.nodeVersion = data.settings.nodeVersion || '20.x'; + data.settings.installCommand = 'bash ./vercel-install.sh'; + data.settings.buildCommand = 'pnpm run build'; + data.settings.outputDirectory = data.settings.outputDirectory || '.vercel/output'; + fs.writeFileSync(path, JSON.stringify(data, null, 2)); + PATCH + + # Build prebuilt output for deterministic preview deploys (produces .vercel/output) + # If this fails, we will fallback to non-prebuilt deploy below. + (npx vercel build --cwd apps/storefront ${TEAM_ARG} --token "${VERCEL_TOKEN}" --yes) || echo "vercel build failed; will try non-prebuilt deploy if needed" + + extract_preview_url() { + echo "$1" | grep -Eo 'https://[a-zA-Z0-9.-]+\.vercel\.app' | tail -n 1 + } + + extract_deployment_id() { + echo "$1" | grep -Eo 'dpl_[A-Za-z0-9]{10,}' | tail -n 1 + } + + extract_error_message() { + echo "$1" | grep -E '^(Error|Error!|Error:)' | tail -n 1 | sed -E 's/^(Error!?[: ]*)//' + } + + fetch_latest_preview() { + local project_id="$1" + local token="$2" + curl -sSf --retry 3 --retry-delay 2 \ + -H "Authorization: Bearer ${token}" \ + "https://api.vercel.com/v13/deployments?projectId=${project_id}&target=preview&limit=1" + } + + resolve_project_id() { + if [[ -n "${VERCEL_STAGING_PROJECT_ID:-}" ]]; then + echo "${VERCEL_STAGING_PROJECT_ID}" + else + jq -r '.projectId // empty' apps/storefront/.vercel/project.json 2>/dev/null || true + fi + } + + tmpfile_prebuilt=$(mktemp) + if (npx vercel deploy --cwd apps/storefront --prebuilt --yes --confirm ${TEAM_ARG} --token "${VERCEL_TOKEN}") >"$tmpfile_prebuilt" 2>&1; then + vercel_exit_code=0 + else + vercel_exit_code=$? + fi + vercel_output=$(cat "$tmpfile_prebuilt") + rm -f "$tmpfile_prebuilt" + + preview_url=$(extract_preview_url "$vercel_output") + deployment_id=$(extract_deployment_id "$vercel_output") + + if [[ -z "$preview_url" || -z "$deployment_id" ]]; then + project_id=$(resolve_project_id) + if [[ -n "$project_id" ]]; then + latest_json=$(fetch_latest_preview "$project_id" "${VERCEL_TOKEN}") || true + if [[ -n "$latest_json" ]]; then + if [[ -z "$deployment_id" ]]; then + deployment_id=$(echo "$latest_json" | jq -r '.deployments[0].id // empty' 2>/dev/null || true) + fi + if [[ -z "$preview_url" ]]; then + latest_url=$(echo "$latest_json" | jq -r '.deployments[0].url // empty' 2>/dev/null || true) + if [[ -n "$latest_url" ]]; then + preview_url="https://${latest_url}" + fi + fi + fi + fi + fi + + # Fallback to non-prebuilt deploy when necessary + if [[ -z "$preview_url" || -z "$deployment_id" || $vercel_exit_code -ne 0 ]]; then + echo "â„šī¸ Prebuilt deploy did not complete successfully; attempting fallback to non-prebuilt deploy" + tmpfile_fallback=$(mktemp) + if (npx vercel deploy --cwd apps/storefront --yes --confirm ${TEAM_ARG} --token "${VERCEL_TOKEN}") >"$tmpfile_fallback" 2>&1; then + vercel_exit_code=0 + else + vercel_exit_code=$? + fi + vercel_output=$(cat "$tmpfile_fallback") + rm -f "$tmpfile_fallback" + + preview_url=$(extract_preview_url "$vercel_output") + deployment_id=$(extract_deployment_id "$vercel_output") + + if [[ -z "$preview_url" || -z "$deployment_id" ]]; then + project_id=${project_id:-$(resolve_project_id)} + if [[ -n "$project_id" ]]; then + latest_json=$(fetch_latest_preview "$project_id" "${VERCEL_TOKEN}") || true + if [[ -n "$latest_json" ]]; then + if [[ -z "$deployment_id" ]]; then + deployment_id=$(echo "$latest_json" | jq -r '.deployments[0].id // empty' 2>/dev/null || true) + fi + if [[ -z "$preview_url" ]]; then + latest_url=$(echo "$latest_json" | jq -r '.deployments[0].url // empty' 2>/dev/null || true) + if [[ -n "$latest_url" ]]; then + preview_url="https://${latest_url}" + fi + fi + fi + fi + fi + + if [[ -z "$preview_url" || -z "$deployment_id" || $vercel_exit_code -ne 0 ]]; then + echo "❌ Preview deployment failed - missing URL or deployment id" + echo "Debug output:"; echo "$vercel_output" + vercel_error=$(extract_error_message "$vercel_output") + if [[ -z "$vercel_error" ]]; then + vercel_error=$(echo "$vercel_output" | grep -E 'Error|ERROR|error|failed|FAILED|Failure' | tail -n 1 | tr -d '\r') + fi + if [[ -z "$vercel_error" && -n "${latest_json:-}" ]]; then + vercel_error=$(echo "$latest_json" | jq -r '.error.message // .error.code // empty' 2>/dev/null || true) + fi + if [[ -z "$vercel_error" ]]; then + vercel_error=$(echo "$vercel_output" | tail -n 40 | tr -d '\r' | tr '\n' '|' | sed 's/|/ | /g' | sed 's/[[:space:]]\+/ /g' | cut -c 1-500) + if [[ -z "$vercel_error" ]]; then + vercel_error="Unknown error (see logs)" + fi + fi + echo "::error file=preview-deploy.sh::Vercel deploy failed: $vercel_error" + exit 1 + fi fi echo "✅ Preview deployed successfully!" @@ -384,7 +582,7 @@ jobs: preview-smoke-tests: name: Preview Smoke Tests runs-on: ubuntu-latest - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && needs.preview.outputs.url != '' needs: preview steps: diff --git a/.github/workflows/deploy-services.yml b/.github/workflows/deploy-services.yml index 354d4dc..bd4ae72 100644 --- a/.github/workflows/deploy-services.yml +++ b/.github/workflows/deploy-services.yml @@ -4,6 +4,17 @@ on: push: branches: [main, staging] workflow_dispatch: + inputs: + first_init: + description: "Run GCE first-init script (dangerous: edits PG/Redis/firewall)" + required: false + default: "false" + type: boolean + db_password: + description: "Optional initial PostgreSQL password for role 'cs'" + required: false + default: "" + type: string permissions: contents: read @@ -13,9 +24,14 @@ permissions: env: REGISTRY: ghcr.io IMAGE_PREFIX: ${{ github.repository_owner }}/cs + RUN_FIRST_INIT: ${{ vars.GCE_FIRST_INIT || '' }} jobs: build-and-deploy: + outputs: + environment: ${{ steps.env.outputs.environment }} + image_tag: ${{ steps.env.outputs.image_tag }} + channel_tag: ${{ steps.env.outputs.channel_tag }} runs-on: ubuntu-latest concurrency: group: deploy-services-${{ github.ref }} @@ -107,13 +123,37 @@ jobs: key: ${{ secrets.GCE_SSH_KEY }} # Provide sources as a single comma-separated string (plugin supports comma-separated list) # Also switch to explicit relative roots to avoid path resolution quirks - source: "./scripts/gce/deploy.sh,./infra/gcp/bin/collect-health.sh" + source: "./scripts/gce/deploy.sh,./scripts/gce/first-init.sh,./infra/gcp/bin/collect-health.sh" target: /tmp/cs-deploy overwrite: true + - name: First-time host init (optional) + if: ${{ (github.event_name == 'workflow_dispatch' && inputs.first_init == true) || env.RUN_FIRST_INIT == 'true' }} + uses: appleboy/ssh-action@v1.0.3 + env: + DB_PW: ${{ github.event_name == 'workflow_dispatch' && inputs.db_password != '' && inputs.first_init && inputs.db_password || secrets.GCE_DB_PASSWORD || '' }} + with: + host: ${{ secrets.GCE_HOST }} + username: ${{ secrets.GCE_USER }} + key: ${{ secrets.GCE_SSH_KEY }} + script: | + set -euo pipefail + # Run first-init with optional DB password when provided + if [ -n "${DB_PW:-}" ]; then + sudo bash /tmp/cs-deploy/scripts/gce/first-init.sh --db-password "$DB_PW" + else + sudo bash /tmp/cs-deploy/scripts/gce/first-init.sh + fi + # Keep scripts for later steps + mkdir -p /srv/cs/bin + install -m 750 /tmp/cs-deploy/scripts/gce/first-init.sh /srv/cs/bin/first-init.sh + - name: Deploy services on GCE id: deploy uses: appleboy/ssh-action@v1.0.3 + env: + GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }} + GHCR_PAT: ${{ secrets.GHCR_PAT }} with: host: ${{ secrets.GCE_HOST }} username: ${{ secrets.GCE_USER }} @@ -126,8 +166,12 @@ jobs: install -m 750 /tmp/cs-deploy/scripts/gce/deploy.sh /srv/cs/bin/deploy.sh install -m 750 /tmp/cs-deploy/infra/gcp/bin/collect-health.sh /srv/cs/bin/collect-health.sh rm -rf /tmp/cs-deploy - # Authenticate Docker to GHCR on the host so compose pull can fetch private packages if needed - echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin >/dev/null 2>&1 || true + # Authenticate Docker to GHCR on the host for compose pull (PAT preferred, fallback to GITHUB_TOKEN) + if [ -n "${GHCR_PAT:-}" ]; then + echo "${GHCR_PAT}" | docker login ghcr.io -u "${GHCR_USERNAME:-${{ github.actor }}}" --password-stdin >/dev/null 2>&1 + else + echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin >/dev/null 2>&1 + fi TAG=${{ steps.env.outputs.image_tag }} /srv/cs/bin/deploy.sh --tag "$TAG" --cwd /srv/cs tail -n 20 /srv/cs/logs/deploy-*.log | sed "s/^/[deploy-log] /" @@ -146,3 +190,43 @@ jobs: Remember to record the run in docs/runbooks/status-log.md with tunnel checks and curl outputs. SUMMARY + + smoke-checks: + name: Post-deploy Smoke Checks + runs-on: ubuntu-latest + needs: build-and-deploy + if: needs.build-and-deploy.outputs.environment == 'production' + + steps: + - name: Curl health endpoints with retries + shell: bash + run: | + set -euo pipefail + api_url="https://api.aidenlux.com/health" + cms_url="https://content.aidenlux.com/_health" + for url in "$api_url" "$cms_url"; do + echo "Checking $url" + ok=0 + for i in {1..30}; do + code=$(curl -s -o /dev/null -w "%{http_code}" "$url" || echo "000") + if [ "$url" = "$cms_url" ]; then + if [ "$code" = "200" ] || [ "$code" = "204" ]; then ok=1; break; fi + else + if [ "$code" = "200" ]; then ok=1; break; fi + fi + echo "Attempt $i/30: $url -> HTTP $code; retrying in 5s..." + sleep 5 + done + if [ $ok -ne 1 ]; then + echo "❌ Health check failed for $url" >&2 + exit 1 + else + echo "✅ $url healthy" + fi + done + + - name: Add job summary + run: | + echo "## Post-deploy Smoke Checks" >> $GITHUB_STEP_SUMMARY + echo "- Checked https://api.aidenlux.com/health (200 required)" >> $GITHUB_STEP_SUMMARY + echo "- Checked https://content.aidenlux.com/_health (200/204 accepted)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index a2d7fbe..4d4aeae 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -43,9 +43,9 @@ jobs: cache: 'pnpm' - name: Install pnpm - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v4 with: - version: 8 + version: 10.18.2 - name: Install dependencies run: pnpm install --frozen-lockfile @@ -96,6 +96,17 @@ jobs: run: | echo "🚀 Deploying to ${{ steps.env.outputs.environment }}..." + # Ensure correct Vercel linkage for production deployments when CLI is used + if [[ "${{ steps.env.outputs.environment }}" == "production" ]]; then + mkdir -p apps/storefront/.vercel + if [[ -f apps/storefront/.vercel/project.production.json ]]; then + cp apps/storefront/.vercel/project.production.json apps/storefront/.vercel/project.json + echo "✅ Applied production Vercel linkage (apps/storefront/.vercel/project.json)" + else + echo "â„šī¸ apps/storefront/.vercel/project.production.json not found; relying on explicit project args or Pulumi outputs" + fi + fi + # Use the deployment script if [[ "${{ steps.env.outputs.environment }}" == "production" ]]; then pnpm deploy:production main diff --git a/.github/workflows/sync-vercel-env.yml b/.github/workflows/sync-vercel-env.yml new file mode 100644 index 0000000..94b2462 --- /dev/null +++ b/.github/workflows/sync-vercel-env.yml @@ -0,0 +1,111 @@ +name: Sync Vercel Env + +on: + workflow_dispatch: + inputs: + scope: + description: 'Which projects to sync' + required: true + default: 'both' + type: choice + options: ['both','production','staging'] + medusa_url: + description: 'Medusa API base URL' + required: true + default: 'https://api.aidenlux.com' + medusa_pk: + description: 'Publishable API key (leave blank to use MEDUSA_PUBLISHABLE_KEY secret)' + required: false + default: '' + strapi_url: + description: 'Strapi base URL (optional)' + required: false + default: 'https://content.aidenlux.com' + nextauth_prod: + description: 'NEXTAUTH_URL for production project' + required: true + default: 'https://prd.aidenlux.com' + nextauth_staging: + description: 'NEXTAUTH_URL for staging project' + required: true + default: 'https://staging.aidenlux.com' + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Ensure jq is installed + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Resolve inputs and secrets + id: vars + shell: bash + env: + IN_SCOPE: ${{ github.event.inputs.scope }} + IN_MEDUSA_URL: ${{ github.event.inputs.medusa_url }} + IN_MEDUSA_PK: ${{ github.event.inputs.medusa_pk }} + IN_STRAPI_URL: ${{ github.event.inputs.strapi_url }} + IN_NEXTAUTH_PROD: ${{ github.event.inputs.nextauth_prod }} + IN_NEXTAUTH_STAGING: ${{ github.event.inputs.nextauth_staging }} + SEC_MEDUSA_PK: ${{ secrets.MEDUSA_PUBLISHABLE_KEY }} + run: | + set -euo pipefail + MEDUSA_PK="$IN_MEDUSA_PK" + if [[ -z "$MEDUSA_PK" ]]; then + MEDUSA_PK="$SEC_MEDUSA_PK" + fi + if [[ -z "$MEDUSA_PK" ]]; then + echo "Publishable key not provided, set input 'medusa_pk' or secret MEDUSA_PUBLISHABLE_KEY" >&2 + exit 1 + fi + echo "scope=$IN_SCOPE" >> $GITHUB_OUTPUT + echo "medusa_url=$IN_MEDUSA_URL" >> $GITHUB_OUTPUT + echo "medusa_pk=$MEDUSA_PK" >> $GITHUB_OUTPUT + echo "strapi_url=$IN_STRAPI_URL" >> $GITHUB_OUTPUT + echo "nextauth_prod=$IN_NEXTAUTH_PROD" >> $GITHUB_OUTPUT + echo "nextauth_staging=$IN_NEXTAUTH_STAGING" >> $GITHUB_OUTPUT + + - name: Sync env to Production project + if: ${{ steps.vars.outputs.scope == 'both' || steps.vars.outputs.scope == 'production' }} + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} + PROJECT_ID: ${{ secrets.VERCEL_PRODUCTION_PROJECT_ID }} + MEDUSA_URL: ${{ steps.vars.outputs.medusa_url }} + MEDUSA_PK: ${{ steps.vars.outputs.medusa_pk }} + STRAPI_URL: ${{ steps.vars.outputs.strapi_url }} + NEXTAUTH_URL: ${{ steps.vars.outputs.nextauth_prod }} + run: | + set -euo pipefail + chmod +x scripts/vercel/upsert-env.sh + # Public vars for both production and preview on this project + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_MEDUSA_URL "$MEDUSA_URL" production,preview "$TEAM_ID" + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY "$MEDUSA_PK" production,preview "$TEAM_ID" + if [[ -n "$STRAPI_URL" ]]; then + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_STRAPI_URL "$STRAPI_URL" production,preview "$TEAM_ID" + fi + # Only set NEXTAUTH_URL for production target on this project + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXTAUTH_URL "$NEXTAUTH_URL" production "$TEAM_ID" + + - name: Sync env to Staging project + if: ${{ steps.vars.outputs.scope == 'both' || steps.vars.outputs.scope == 'staging' }} + env: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} + PROJECT_ID: ${{ secrets.VERCEL_STAGING_PROJECT_ID }} + MEDUSA_URL: ${{ steps.vars.outputs.medusa_url }} + MEDUSA_PK: ${{ steps.vars.outputs.medusa_pk }} + STRAPI_URL: ${{ steps.vars.outputs.strapi_url }} + NEXTAUTH_URL: ${{ steps.vars.outputs.nextauth_staging }} + run: | + set -euo pipefail + chmod +x scripts/vercel/upsert-env.sh + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_MEDUSA_URL "$MEDUSA_URL" production,preview "$TEAM_ID" + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY "$MEDUSA_PK" production,preview "$TEAM_ID" + if [[ -n "$STRAPI_URL" ]]; then + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXT_PUBLIC_STRAPI_URL "$STRAPI_URL" production,preview "$TEAM_ID" + fi + scripts/vercel/upsert-env.sh "$PROJECT_ID" NEXTAUTH_URL "$NEXTAUTH_URL" production "$TEAM_ID" + diff --git a/README.md b/README.md index 16688a3..6c1d79e 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,13 @@ solution architecture in `docs/solution-architecture.md`. - `pnpm --filter storefront dev` – Launch the Next.js storefront. Refer to service-specific READMEs inside `apps/*` for additional scripts (migrations, seeds, etc.). + +## Operations & CI/CD + +- End-to-end flow (push → deploy → health): `docs/ci-cd-gce-flow-2025-10-26.md` +- GCE operations cheat sheet: `docs/cheat-sheet.md` + +## Developer Onboarding + +- Quick start: `docs/onboarding.md` +- Runbooks: `docs/runbooks/deployments.md`, `docs/runbooks/troubleshooting.md`, `docs/runbooks/observability-baseline.md` diff --git a/apps/medusa/jest.config.js b/apps/medusa/jest.config.js index d53947e..677ea88 100644 --- a/apps/medusa/jest.config.js +++ b/apps/medusa/jest.config.js @@ -8,7 +8,9 @@ module.exports = { { jsc: { parser: { syntax: "typescript", decorators: true }, + target: "es2022", }, + module: { type: "commonjs" }, }, ], }, diff --git a/apps/medusa/medusa-config.ts b/apps/medusa/medusa-config.ts index 2f7561a..f545175 100644 --- a/apps/medusa/medusa-config.ts +++ b/apps/medusa/medusa-config.ts @@ -1,5 +1,6 @@ import path from "path" import { defineConfig, loadEnv } from "@medusajs/framework/utils" +import "./src/utils/sentry" const projectRoot = path.resolve(__dirname, "..") diff --git a/apps/medusa/package.json b/apps/medusa/package.json index d861249..a3e346b 100644 --- a/apps/medusa/package.json +++ b/apps/medusa/package.json @@ -17,6 +17,7 @@ "seed": "medusa exec ./src/scripts/seed.ts", "start": "medusa start", "dev": "medusa develop", + "typecheck": "tsc --noEmit", "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit", "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit", "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit" @@ -35,7 +36,8 @@ "@mikro-orm/migrations": "6.4.3", "@mikro-orm/postgresql": "6.4.3", "awilix": "^8.0.1", - "pg": "^8.13.0" + "pg": "^8.13.0", + "@sentry/node": "^8.0.0" }, "devDependencies": { "@medusajs/test-utils": "2.10.3", diff --git a/apps/medusa/src/__tests__/logging/logger.format.unit.spec.ts b/apps/medusa/src/__tests__/logging/logger.format.unit.spec.ts new file mode 100644 index 0000000..35fbe14 --- /dev/null +++ b/apps/medusa/src/__tests__/logging/logger.format.unit.spec.ts @@ -0,0 +1,30 @@ +import { logger } from '../../utils/logger' + +describe('logger (JSON format)', () => { + const originalLog = console.log + let output: string[] + + beforeEach(() => { + output = [] + // @ts-expect-error override for test + console.log = (msg: string) => { + output.push(msg) + } + }) + + afterEach(() => { + console.log = originalLog + }) + + it('emits parseable JSON with required fields', () => { + logger.info('test-message', { requestId: 'abc123' }) + expect(output.length).toBeGreaterThan(0) + const obj = JSON.parse(output[0]) + expect(obj.level).toBe('info') + expect(obj.service).toBe('medusa') + expect(obj.message).toBe('test-message') + expect(typeof obj.ts).toBe('string') + expect(obj.requestId).toBe('abc123') + }) +}) + diff --git a/apps/medusa/src/__tests__/smoke/ping.unit.spec.ts b/apps/medusa/src/__tests__/smoke/ping.unit.spec.ts new file mode 100644 index 0000000..5f8604d --- /dev/null +++ b/apps/medusa/src/__tests__/smoke/ping.unit.spec.ts @@ -0,0 +1,6 @@ +describe('smoke: ping', () => { + it('true should be true', () => { + expect(true).toBe(true) + }) +}) + diff --git a/apps/medusa/src/utils/logger.ts b/apps/medusa/src/utils/logger.ts new file mode 100644 index 0000000..7ca8955 --- /dev/null +++ b/apps/medusa/src/utils/logger.ts @@ -0,0 +1,23 @@ +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' + +const SERVICE = 'medusa' + +export function log(level: LogLevel, message: string, meta: Record = {}) { + const entry = { + ts: new Date().toISOString(), + level, + service: SERVICE, + message, + ...meta, + } + // eslint-disable-next-line no-console + console.log(JSON.stringify(entry)) +} + +export const logger = { + debug: (message: string, meta?: Record) => log('debug', message, meta), + info: (message: string, meta?: Record) => log('info', message, meta), + warn: (message: string, meta?: Record) => log('warn', message, meta), + error: (message: string, meta?: Record) => log('error', message, meta), +} + diff --git a/apps/medusa/src/utils/sentry.ts b/apps/medusa/src/utils/sentry.ts new file mode 100644 index 0000000..d16a8f0 --- /dev/null +++ b/apps/medusa/src/utils/sentry.ts @@ -0,0 +1,14 @@ +import * as Sentry from '@sentry/node' + +const DSN = process.env.SENTRY_DSN + +if (DSN) { + Sentry.init({ + dsn: DSN, + tracesSampleRate: 0.1, + environment: process.env.NODE_ENV, + }) +} + +export { Sentry } + diff --git a/apps/medusa/tsconfig.json b/apps/medusa/tsconfig.json index 27bc845..696d301 100644 --- a/apps/medusa/tsconfig.json +++ b/apps/medusa/tsconfig.json @@ -30,6 +30,9 @@ "node_modules", ".medusa/server", ".medusa/admin", - ".cache" + ".cache", + "**/__tests__/**", + "**/*.test.ts", + "**/*.spec.ts" ] } diff --git a/apps/storefront/.env.local.example b/apps/storefront/.env.local.example new file mode 100644 index 0000000..112f25b --- /dev/null +++ b/apps/storefront/.env.local.example @@ -0,0 +1,77 @@ +# =========================================== +# Storefront Environment Configuration +# =========================================== +# Copy this file to .env.local for local development +# Use bootstrap.ts script to generate environment-specific files + +# ------------------------------------------- +# Service URLs (Environment-specific) +# ------------------------------------------- +# Local: http://localhost:9000 +# Preview: https://api-preview.aidenlux.com +# Staging: https://api.aidenlux.com +# Production: https://api.aidenlux.com +NEXT_PUBLIC_MEDUSA_URL=http://localhost:9000 +# Required for Medusa Store API access in browser. Set to your publishable key (pk_...) +NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY= + +# Local: http://localhost:1337 +# Staging/Prod: https://content.aidenlux.com +NEXT_PUBLIC_STRAPI_URL=http://localhost:1337 + +# ------------------------------------------- +# Authentication & Security +# ------------------------------------------- +# NextAuth.js configuration - generate with: openssl rand -base64 32 +NEXTAUTH_SECRET=change-me-in-production +# For staging: https://staging.aidenlux.com +# For production: https://prd.aidenlux.com +NEXTAUTH_URL=http://localhost:3000 + +# Google OAuth (optional) - register at https://console.developers.google.com +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret + +# ------------------------------------------- +# Analytics & Monitoring +# ------------------------------------------- +# Vercel Analytics (auto-configured in Vercel deployments) +NEXT_PUBLIC_VERCEL_ANALYTICS_ID= + +# Sentry Error Tracking +NEXT_PUBLIC_SENTRY_DSN= +SENTRY_AUTH_TOKEN= + +# Segment Analytics (optional) +NEXT_PUBLIC_SEGMENT_WRITE_KEY= + +# --- Quick Launch (optional) --- +# If using PayPal Payment Link for MVP direct checkout, set this to your link +NEXT_PUBLIC_PAYPAL_LINK= + +# ------------------------------------------- +# E-commerce Configuration +# ------------------------------------------- +# Currency and localization +NEXT_PUBLIC_DEFAULT_CURRENCY=USD +NEXT_PUBLIC_SUPPORTED_CURRENCIES=USD,EUR,GBP + +# Payment providers (client-side) +NEXT_PUBLIC_PAYPAL_CLIENT_ID=test +NEXT_PUBLIC_PAYPAL_ENVIRONMENT=sandbox + +# ------------------------------------------- +# Feature Flags +# ------------------------------------------- +# Enable/disable features per environment +NEXT_PUBLIC_ENABLE_ANALYTICS=true +NEXT_PUBLIC_ENABLE_ERROR_REPORTING=true +NEXT_PUBLIC_ENABLE_COMMUNITY_FEATURES=true +NEXT_PUBLIC_ENABLE_DARK_MODE=true + +# ------------------------------------------- +# Development Settings +# ------------------------------------------- +# Enable development-specific features +NEXT_PUBLIC_DEBUG_MODE=false +NEXT_PUBLIC_DEV_TOOLS=false diff --git a/apps/storefront/.gitignore b/apps/storefront/.gitignore index 5ef6a52..b61ee65 100644 --- a/apps/storefront/.gitignore +++ b/apps/storefront/.gitignore @@ -33,8 +33,8 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* -# vercel -.vercel +# vercel (allow committing project linkage so CI can deploy) +# .vercel # typescript *.tsbuildinfo diff --git a/apps/storefront/.vercel/project.json b/apps/storefront/.vercel/project.json new file mode 100644 index 0000000..0478dd1 --- /dev/null +++ b/apps/storefront/.vercel/project.json @@ -0,0 +1,11 @@ +{ + "orgId": "team_adx4fSocOKoip4Zz0TCCHPBR", + "projectId": "prj_Sze4YDpBYJZik3XfGclC38dq80ac", + "settings": { + "framework": "nextjs", + "nodeVersion": "20.x", + "installCommand": "cd ../../ && pwd && ls && corepack enable pnpm && pnpm install --frozen-lockfile", + "buildCommand": "pnpm run build", + "outputDirectory": ".vercel/output" + } +} diff --git a/apps/storefront/.vercel/project.production.json b/apps/storefront/.vercel/project.production.json new file mode 100644 index 0000000..05b38d1 --- /dev/null +++ b/apps/storefront/.vercel/project.production.json @@ -0,0 +1,11 @@ +{ + "orgId": "team_adx4fSocOKoip4Zz0TCCHPBR", + "projectId": "prj_AdvPbwdDFbivF5DcccODeeOj3Y3a", + "settings": { + "framework": "nextjs", + "nodeVersion": "20.x", + "installCommand": "cd ../../ && pwd && ls && corepack enable pnpm && pnpm install --frozen-lockfile", + "buildCommand": "pnpm run build", + "outputDirectory": ".vercel/output" + } +} diff --git a/apps/storefront/package.json b/apps/storefront/package.json index ccae8a1..0295fdd 100644 --- a/apps/storefront/package.json +++ b/apps/storefront/package.json @@ -2,11 +2,13 @@ "name": "storefront", "version": "0.1.0", "private": true, + "packageManager": "pnpm@10.18.2", "scripts": { "dev": "next dev --turbopack", - "build": "next build --turbopack", + "build": "NEXT_TELEMETRY_DISABLED=1 next build --turbopack", "start": "next start", - "lint": "eslint" + "lint": "eslint --cache --cache-location .eslintcache", + "typecheck": "tsc --noEmit" }, "dependencies": { "@medusajs/medusa-js": "^6.1.10", @@ -16,7 +18,9 @@ "react": "19.1.0", "react-dom": "19.1.0", "zod": "^4.1.12", - "zustand": "^5.0.8" + "zustand": "^5.0.8", + "@sentry/nextjs": "^8.0.0", + "next-auth": "^4.24.7" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/apps/storefront/sentry.client.config.ts b/apps/storefront/sentry.client.config.ts new file mode 100644 index 0000000..9f85bc9 --- /dev/null +++ b/apps/storefront/sentry.client.config.ts @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/nextjs' + +if (process.env.NEXT_PUBLIC_SENTRY_DSN) { + Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + tracesSampleRate: 0.1, + environment: process.env.NODE_ENV, + }) +} + diff --git a/apps/storefront/sentry.server.config.ts b/apps/storefront/sentry.server.config.ts new file mode 100644 index 0000000..9f85bc9 --- /dev/null +++ b/apps/storefront/sentry.server.config.ts @@ -0,0 +1,10 @@ +import * as Sentry from '@sentry/nextjs' + +if (process.env.NEXT_PUBLIC_SENTRY_DSN) { + Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + tracesSampleRate: 0.1, + environment: process.env.NODE_ENV, + }) +} + diff --git a/apps/storefront/src/app/api/auth/[...nextauth]/route.ts b/apps/storefront/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..c29d8cb --- /dev/null +++ b/apps/storefront/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import NextAuth from 'next-auth' +import GoogleProvider from 'next-auth/providers/google' + +const handler = (NextAuth as any)({ + providers: [ + (GoogleProvider as any)({ + clientId: process.env.GOOGLE_CLIENT_ID || '', + clientSecret: process.env.GOOGLE_CLIENT_SECRET || '', + }), + ], + callbacks: { + async session({ session }: { session: any }) { + return session + }, + }, +}) + +export { handler as GET, handler as POST } diff --git a/apps/storefront/src/app/auth/status/page.tsx b/apps/storefront/src/app/auth/status/page.tsx new file mode 100644 index 0000000..a22b17f --- /dev/null +++ b/apps/storefront/src/app/auth/status/page.tsx @@ -0,0 +1,26 @@ +import { getServerSession } from 'next-auth' +import Link from 'next/link' + +export default async function AuthStatusPage() { + const session = (await getServerSession()) as { + user?: { email?: string; name?: string } + } | null + const authed = !!(session && session.user) + return ( +
+

Auth Status

+ {authed ? ( + <> +

Signed in as {session?.user?.email || session?.user?.name}

+ Sign out + + ) : ( + <> +

You are not signed in.

+ Sign in with Google + + )} +

Back to shop

+
+ ) +} diff --git a/apps/storefront/src/app/checkout/cancel/page.tsx b/apps/storefront/src/app/checkout/cancel/page.tsx new file mode 100644 index 0000000..70cf3f1 --- /dev/null +++ b/apps/storefront/src/app/checkout/cancel/page.tsx @@ -0,0 +1,10 @@ +export default function CheckoutCancel() { + return ( +
+

Payment canceled

+

Your PayPal checkout was canceled. You can try again or contact support if needed.

+

Back to shop

+
+ ) +} + diff --git a/apps/storefront/src/app/checkout/success/page.tsx b/apps/storefront/src/app/checkout/success/page.tsx new file mode 100644 index 0000000..b5b191b --- /dev/null +++ b/apps/storefront/src/app/checkout/success/page.tsx @@ -0,0 +1,10 @@ +export default function CheckoutSuccess() { + return ( +
+

Thank you for your purchase

+

Your PayPal payment was successful. You will receive an email confirmation shortly.

+

Continue shopping

+
+ ) +} + diff --git a/apps/storefront/src/app/product/[handle]/page.tsx b/apps/storefront/src/app/product/[handle]/page.tsx new file mode 100644 index 0000000..64a27fa --- /dev/null +++ b/apps/storefront/src/app/product/[handle]/page.tsx @@ -0,0 +1,87 @@ +import type { Metadata } from 'next' +import Image from 'next/image' +import Link from 'next/link' +import { notFound } from 'next/navigation' +import { getProductByHandle } from '@/lib/medusa' +import { getPaypalCheckoutLink } from '../../../lib/payments' + +type Params = { handle: string } + +export async function generateMetadata({ params }: { params: Params }): Promise { + const product = await getProductByHandle(params.handle) + if (!product) return { title: 'Product Not Found' } + const img = product?.images?.[0]?.url + return { + title: `${product.title} | Shop`, + description: product.description || undefined, + openGraph: { + title: product.title, + description: product.description || undefined, + images: img ? [{ url: img }] : undefined, + }, + } +} + +export default async function ProductPage({ params }: { params: Params }) { + const product = await getProductByHandle(params.handle) + if (!product) notFound() + + const img = product?.images?.[0]?.url + const variant = product?.variants?.[0] + const price = variant?.prices?.[0] + const amount = price?.amount + const currency = price?.currency_code?.toUpperCase?.() + const paypalLink = getPaypalCheckoutLink() + + const productSchema = { + '@context': 'https://schema.org/', + '@type': 'Product', + name: product.title, + description: product.description || '', + image: (product.images || []).map((i) => (i as { url?: string } | undefined)?.url).filter(Boolean) || [], + offers: amount != null && currency ? { + '@type': 'Offer', + price: amount, + priceCurrency: currency, + availability: 'https://schema.org/InStock', + } : undefined, + } + + return ( +
+ '; - await page.getByLabel('Bio').fill(xssPayload); - await page.getByRole('button', { name: 'Save' }).click(); - - // Reload and verify XSS is escaped (not executed) - await page.reload(); - const bio = await page.getByTestId('user-bio').textContent(); - - // Text should be escaped, script should NOT execute - expect(bio).toContain('<script>'); - expect(bio).not.toContain(' + + + +
+ + + \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..b49a65f --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig, devices } from '@playwright/test' + +const WEB_BASE_URL = process.env.WEB_BASE_URL || process.env.PREVIEW_URL || 'http://localhost:3000' + +export default defineConfig({ + testDir: './tests/e2e', + timeout: 30_000, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: [['html']], + use: { + baseURL: WEB_BASE_URL, + trace: 'on-first-retry', + screenshot: 'only-on-failure', + video: 'off', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}) + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12dd969..ae0b2b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@eslint/js': specifier: ^9.38.0 version: 9.38.0 + '@playwright/test': + specifier: ^1.48.2 + version: 1.56.1 '@typescript-eslint/eslint-plugin': specifier: ^8.46.1 version: 8.46.1(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) @@ -65,6 +68,9 @@ importers: '@mikro-orm/postgresql': specifier: 6.4.3 version: 6.4.3(@mikro-orm/core@6.4.3) + '@sentry/node': + specifier: ^8.0.0 + version: 8.55.0 awilix: specifier: ^8.0.1 version: 8.0.1 @@ -126,6 +132,9 @@ importers: '@medusajs/medusa-js': specifier: ^6.1.10 version: 6.1.10(@medusajs/medusa@2.10.3) + '@sentry/nextjs': + specifier: ^8.0.0 + version: 8.55.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17))) '@tanstack/react-query': specifier: ^5.90.5 version: 5.90.5(react@19.1.0) @@ -134,7 +143,10 @@ importers: version: 1.12.2(debug@4.3.4) next: specifier: 15.5.6 - version: 15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next-auth: + specifier: ^4.24.7 + version: 4.24.11(next@15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: 19.1.0 version: 19.1.0 @@ -178,6 +190,9 @@ importers: apps/strapi: dependencies: + '@sentry/node': + specifier: ^8.0.0 + version: 8.55.0 '@strapi/plugin-cloud': specifier: 5.28.0 version: 5.28.0(4e6ef4b145941a107d5a070ba946c5fb) @@ -2632,10 +2647,22 @@ packages: engines: {node: '>=8.0.0'} deprecated: Deprecated in favor of @oclif/core + '@opentelemetry/api-logs@0.53.0': + resolution: {integrity: sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw==} + engines: {node: '>=14'} + '@opentelemetry/api-logs@0.55.0': resolution: {integrity: sha512-3cpa+qI45VHYcA5c0bHM6VHo9gicv3p5mlLHNG3rLyjQU8b7e0st1rWtrUn3JbZ3DwwCfhKop4eQ9UuYlC6Pkg==} engines: {node: '>=14'} + '@opentelemetry/api-logs@0.57.1': + resolution: {integrity: sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg==} + engines: {node: '>=14'} + + '@opentelemetry/api-logs@0.57.2': + resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} + engines: {node: '>=14'} + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -2658,18 +2685,180 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 + '@opentelemetry/instrumentation-amqplib@0.46.1': + resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.43.0': + resolution: {integrity: sha512-Q57JGpH6T4dkYHo9tKXONgLtxzsh1ZEW5M9A/OwKrZFyEpLqWgjhcZ3hIuVvDlhb426iDF1f9FPToV/mi5rpeA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dataloader@0.16.0': + resolution: {integrity: sha512-88+qCHZC02up8PwKHk0UQKLLqGGURzS3hFQBZC7PnGwReuoKjHXS1o29H58S+QkXJpkTr2GACbx8j6mUoGjNPA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.47.0': + resolution: {integrity: sha512-XFWVx6k0XlU8lu6cBlCa29ONtVt6ADEjmxtyAyeF2+rifk8uBJbk1La0yIVfI0DoKURGbaEDTNelaXG9l/lNNQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.44.1': + resolution: {integrity: sha512-RoVeMGKcNttNfXMSl6W4fsYoCAYP1vi6ZAWIGhBY+o7R9Y0afA7f9JJL0j8LHbyb0P0QhSYk+6O56OwI2k4iRQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fs@0.19.0': + resolution: {integrity: sha512-JGwmHhBkRT2G/BYNV1aGI+bBjJu4fJUD/5/Jat0EWZa2ftrLV3YE8z84Fiij/wK32oMZ88eS8DI4ecLGZhpqsQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-generic-pool@0.43.0': + resolution: {integrity: sha512-at8GceTtNxD1NfFKGAuwtqM41ot/TpcLh+YsGe4dhf7gvv1HW/ZWdq6nfRtS6UjIvZJOokViqLPJ3GVtZItAnQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.47.0': + resolution: {integrity: sha512-Cc8SMf+nLqp0fi8oAnooNEfwZWFnzMiBHCGmDFYqmgjPylyLmi83b+NiTns/rKGwlErpW0AGPt0sMpkbNlzn8w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-grpc@0.55.0': resolution: {integrity: sha512-n2ZH4pRwOy0Vhag/3eKqiyDBwcpUnGgJI9iiIRX7vivE0FMncaLazWphNFezRRaM/LuKwq1TD8pVUvieP68mow==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-hapi@0.45.1': + resolution: {integrity: sha512-VH6mU3YqAKTePPfUPwfq4/xr049774qWtfTuJqVHoVspCLiT3bW+fCQ1toZxt6cxRPYASoYaBsMA3CWo8B8rcw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.57.1': + resolution: {integrity: sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.47.0': + resolution: {integrity: sha512-4HqP9IBC8e7pW9p90P3q4ox0XlbLGme65YTrA3UTLvqvo4Z6b0puqZQP203YFu8m9rE/luLfaG7/xrwwqMUpJw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-kafkajs@0.7.0': + resolution: {integrity: sha512-LB+3xiNzc034zHfCtgs4ITWhq6Xvdo8bsq7amR058jZlf2aXXDrN9SV4si4z2ya9QX4tz6r4eZJwDkXOp14/AQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-knex@0.44.0': + resolution: {integrity: sha512-SlT0+bLA0Lg3VthGje+bSZatlGHw/vwgQywx0R/5u9QC59FddTQSPJeWNw29M6f8ScORMeUOOTwihlQAn4GkJQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.47.0': + resolution: {integrity: sha512-HFdvqf2+w8sWOuwtEXayGzdZ2vWpCKEQv5F7+2DSA74Te/Cv4rvb2E5So5/lh+ok4/RAIPuvCbCb/SHQFzMmbw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-lru-memoizer@0.44.0': + resolution: {integrity: sha512-Tn7emHAlvYDFik3vGU0mdwvWJDwtITtkJ+5eT2cUquct6nIs+H8M47sqMJkCpyPe5QIBJoTOHxmc6mj9lz6zDw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.51.0': + resolution: {integrity: sha512-cMKASxCX4aFxesoj3WK8uoQ0YUrRvnfxaO72QWI2xLu5ZtgX/QvdGBlU3Ehdond5eb74c2s1cqRQUIptBnKz1g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.46.0': + resolution: {integrity: sha512-mtVv6UeaaSaWTeZtLo4cx4P5/ING2obSqfWGItIFSunQBrYROfhuVe7wdIrFUs2RH1tn2YYpAJyMaRe/bnTTIQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.45.0': + resolution: {integrity: sha512-qLslv/EPuLj0IXFvcE3b0EqhWI8LKmrgRPIa4gUd8DllbBpqJAvLNJSv3cC6vWwovpbSI3bagNO/3Q2SuXv2xA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.45.0': + resolution: {integrity: sha512-tWWyymgwYcTwZ4t8/rLDfPYbOTF3oYB8SxnYMtIQ1zEf5uDm90Ku3i6U/vhaMyfHNlIHvDhvJh+qx5Nc4Z3Acg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.44.0': + resolution: {integrity: sha512-t16pQ7A4WYu1yyQJZhRKIfUNvl5PAaF2pEteLvgJb/BWdd1oNuU1rOYt4S825kMy+0q4ngiX281Ss9qiwHfxFQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.50.0': + resolution: {integrity: sha512-TtLxDdYZmBhFswm8UIsrDjh/HFBeDXd4BLmE8h2MxirNHewLJ0VS9UUddKKEverb5Sm2qFVjqRjcU+8Iw4FJ3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.46.0': + resolution: {integrity: sha512-aTUWbzbFMFeRODn3720TZO0tsh/49T8H3h8vVnVKJ+yE36AeW38Uj/8zykQ/9nO8Vrtjr5yKuX3uMiG/W8FKNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-tedious@0.18.0': + resolution: {integrity: sha512-9zhjDpUDOtD+coeADnYEJQ0IeLVCj7w/hqzIutdp5NqS1VqTAanaEfsEcSypyvYv5DX3YOsTUoF+nr2wDXPETA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-undici@0.10.0': + resolution: {integrity: sha512-vm+V255NGw9gaSsPD6CP0oGo8L55BffBc8KnxqsMuc6XiAD1L8SFNzsW0RHhxJFqy9CJaJh+YiJ5EHXuZ5rZBw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + + '@opentelemetry/instrumentation@0.53.0': + resolution: {integrity: sha512-DMwg0hy4wzf7K73JJtl95m/e0boSoWhH07rfvHvYzQtBD3Bmv0Wc1x733vyZBqmFm8OjJD0/pfiUg1W3JjFX0A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.55.0': resolution: {integrity: sha512-YDCMlaQRZkziLL3t6TONRgmmGxDx6MyQDXRD0dknkkgUZtOK5+8MWft1OXzmNu6XfBOdT12MKN5rz+jHUkafKQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.57.1': + resolution: {integrity: sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.57.2': + resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/propagator-b3@1.30.1': resolution: {integrity: sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==} engines: {node: '>=14'} @@ -2682,6 +2871,10 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + '@opentelemetry/resources@1.30.1': resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} engines: {node: '>=14'} @@ -2708,6 +2901,15 @@ packages: resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} engines: {node: '>=14'} + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@paralleldrive/cuid2@2.2.2': resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} @@ -2715,6 +2917,11 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@playwright/test@1.56.1': + resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} + engines: {node: '>=18'} + hasBin: true + '@pmmmwh/react-refresh-webpack-plugin@0.5.15': resolution: {integrity: sha512-LFWllMA55pzB9D34w/wXUCf8+c+IYKuJDgxiZ3qMhl64KRMBHYM1I3VdGaD2BV5FNPV2/S2596bppxHbv2ZydQ==} engines: {node: '>= 10.13'} @@ -2756,6 +2963,9 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@prisma/instrumentation@5.22.0': + resolution: {integrity: sha512-LxccF392NN37ISGxIurUljZSh1YWnphO34V5a0+T7FVQG2u9bhAXRTJpgmQ3483woVhkraQZFF7cbRrpbw/F4Q==} + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -4631,6 +4841,24 @@ packages: '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/plugin-commonjs@28.0.1': + resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.52.5': resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] @@ -4778,6 +5006,121 @@ packages: resolution: {integrity: sha512-/ZqxUvKeEztU9drOoPC/8opEPOk+jLlB2q4+xpx6HVLq6aFu3pMpalkTpAQz8XfRfpLp8O25bh6pGPcHDCYpqg==} engines: {node: '>=12.*'} + '@sentry-internal/browser-utils@8.55.0': + resolution: {integrity: sha512-ROgqtQfpH/82AQIpESPqPQe0UyWywKJsmVIqi3c5Fh+zkds5LUxnssTj3yNd1x+kxaPDVB023jAP+3ibNgeNDw==} + engines: {node: '>=14.18'} + + '@sentry-internal/feedback@8.55.0': + resolution: {integrity: sha512-cP3BD/Q6pquVQ+YL+rwCnorKuTXiS9KXW8HNKu4nmmBAyf7urjs+F6Hr1k9MXP5yQ8W3yK7jRWd09Yu6DHWOiw==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay-canvas@8.55.0': + resolution: {integrity: sha512-nIkfgRWk1091zHdu4NbocQsxZF1rv1f7bbp3tTIlZYbrH62XVZosx5iHAuZG0Zc48AETLE7K4AX9VGjvQj8i9w==} + engines: {node: '>=14.18'} + + '@sentry-internal/replay@8.55.0': + resolution: {integrity: sha512-roCDEGkORwolxBn8xAKedybY+Jlefq3xYmgN2fr3BTnsXjSYOPC7D1/mYqINBat99nDtvgFvNfRcZPiwwZ1hSw==} + engines: {node: '>=14.18'} + + '@sentry/babel-plugin-component-annotate@2.22.7': + resolution: {integrity: sha512-aa7XKgZMVl6l04NY+3X7BP7yvQ/s8scn8KzQfTLrGRarziTlMGrsCOBQtCNWXOPEbtxAIHpZ9dsrAn5EJSivOQ==} + engines: {node: '>= 14'} + + '@sentry/browser@8.55.0': + resolution: {integrity: sha512-1A31mCEWCjaMxJt6qGUK+aDnLDcK6AwLAZnqpSchNysGni1pSn1RWSmk9TBF8qyTds5FH8B31H480uxMPUJ7Cw==} + engines: {node: '>=14.18'} + + '@sentry/bundler-plugin-core@2.22.7': + resolution: {integrity: sha512-ouQh5sqcB8vsJ8yTTe0rf+iaUkwmeUlGNFi35IkCFUQlWJ22qS6OfvNjOqFI19e6eGUXks0c/2ieFC4+9wJ+1g==} + engines: {node: '>= 14'} + + '@sentry/cli-darwin@2.39.1': + resolution: {integrity: sha512-kiNGNSAkg46LNGatfNH5tfsmI/kCAaPA62KQuFZloZiemTNzhy9/6NJP8HZ/GxGs8GDMxic6wNrV9CkVEgFLJQ==} + engines: {node: '>=10'} + os: [darwin] + + '@sentry/cli-linux-arm64@2.39.1': + resolution: {integrity: sha512-5VbVJDatolDrWOgaffsEM7znjs0cR8bHt9Bq0mStM3tBolgAeSDHE89NgHggfZR+DJ2VWOy4vgCwkObrUD6NQw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux, freebsd] + + '@sentry/cli-linux-arm@2.39.1': + resolution: {integrity: sha512-DkENbxyRxUrfLnJLXTA4s5UL/GoctU5Cm4ER1eB7XN7p9WsamFJd/yf2KpltkjEyiTuplv0yAbdjl1KX3vKmEQ==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux, freebsd] + + '@sentry/cli-linux-i686@2.39.1': + resolution: {integrity: sha512-pXWVoKXCRrY7N8vc9H7mETiV9ZCz+zSnX65JQCzZxgYrayQPJTc+NPRnZTdYdk5RlAupXaFicBI2GwOCRqVRkg==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [linux, freebsd] + + '@sentry/cli-linux-x64@2.39.1': + resolution: {integrity: sha512-IwayNZy+it7FWG4M9LayyUmG1a/8kT9+/IEm67sT5+7dkMIMcpmHDqL8rWcPojOXuTKaOBBjkVdNMBTXy0mXlA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux, freebsd] + + '@sentry/cli-win32-i686@2.39.1': + resolution: {integrity: sha512-NglnNoqHSmE+Dz/wHeIVRnV2bLMx7tIn3IQ8vXGO5HWA2f8zYJGktbkLq1Lg23PaQmeZLPGlja3gBQfZYSG10Q==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [win32] + + '@sentry/cli-win32-x64@2.39.1': + resolution: {integrity: sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@sentry/cli@2.39.1': + resolution: {integrity: sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ==} + engines: {node: '>= 10'} + hasBin: true + + '@sentry/core@8.55.0': + resolution: {integrity: sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==} + engines: {node: '>=14.18'} + + '@sentry/nextjs@8.55.0': + resolution: {integrity: sha512-poUjt8KF/6RKn0AwBYgtFu764nduziCYpuLgfDNTs7qAMWBMq3tTnDiXxjwJCDnaPeZRAK2pfoAEZxWSXf+22w==} + engines: {node: '>=14.18'} + peerDependencies: + next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 + + '@sentry/node@8.55.0': + resolution: {integrity: sha512-h10LJLDTRAzYgay60Oy7moMookqqSZSviCWkkmHZyaDn+4WURnPp5SKhhfrzPRQcXKrweiOwDSHBgn1tweDssg==} + engines: {node: '>=14.18'} + + '@sentry/opentelemetry@8.55.0': + resolution: {integrity: sha512-UvatdmSr3Xf+4PLBzJNLZ2JjG1yAPWGe/VrJlJAqyTJ2gKeTzgXJJw8rp4pbvNZO8NaTGEYhhO+scLUj0UtLAQ==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/context-async-hooks': ^1.30.1 + '@opentelemetry/core': ^1.30.1 + '@opentelemetry/instrumentation': ^0.57.1 + '@opentelemetry/sdk-trace-base': ^1.30.1 + '@opentelemetry/semantic-conventions': ^1.28.0 + + '@sentry/react@8.55.0': + resolution: {integrity: sha512-/qNBvFLpvSa/Rmia0jpKfJdy16d4YZaAnH/TuKLAtm0BWlsPQzbXCU4h8C5Hsst0Do0zG613MEtEmWpWrVOqWA==} + engines: {node: '>=14.18'} + peerDependencies: + react: ^16.14.0 || 17.x || 18.x || 19.x + + '@sentry/vercel-edge@8.55.0': + resolution: {integrity: sha512-uDoHz+iBjkXsyRStodZxHssMXj7WbOrDkFLy7ggCtvREBg2n4CRS4OcBu+kAwZOysOZblAtx/ZOdIMW1kJXswQ==} + engines: {node: '>=14.18'} + + '@sentry/webpack-plugin@2.22.7': + resolution: {integrity: sha512-j5h5LZHWDlm/FQCCmEghQ9FzYXwfZdlOf3FE/X6rK6lrtx0JCAkq+uhMSasoyP4XYKL4P4vRS6WFSos4jxf/UA==} + engines: {node: '>= 14'} + peerDependencies: + webpack: '>=4.40.0' + '@sigstore/bundle@2.3.2': resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==} engines: {node: ^16.14.0 || >=18.0.0} @@ -5530,6 +5873,9 @@ packages: '@types/co-body@6.1.3': resolution: {integrity: sha512-UhuhrQ5hclX6UJctv5m4Rfp52AfG9o9+d9/HwjxhVB5NjXxr5t9oKgJxN8xRHgr35oo8meUEHUPFWiKg6y71aA==} + '@types/connect@3.4.36': + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -5652,6 +5998,9 @@ packages: '@types/mute-stream@0.0.4': resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + '@types/mysql@2.15.26': + resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} + '@types/node@20.19.22': resolution: {integrity: sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==} @@ -5667,6 +6016,12 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/pg-pool@2.0.6': + resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/picomatch@4.0.2': resolution: {integrity: sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==} @@ -5730,6 +6085,9 @@ packages: '@types/stylis@4.2.5': resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} + '@types/tedious@4.0.14': + resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + '@types/through@0.0.33': resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} @@ -6075,6 +6433,10 @@ packages: addressparser@1.0.1: resolution: {integrity: sha512-aQX7AISOMM7HFE0iZ3+YnD07oIeJqWGVnJ+ZIKaBZAk03ftmVYVqsGas/rbXKR21n4D/hKCSHypvcyOkds/xzg==} + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -6549,6 +6911,10 @@ packages: resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} engines: {node: '>=4'} + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -6809,6 +7175,9 @@ packages: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -7606,6 +7975,9 @@ packages: estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -7843,6 +8215,9 @@ packages: peerDependencies: react: '>=16.8.0' + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -7908,6 +8283,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -8022,6 +8402,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported + glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + global-agent@3.0.0: resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} engines: {node: '>=10.0'} @@ -8241,6 +8625,10 @@ packages: 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'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -8582,6 +8970,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -9406,6 +9797,10 @@ packages: resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} engines: {node: '>=12'} + magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} + engines: {node: '>=12'} + mailcomposer@3.12.0: resolution: {integrity: sha512-zBeDoKUTNI8IAsazoMQFt3eVSVRtDtgrvBjBVdBjxDEX+5KLlKtEFCrBXnxPhs8aTYufUS1SmbFnGpjHS53deg==} deprecated: This project is unmaintained @@ -9679,6 +10074,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -9714,6 +10113,10 @@ packages: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} + minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} @@ -9843,6 +10246,20 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-auth@4.24.11: + resolution: {integrity: sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==} + peerDependencies: + '@auth/core': 0.34.2 + next: ^12.2.5 || ^13 || ^14 || ^15 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 || ^19 + react-dom: ^17.0.2 || ^18 || ^19 + peerDependenciesMeta: + '@auth/core': + optional: true + nodemailer: + optional: true + next@15.5.6: resolution: {integrity: sha512-zTxsnI3LQo3c9HSdSf91O1jMNsEzIXDShXd4wVdg9y5shwLqBXi4ZtUUJyB86KGVSJLZx0PFONvO54aheGX8QQ==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} @@ -10002,10 +10419,17 @@ packages: oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -10057,6 +10481,10 @@ packages: oblivious-set@1.0.0: resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} + oidc-token-hash@5.1.1: + resolution: {integrity: sha512-D7EmwxJV6DsEB6vOFLrBM2OzsVgQzgPWyHlV2OOAVj772n+WTXpudC9e9u5BVKQnYwaD30Ivhi9b+4UeBcGu9g==} + engines: {node: ^10.13.0 || >=12.0.0} + on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -10105,6 +10533,9 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + openid-client@5.7.1: + resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -10431,6 +10862,16 @@ packages: player.style@0.0.8: resolution: {integrity: sha512-ScmFzio3634eEn+ejpkEw13F5xYvnPghtaZz/Kg7QQP78ECrxdjRVqwVPZhUwbYxmg5OIScByOgHfrHpzTtR1Q==} + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + engines: {node: '>=18'} + hasBin: true + plop@4.0.1: resolution: {integrity: sha512-5n8QU93kvL/ObOzBcPAB1siVFtAH1TZM6TntJ3JK5kXT0jIgnQV+j+uaOWWFJlg1cNkzLYm8klgASF65K36q9w==} engines: {node: '>=18'} @@ -10563,6 +11004,14 @@ packages: resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} engines: {node: '>=15.0.0'} + preact-render-to-string@5.2.6: + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + + preact@10.27.2: + resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + preferred-pm@3.1.2: resolution: {integrity: sha512-nk7dKrcW8hfCZ4H6klWcdRknBOXWzNQByJ0oJyX97BOupsYD+FzLS4hflgEu/uPUEHZCuRfMxzCBsuWd7OzT8Q==} engines: {node: '>=10'} @@ -10587,6 +11036,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + prism-react-renderer@2.4.1: resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} peerDependencies: @@ -10611,6 +11063,10 @@ packages: resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + promise-all-reject-late@1.0.1: resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} @@ -11174,6 +11630,10 @@ packages: engines: {node: '>= 0.4'} hasBin: true + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true @@ -11217,6 +11677,11 @@ packages: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + rollup@4.52.5: resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -11569,6 +12034,10 @@ packages: stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + stacktrace-parser@0.1.11: + resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} + engines: {node: '>=6'} + standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -12063,6 +12532,10 @@ packages: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} + type-fest@0.7.1: + resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} + engines: {node: '>=8'} + type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -12219,6 +12692,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unplugin@1.0.1: + resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -12486,6 +12962,9 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} + webpack-virtual-modules@0.5.0: + resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + webpack@5.102.1: resolution: {integrity: sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==} engines: {node: '>=10.13.0'} @@ -14046,6 +14525,18 @@ snapshots: dependencies: tslib: 2.8.1 + '@formatjs/intl@2.10.0(typescript@5.4.4)': + dependencies: + '@formatjs/ecma402-abstract': 1.18.2 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.6 + '@formatjs/intl-displaynames': 6.6.6 + '@formatjs/intl-listformat': 7.5.5 + intl-messageformat: 10.5.11 + tslib: 2.8.1 + optionalDependencies: + typescript: 5.4.4 + '@formatjs/intl@2.10.0(typescript@5.9.3)': dependencies: '@formatjs/ecma402-abstract': 1.18.2 @@ -16332,10 +16823,22 @@ snapshots: '@oclif/screen@1.0.4': {} + '@opentelemetry/api-logs@0.53.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs@0.55.0': dependencies: '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs@0.57.1': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.57.2': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api@1.9.0': {} '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': @@ -16355,18 +16858,262 @@ snapshots: '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.28.0 - '@opentelemetry/instrumentation-grpc@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-amqplib@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-connect@0.43.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@types/connect': 3.4.36 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dataloader@0.16.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fastify@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fs@0.19.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-generic-pool@0.43.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-grpc@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.45.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.57.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + forwarded-parse: 2.1.2 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-kafkajs@0.7.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-knex@0.44.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-lru-memoizer@0.44.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.51.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.45.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.45.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@types/mysql': 2.15.26 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.44.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.50.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + '@types/pg': 8.6.1 + '@types/pg-pool': 2.0.6 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis-4@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-tedious@0.18.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@types/tedious': 4.0.14 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-undici@0.10.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.53.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + semver: 7.7.3 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.55.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.55.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + semver: 7.7.3 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.57.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 + '@opentelemetry/api-logs': 0.57.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + semver: 7.7.3 + shimmer: 1.2.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.55.0 + '@opentelemetry/api-logs': 0.57.2 '@types/shimmer': 1.2.0 import-in-the-middle: 1.15.0 require-in-the-middle: 7.5.2 @@ -16385,6 +17132,8 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common@0.36.2': {} + '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -16412,6 +17161,13 @@ snapshots: '@opentelemetry/semantic-conventions@1.28.0': {} + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@panva/hkdf@1.2.1': {} + '@paralleldrive/cuid2@2.2.2': dependencies: '@noble/hashes': 1.8.0 @@ -16419,6 +17175,10 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@playwright/test@1.56.1': + dependencies: + playwright: 1.56.1 + '@pmmmwh/react-refresh-webpack-plugin@0.5.15(react-refresh@0.14.0)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.102.1(esbuild@0.25.11))': dependencies: ansi-html: 0.0.9 @@ -16448,6 +17208,14 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@prisma/instrumentation@5.22.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -21343,6 +22111,26 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rollup/plugin-commonjs@28.0.1(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.19 + picomatch: 4.0.3 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@5.3.0(rollup@3.29.5)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 3.29.5 + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true @@ -21460,6 +22248,189 @@ snapshots: transitivePeerDependencies: - debug + '@sentry-internal/browser-utils@8.55.0': + dependencies: + '@sentry/core': 8.55.0 + + '@sentry-internal/feedback@8.55.0': + dependencies: + '@sentry/core': 8.55.0 + + '@sentry-internal/replay-canvas@8.55.0': + dependencies: + '@sentry-internal/replay': 8.55.0 + '@sentry/core': 8.55.0 + + '@sentry-internal/replay@8.55.0': + dependencies: + '@sentry-internal/browser-utils': 8.55.0 + '@sentry/core': 8.55.0 + + '@sentry/babel-plugin-component-annotate@2.22.7': {} + + '@sentry/browser@8.55.0': + dependencies: + '@sentry-internal/browser-utils': 8.55.0 + '@sentry-internal/feedback': 8.55.0 + '@sentry-internal/replay': 8.55.0 + '@sentry-internal/replay-canvas': 8.55.0 + '@sentry/core': 8.55.0 + + '@sentry/bundler-plugin-core@2.22.7(encoding@0.1.13)': + dependencies: + '@babel/core': 7.28.4 + '@sentry/babel-plugin-component-annotate': 2.22.7 + '@sentry/cli': 2.39.1(encoding@0.1.13) + dotenv: 16.6.1 + find-up: 5.0.0 + glob: 9.3.5 + magic-string: 0.30.8 + unplugin: 1.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/cli-darwin@2.39.1': + optional: true + + '@sentry/cli-linux-arm64@2.39.1': + optional: true + + '@sentry/cli-linux-arm@2.39.1': + optional: true + + '@sentry/cli-linux-i686@2.39.1': + optional: true + + '@sentry/cli-linux-x64@2.39.1': + optional: true + + '@sentry/cli-win32-i686@2.39.1': + optional: true + + '@sentry/cli-win32-x64@2.39.1': + optional: true + + '@sentry/cli@2.39.1(encoding@0.1.13)': + dependencies: + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0(encoding@0.1.13) + progress: 2.0.3 + proxy-from-env: 1.1.0 + which: 2.0.2 + optionalDependencies: + '@sentry/cli-darwin': 2.39.1 + '@sentry/cli-linux-arm': 2.39.1 + '@sentry/cli-linux-arm64': 2.39.1 + '@sentry/cli-linux-i686': 2.39.1 + '@sentry/cli-linux-x64': 2.39.1 + '@sentry/cli-win32-i686': 2.39.1 + '@sentry/cli-win32-x64': 2.39.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@sentry/core@8.55.0': {} + + '@sentry/nextjs@8.55.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17)))': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.28.0 + '@rollup/plugin-commonjs': 28.0.1(rollup@3.29.5) + '@sentry-internal/browser-utils': 8.55.0 + '@sentry/core': 8.55.0 + '@sentry/node': 8.55.0 + '@sentry/opentelemetry': 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) + '@sentry/react': 8.55.0(react@19.1.0) + '@sentry/vercel-edge': 8.55.0 + '@sentry/webpack-plugin': 2.22.7(encoding@0.1.13)(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17))) + chalk: 3.0.0 + next: 15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + resolve: 1.22.8 + rollup: 3.29.5 + stacktrace-parser: 0.1.11 + transitivePeerDependencies: + - '@opentelemetry/context-async-hooks' + - '@opentelemetry/core' + - '@opentelemetry/instrumentation' + - '@opentelemetry/sdk-trace-base' + - encoding + - react + - supports-color + - webpack + + '@sentry/node@8.55.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.16.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.19.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.57.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.7.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.44.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.44.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.51.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.45.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.45.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.44.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.50.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.18.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.10.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@prisma/instrumentation': 5.22.0 + '@sentry/core': 8.55.0 + '@sentry/opentelemetry': 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0) + import-in-the-middle: 1.15.0 + transitivePeerDependencies: + - supports-color + + '@sentry/opentelemetry@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.28.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + '@sentry/core': 8.55.0 + + '@sentry/react@8.55.0(react@19.1.0)': + dependencies: + '@sentry/browser': 8.55.0 + '@sentry/core': 8.55.0 + hoist-non-react-statics: 3.3.2 + react: 19.1.0 + + '@sentry/vercel-edge@8.55.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@sentry/core': 8.55.0 + + '@sentry/webpack-plugin@2.22.7(encoding@0.1.13)(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17)))': + dependencies: + '@sentry/bundler-plugin-core': 2.22.7(encoding@0.1.13) + unplugin: 1.0.1 + uuid: 9.0.1 + webpack: 5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17)) + transitivePeerDependencies: + - encoding + - supports-color + '@sigstore/bundle@2.3.2': dependencies: '@sigstore/protobuf-specs': 0.3.3 @@ -23137,6 +24108,10 @@ snapshots: '@types/node': 20.19.22 '@types/qs': 6.14.0 + '@types/connect@3.4.36': + dependencies: + '@types/node': 20.19.22 + '@types/connect@3.4.38': dependencies: '@types/node': 20.19.22 @@ -23288,6 +24263,10 @@ snapshots: dependencies: '@types/node': 20.19.22 + '@types/mysql@2.15.26': + dependencies: + '@types/node': 20.19.22 + '@types/node@20.19.22': dependencies: undici-types: 6.21.0 @@ -23304,6 +24283,16 @@ snapshots: '@types/parse-json@4.0.2': {} + '@types/pg-pool@2.0.6': + dependencies: + '@types/pg': 8.6.1 + + '@types/pg@8.6.1': + dependencies: + '@types/node': 20.19.22 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + '@types/picomatch@4.0.2': {} '@types/pluralize@0.0.33': {} @@ -23364,6 +24353,10 @@ snapshots: '@types/stylis@4.2.5': {} + '@types/tedious@4.0.14': + dependencies: + '@types/node': 20.19.22 + '@types/through@0.0.33': dependencies: '@types/node': 20.19.22 @@ -23755,6 +24748,12 @@ snapshots: addressparser@1.0.1: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + agent-base@7.1.4: {} aggregate-error@3.1.0: @@ -24328,6 +25327,11 @@ snapshots: pathval: 1.1.1 type-detect: 4.1.0 + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -24615,6 +25619,8 @@ snapshots: common-tags@1.8.2: {} + commondir@1.0.1: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -25387,7 +26393,7 @@ snapshots: eslint: 9.38.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.38.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.38.0(jiti@2.6.1)) eslint-plugin-react-hooks: 5.2.0(eslint@9.38.0(jiti@2.6.1)) @@ -25417,7 +26423,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -25432,7 +26438,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.1(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -25585,6 +26591,8 @@ snapshots: estree-util-is-identifier-name@3.0.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -25917,6 +26925,8 @@ snapshots: transitivePeerDependencies: - '@types/react' + forwarded-parse@2.1.2: {} + forwarded@0.2.0: {} fraction.js@4.3.7: {} @@ -25975,6 +26985,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -26110,6 +27123,13 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + glob@9.3.5: + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.11.1 + global-agent@3.0.0: dependencies: boolean: 3.2.0 @@ -26396,6 +27416,13 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -26748,6 +27775,10 @@ snapshots: is-plain-object@5.0.0: {} + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.8 + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -27802,6 +28833,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@0.30.8: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + mailcomposer@3.12.0: dependencies: buildmail: 3.10.0 @@ -28179,6 +29214,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@8.0.4: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.3: dependencies: brace-expansion: 2.0.2 @@ -28217,6 +29256,8 @@ snapshots: dependencies: yallist: 4.0.0 + minipass@4.2.8: {} + minipass@5.0.0: {} minipass@7.1.2: {} @@ -28341,7 +29382,22 @@ snapshots: neo-async@2.6.2: {} - next@15.5.6(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-auth@4.24.11(next@15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@babel/runtime': 7.28.4 + '@panva/hkdf': 1.2.1 + cookie: 0.7.2 + jose: 4.15.9 + next: 15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + oauth: 0.9.15 + openid-client: 5.7.1 + preact: 10.27.2 + preact-render-to-string: 5.2.6(preact@10.27.2) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + uuid: 8.3.2 + + next@15.5.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@next/env': 15.5.6 '@swc/helpers': 0.5.15 @@ -28360,6 +29416,7 @@ snapshots: '@next/swc-win32-arm64-msvc': 15.5.6 '@next/swc-win32-x64-msvc': 15.5.6 '@opentelemetry/api': 1.9.0 + '@playwright/test': 1.56.1 sharp: 0.34.4 transitivePeerDependencies: - '@babel/core' @@ -28544,8 +29601,12 @@ snapshots: oauth-sign@0.9.0: {} + oauth@0.9.15: {} + object-assign@4.1.1: {} + object-hash@2.2.0: {} + object-hash@3.0.0: {} object-inspect@1.13.4: {} @@ -28608,6 +29669,8 @@ snapshots: oblivious-set@1.0.0: {} + oidc-token-hash@5.1.1: {} + on-finished@2.3.0: dependencies: ee-first: 1.1.1 @@ -28652,6 +29715,13 @@ snapshots: opener@1.5.2: {} + openid-client@5.7.1: + dependencies: + jose: 4.15.9 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.1.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -29012,6 +30082,14 @@ snapshots: dependencies: media-chrome: 4.2.3 + playwright-core@1.56.1: {} + + playwright@1.56.1: + dependencies: + playwright-core: 1.56.1 + optionalDependencies: + fsevents: 2.3.2 + plop@4.0.1(@types/node@20.19.22): dependencies: '@types/liftoff': 4.0.3 @@ -29129,6 +30207,13 @@ snapshots: transitivePeerDependencies: - debug + preact-render-to-string@5.2.6(preact@10.27.2): + dependencies: + preact: 10.27.2 + pretty-format: 3.8.0 + + preact@10.27.2: {} + preferred-pm@3.1.2: dependencies: find-up: 5.0.0 @@ -29157,6 +30242,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-format@3.8.0: {} + prism-react-renderer@2.4.1(react@18.3.1): dependencies: '@types/prismjs': 1.26.5 @@ -29179,6 +30266,8 @@ snapshots: proggy@2.0.0: {} + progress@2.0.3: {} + promise-all-reject-late@1.0.1: {} promise-call-limit@3.0.2: {} @@ -29653,7 +30742,7 @@ snapshots: dependencies: '@formatjs/ecma402-abstract': 1.18.2 '@formatjs/icu-messageformat-parser': 2.7.6 - '@formatjs/intl': 2.10.0(typescript@5.9.3) + '@formatjs/intl': 2.10.0(typescript@5.4.4) '@formatjs/intl-displaynames': 6.6.6 '@formatjs/intl-listformat': 7.5.5 '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.26) @@ -30203,6 +31292,12 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.8: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 @@ -30248,6 +31343,10 @@ snapshots: semver-compare: 1.0.0 sprintf-js: 1.1.3 + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 @@ -30715,6 +31814,10 @@ snapshots: stackframe@1.3.4: {} + stacktrace-parser@0.1.11: + dependencies: + type-fest: 0.7.1 + standard-as-callback@2.1.0: {} statuses@1.5.0: {} @@ -30998,6 +32101,17 @@ snapshots: tarn@3.0.2: {} + terser-webpack-plugin@5.3.14(@swc/core@1.5.7(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17))): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.0 + webpack: 5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17)) + optionalDependencies: + '@swc/core': 1.5.7(@swc/helpers@0.5.17) + terser-webpack-plugin@5.3.14(esbuild@0.25.11)(webpack@5.102.1(esbuild@0.25.11)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -31215,6 +32329,8 @@ snapshots: type-fest@0.6.0: {} + type-fest@0.7.1: {} + type-fest@0.8.1: {} type-fest@4.41.0: {} @@ -31399,6 +32515,13 @@ snapshots: unpipe@1.0.0: {} + unplugin@1.0.1: + dependencies: + acorn: 8.15.0 + chokidar: 3.6.0 + webpack-sources: 3.3.3 + webpack-virtual-modules: 0.5.0 + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -31692,6 +32815,40 @@ snapshots: webpack-sources@3.3.3: {} + webpack-virtual-modules@0.5.0: {} + + webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17)): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.26.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(@swc/core@1.5.7(@swc/helpers@0.5.17))(webpack@5.102.1(@swc/core@1.5.7(@swc/helpers@0.5.17))) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.102.1(esbuild@0.25.11): dependencies: '@types/eslint-scope': 3.7.7 diff --git a/scripts/gce/configure-secrets.sh b/scripts/gce/configure-secrets.sh index d3c0048..8924301 100755 --- a/scripts/gce/configure-secrets.sh +++ b/scripts/gce/configure-secrets.sh @@ -226,7 +226,8 @@ configure_domains() { CORS_DOMAINS="$MAIN_URL,$STAGING_URL" run_cmd "sudo sed -i \"s|STORE_CORS=.*|STORE_CORS=$CORS_DOMAINS|\" /srv/cs/env/medusa.env" run_cmd "sudo sed -i \"s|ADMIN_CORS=.*|ADMIN_CORS=$ADMIN_URL|\" /srv/cs/env/medusa.env" - run_cmd "sudo sed -i \"s|AUTH_CORS=.*|AUTH_CORS=$MAIN_URL|\" /srv/cs/env/medusa.env" + # Allow both prod and staging storefront domains for auth callbacks + run_cmd "sudo sed -i \"s|AUTH_CORS=.*|AUTH_CORS=$MAIN_URL,$STAGING_URL|\" /srv/cs/env/medusa.env" log "Updating Strapi URL configuration" run_cmd "sudo sed -i \"s|URL=.*|URL=$CONTENT_URL|\" /srv/cs/env/strapi.env" @@ -328,4 +329,4 @@ main() { if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" -fi \ No newline at end of file +fi diff --git a/scripts/vercel/upsert-env.sh b/scripts/vercel/upsert-env.sh new file mode 100644 index 0000000..3572efe --- /dev/null +++ b/scripts/vercel/upsert-env.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Upsert a single Vercel project environment variable via API. +# Usage: VERCEL_TOKEN=... ./upsert-env.sh [team_id] +# Example: ./upsert-env.sh proj_abc NEXT_PUBLIC_MEDUSA_URL https://api.aidenlux.com production,preview team_123 + +if [[ $# -lt 4 ]]; then + echo "Usage: VERCEL_TOKEN=... $0 [team_id]" >&2 + exit 1 +fi + +PROJECT_ID="$1" +KEY="$2" +VALUE="$3" +TARGETS_CSV="$4" +TEAM_ID="${5-}" + +if [[ -z "${VERCEL_TOKEN:-}" ]]; then + echo "VERCEL_TOKEN is required" >&2 + exit 1 +fi + +BASE_URL="https://api.vercel.com" +TEAM_QS="" +if [[ -n "$TEAM_ID" ]]; then + TEAM_QS="?teamId=$TEAM_ID" +fi + +# List existing envs and find by key +LIST_URL="$BASE_URL/v10/projects/$PROJECT_ID/env${TEAM_QS}" +EXISTING=$(curl -fsSL -H "Authorization: Bearer $VERCEL_TOKEN" "$LIST_URL" | jq -r --arg KEY "$KEY" '.envs[]? | select(.key==$KEY) | .id' || true) + +if [[ -n "$EXISTING" ]]; then + # Delete existing to avoid conflicts + DEL_URL="$BASE_URL/v10/projects/$PROJECT_ID/env/$EXISTING${TEAM_QS}" + curl -fsSL -X DELETE -H "Authorization: Bearer $VERCEL_TOKEN" "$DEL_URL" >/dev/null || true +fi + +# Create new +IFS=',' read -r -a TARGETS_ARR <<< "$TARGETS_CSV" +TARGETS_JSON=$(printf '%s\n' "${TARGETS_ARR[@]}" | jq -R . | jq -s .) + +CREATE_URL="$BASE_URL/v10/projects/$PROJECT_ID/env${TEAM_QS}" +PAYLOAD=$(jq -n \ + --arg key "$KEY" \ + --arg value "$VALUE" \ + --argjson target "$TARGETS_JSON" \ + '{ key: $key, value: $value, target: $target, type: "plain" }') + +curl -fsSL -X POST \ + -H "Authorization: Bearer $VERCEL_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$PAYLOAD" \ + "$CREATE_URL" >/dev/null + +echo "Upserted $KEY -> [$TARGETS_CSV] on project $PROJECT_ID" + diff --git a/tests/e2e/admin.apps.spec.ts b/tests/e2e/admin.apps.spec.ts new file mode 100644 index 0000000..2822422 --- /dev/null +++ b/tests/e2e/admin.apps.spec.ts @@ -0,0 +1,21 @@ +import { test, expect, request } from '@playwright/test' + +const MEDUSA_BASE_URL = process.env.MEDUSA_BASE_URL +const STRAPI_BASE_URL = process.env.STRAPI_BASE_URL + +test('smoke: strapi admin login page responds', async () => { + test.skip(!STRAPI_BASE_URL, 'STRAPI_BASE_URL not set') + const api = await request.newContext() + const res = await api.get(`${STRAPI_BASE_URL.replace(/\/$/, '')}/admin`) + // Strapi usually serves login HTML with 200 when unauthenticated + expect([200, 302, 401]).toContain(res.status()) +}) + +test('smoke: medusa admin app entry is reachable', async ({ page }) => { + test.skip(!MEDUSA_BASE_URL, 'MEDUSA_BASE_URL not set') + // Medusa admin app is served at /app/admin + const url = `${MEDUSA_BASE_URL!.replace(/\/$/, '')}/app/admin` + const res = await page.goto(url) + expect(res?.ok(), 'GET /app/admin should return 2xx').toBeTruthy() +}) + diff --git a/tests/e2e/health.postdeploy.spec.ts b/tests/e2e/health.postdeploy.spec.ts new file mode 100644 index 0000000..d9feb55 --- /dev/null +++ b/tests/e2e/health.postdeploy.spec.ts @@ -0,0 +1,20 @@ +import { test, expect, request } from '@playwright/test' + +const MEDUSA_BASE_URL = process.env.MEDUSA_BASE_URL +const STRAPI_BASE_URL = process.env.STRAPI_BASE_URL + +test('postdeploy: medusa /health returns success', async () => { + test.skip(!MEDUSA_BASE_URL, 'MEDUSA_BASE_URL not set') + const api = await request.newContext() + const res = await api.get(`${MEDUSA_BASE_URL.replace(/\/$/, '')}/health`) + expect(res.status(), 'Medusa /health should be 200').toBe(200) +}) + +test('postdeploy: strapi /_health returns success', async () => { + test.skip(!STRAPI_BASE_URL, 'STRAPI_BASE_URL not set') + const api = await request.newContext() + const res = await api.get(`${STRAPI_BASE_URL.replace(/\/$/, '')}/_health`) + // Accept 200 or 204 depending on implementation + expect([200, 204]).toContain(res.status()) +}) + diff --git a/tests/e2e/shop.pdp.smoke.spec.ts b/tests/e2e/shop.pdp.smoke.spec.ts new file mode 100644 index 0000000..3192e5d --- /dev/null +++ b/tests/e2e/shop.pdp.smoke.spec.ts @@ -0,0 +1,19 @@ +import { test, expect } from '@playwright/test' + +test('smoke: shop page returns 200 and lists products', async ({ page, request }) => { + const res = await request.get('/shop') + expect(res.status()).toBe(200) + await page.goto('/shop') + const items = page.locator('a[href^="/product/"]') + await expect(items.first()).toBeVisible() +}) + +test('smoke: two PDP pages return 200 from links on /shop', async ({ page, request }) => { + await page.goto('/shop') + const links = await page.$$eval('a[href^="/product/"]', els => els.slice(0,2).map(e => (e as HTMLAnchorElement).getAttribute('href'))) + for (const href of links) { + if (!href) continue + const res = await request.get(href) + expect(res.status(), `GET ${href}`).toBe(200) + } +}) diff --git a/tests/e2e/smoke.home.spec.ts b/tests/e2e/smoke.home.spec.ts new file mode 100644 index 0000000..319b611 --- /dev/null +++ b/tests/e2e/smoke.home.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '@playwright/test' + +test('smoke: storefront home loads', async ({ page }) => { + const res = await page.goto('/') + expect(res?.ok(), 'GET / should return 2xx').toBeTruthy() + // Basic sanity: page has a title or rendered content + const title = await page.title() + expect(title).toBeDefined() +}) + diff --git a/turbo.json b/turbo.json index 9ba5bd5..6bbff8a 100644 --- a/turbo.json +++ b/turbo.json @@ -4,6 +4,9 @@ "lint": { "outputs": [] }, + "typecheck": { + "outputs": [] + }, "test:unit": { "outputs": [] }, diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..9364466 --- /dev/null +++ b/vercel.json @@ -0,0 +1,6 @@ +{ + "framework": "nextjs", + "installCommand": "bash ./vercel-install.sh", + "buildCommand": "pnpm run build", + "outputDirectory": ".vercel/output" +}