diff --git a/.env.example b/.env.example
index 10ee5100..3afaa789 100644
--- a/.env.example
+++ b/.env.example
@@ -1,24 +1,45 @@
-# Next Auth
+# Server Configuration
+PORT=3001
+HOST=0.0.0.0
+NODE_ENV=development
+LOG_LEVEL=info
+CORS_ORIGIN=http://localhost:3000
+
+# Authentication Configuration
# You can generate a new secret on the command line with:
-# `npm exec auth secret`
-# OR
-# `openssl rand -base64 32``
-# https://next-auth.js.org/configuration/options#secret
+# `openssl rand -base64 32`
AUTH_SECRET=
AUTH_URL=http://localhost:3000
-# API token details
+# API token details (optional - if not set, no auth required)
API_TOKEN='my-api-token'
UI_AUTH_EXPIRE_HOURS='2'
-# Storage details
+# Storage Configuration
DATA_STORAGE=fs # could be s3
+DATA_DIR=./data
+
+# Frontend Configuration (for production)
+FRONTEND_DIST=./frontend/dist
-# S3 related configuration if DATA_STORAGE is "s3"
-S3_ENDPOINT="s3.endpoint",
+# S3 Configuration (if DATA_STORAGE is "s3")
+S3_ENDPOINT="s3.endpoint"
S3_ACCESS_KEY="some_access_key"
S3_SECRET_KEY="some_secret_key"
S3_PORT=9000 # optional
S3_REGION="us-east-1"
-S3_BUCKET="bucket_name" # by default "playwright-reports-server"
-S3_BATCH_SIZE=10 # by default 10
\ No newline at end of file
+S3_BUCKET="playwright-reports-server" # default
+S3_BATCH_SIZE=10 # default
+S3_MULTIPART_CHUNK_SIZE_MB=25 # default
+
+# Jira Integration (optional)
+JIRA_BASE_URL=
+JIRA_EMAIL=
+JIRA_API_TOKEN=
+JIRA_PROJECT_KEY=
+
+# Data Cleanup Configuration (optional)
+RESULT_EXPIRE_DAYS=
+RESULT_EXPIRE_CRON_SCHEDULE="33 3 * * *"
+REPORT_EXPIRE_DAYS=
+REPORT_EXPIRE_CRON_SCHEDULE="44 4 * * *"
\ No newline at end of file
diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index af6ab76f..00000000
--- a/.eslintignore
+++ /dev/null
@@ -1,20 +0,0 @@
-.now/*
-*.css
-.changeset
-dist
-esm/*
-public/*
-tests/*
-scripts/*
-*.config.js
-.DS_Store
-node_modules
-coverage
-.next
-build
-!.commitlintrc.cjs
-!.lintstagedrc.cjs
-!jest.config.js
-!plopfile.js
-!react-shim.js
-!tsup.config.ts
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index d2fbabe5..00000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,83 +0,0 @@
-{
- "$schema": "https://json.schemastore.org/eslintrc.json",
- "env": {
- "browser": false,
- "es2021": true,
- "node": true
- },
- "extends": [
- "plugin:react/recommended",
- "plugin:prettier/recommended",
- "plugin:react-hooks/recommended",
- "plugin:jsx-a11y/recommended"
- ],
- "plugins": ["react", "unused-imports", "import", "@typescript-eslint", "jsx-a11y", "prettier"],
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "ecmaFeatures": {
- "jsx": true
- },
- "ecmaVersion": 12,
- "sourceType": "module"
- },
- "settings": {
- "react": {
- "version": "detect"
- }
- },
- "rules": {
- "no-console": "off",
- "react/prop-types": "off",
- "react/jsx-uses-react": "off",
- "react/react-in-jsx-scope": "off",
- "react-hooks/exhaustive-deps": "off",
- "jsx-a11y/click-events-have-key-events": "warn",
- "jsx-a11y/interactive-supports-focus": "warn",
- "prettier/prettier": "warn",
- "no-unused-vars": "off",
- "unused-imports/no-unused-vars": "off",
- "unused-imports/no-unused-imports": "warn",
- "@typescript-eslint/no-unused-vars": [
- "warn",
- {
- "args": "after-used",
- "ignoreRestSiblings": false,
- "argsIgnorePattern": "^_.*?$"
- }
- ],
- "import/order": [
- "warn",
- {
- "groups": ["type", "builtin", "object", "external", "internal", "parent", "sibling", "index"],
- "pathGroups": [
- {
- "pattern": "~/**",
- "group": "external",
- "position": "after"
- }
- ],
- "newlines-between": "always"
- }
- ],
- "react/self-closing-comp": "warn",
- "react/jsx-sort-props": [
- "warn",
- {
- "callbacksLast": true,
- "shorthandFirst": true,
- "noSortAlphabetically": false,
- "reservedFirst": true
- }
- ],
- "padding-line-between-statements": [
- "warn",
- { "blankLine": "always", "prev": "*", "next": "return" },
- { "blankLine": "always", "prev": ["const", "let", "var"], "next": "*" },
- {
- "blankLine": "any",
- "prev": ["const", "let", "var"],
- "next": ["const", "let", "var"]
- }
- ]
- }
-}
diff --git a/.gitignore b/.gitignore
index 93df80f9..bd91d914 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,21 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# data
-/data
-.tmp
+**/data
+**/.tmp
# dependencies
-/node_modules
+**/node_modules
/.pnp
.pnp.js
# testing
/coverage
-
-# next.js
-.next/
-/out/
+**/test-results
# production
-/build
+**/build
+**/dist
# misc
.DS_Store
@@ -38,8 +36,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
-data/reports
-data/results
# vscode
.vscode
diff --git a/.prettierignore b/.prettierignore
deleted file mode 100644
index 746669b4..00000000
--- a/.prettierignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.npm/
-node_modules/
-.eslintignore
-.prettierignore
-package.json
-package-lock.json
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index 957a15d6..00000000
--- a/.prettierrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "semi": true,
- "trailingComma": "all",
- "singleQuote": true,
- "printWidth": 120,
- "tabWidth": 2,
- "arrowParens": "always",
- "endOfLine": "lf"
-}
diff --git a/Dockerfile b/Dockerfile
index 009363db..2c13e833 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,35 +1,58 @@
FROM node:22-alpine AS base
-# Install dependencies only when needed
+# Install all dependencies for monorepo
FROM base AS deps
-# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
-RUN apk add --no-cache libc6-compat
WORKDIR /app
-
-# Install dependencies based on the preferred package manager
COPY package.json package-lock.json* ./
RUN npm ci
-# Rebuild the source code only when needed
-FROM base AS builder
-WORKDIR /app
-COPY --from=deps /app/node_modules ./node_modules
-COPY . .
-
-ARG API_BASE_PATH=""
-ENV API_BASE_PATH=$API_BASE_PATH
+# Install dependencies for backend
+FROM base AS backend-deps
+RUN apk add --no-cache libc6-compat
-ARG ASSETS_BASE_PATH=""
-ENV ASSETS_BASE_PATH=$ASSETS_BASE_PATH
+# Install dependencies for frontend
-# Next.js collects completely anonymous telemetry data about general usage.
-# Learn more here: https://nextjs.org/telemetry
-# Uncomment the following line in case you want to disable telemetry during the build.
-ENV NEXT_TELEMETRY_DISABLED=1
+# Build shared package first
+FROM base AS shared-builder
+WORKDIR /app/packages/shared
+COPY --from=deps /app/node_modules ./node_modules
+COPY packages/shared/ .
+RUN npm run build
+# Build frontend
+FROM base AS frontend-builder
+WORKDIR /app/apps/frontend
+COPY --from=deps /app/node_modules ./node_modules
+COPY --from=shared-builder /app/packages/shared ./packages/shared
+COPY apps/frontend/ .
+# Remove the shared dependency from package.json temporarily to avoid npm trying to install it
+RUN sed -i '/"@playwright-reports\/shared":/d' package.json
+# Create symlink for shared package in node_modules for TypeScript resolution
+RUN mkdir -p ./node_modules/@playwright-reports \
+ ln -sf ../../packages/shared ./node_modules/@playwright-reports/shared
+# Install missing rollup native dependency if needed
+RUN npm install @rollup/rollup-linux-arm64-musl --no-save || true
+# Skip TypeScript checks and just build with Vite
+ENV DOCKER_BUILD=true
+RUN npm run build:vite
+
+# Build backend
+FROM base AS backend-builder
+WORKDIR /app/apps/backend
+COPY --from=deps /app/node_modules ./node_modules
+COPY --from=shared-builder /app/packages/shared ./packages/shared
+COPY apps/backend/ .
+# Create symlink for shared package in node_modules for TypeScript resolution
+RUN mkdir -p ./node_modules/@playwright-reports \
+ ln -sf ../../packages/shared ./node_modules/@playwright-reports/shared
+# Build first with dev dependencies
RUN npm run build
+# Remove the shared dependency from package.json temporarily to avoid npm trying to install it
+RUN sed -i '/"@playwright-reports\/shared":/d' package.json
+# Install backend production dependencies (removes dev dependencies)
+RUN npm ci --only=production
-# Production image, copy all the files and run next
+# Production image
FROM base AS runner
WORKDIR /app
@@ -37,22 +60,30 @@ ENV NODE_ENV=production
RUN apk add --no-cache curl
-# Uncomment the following line in case you want to disable telemetry during runtime.
-# ENV NEXT_TELEMETRY_DISABLED 1
-
RUN addgroup --system --gid 1001 nodejs && \
- adduser --system --uid 1001 --ingroup nodejs nextjs
+ adduser --system --uid 1001 --ingroup nodejs appuser
+
+# Copy all node_modules from deps
+COPY --from=deps --chown=appuser:nodejs /app/node_modules ./node_modules
-COPY --from=builder --chown=nextjs:nodejs /app/public ./public
+# Copy backend node_modules for backend-specific dependencies
+COPY --from=backend-builder --chown=appuser:nodejs /app/apps/backend/node_modules ./apps/backend/node_modules
-# Set the correct permission for prerender cache
-RUN mkdir .next && \
- chown nextjs:nodejs .next
+# Copy backend build
+COPY --from=backend-builder --chown=appuser:nodejs /app/apps/backend/dist ./apps/backend/dist
+COPY --from=backend-builder --chown=appuser:nodejs /app/apps/backend/package.json ./apps/backend/package.json
-# Automatically leverage output traces to reduce image size
-# https://nextjs.org/docs/advanced-features/output-file-tracing
-COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
-COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+# Copy frontend build
+COPY --from=frontend-builder --chown=appuser:nodejs /app/apps/frontend/dist ./apps/frontend/dist
+
+# Copy shared build
+COPY --from=shared-builder --chown=appuser:nodejs /app/packages/shared/dist ./packages/shared/dist
+
+# Copy public assets
+COPY --from=frontend-builder --chown=appuser:nodejs /app/apps/frontend/public ./frontend/public
+
+# Copy environment configuration (for default values)
+COPY --chown=appuser:nodejs .env.example /app/.env.example
# Create folders required for storing results and reports
ARG DATA_DIR=/app/data
@@ -60,16 +91,18 @@ ARG RESULTS_DIR=${DATA_DIR}/results
ARG REPORTS_DIR=${DATA_DIR}/reports
ARG TEMP_DIR=/app/.tmp
RUN mkdir -p ${DATA_DIR} ${RESULTS_DIR} ${REPORTS_DIR} ${TEMP_DIR} && \
- chown -R nextjs:nodejs ${DATA_DIR} ${TEMP_DIR}
+ chown -R appuser:nodejs ${DATA_DIR} ${TEMP_DIR}
+
+USER appuser
-USER nextjs
+EXPOSE 3001
-EXPOSE 3000
+ENV PORT=3001
+ENV FRONTEND_DIST=/app/apps/frontend/dist
-ENV PORT=3000
+WORKDIR /app/apps/backend
-# server.js is created by next build from the standalone output
-# https://nextjs.org/docs/pages/api-reference/next-config-js/output
-CMD ["sh", "-c", "HOSTNAME=0.0.0.0 node server.js"]
+CMD ["node", "dist/index.js"]
-HEALTHCHECK --interval=3m --timeout=3s CMD curl -f http://localhost:$PORT/api/ping || exit 1
+HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
+ CMD curl -f http://localhost:$PORT/api/ping || exit 1
\ No newline at end of file
diff --git a/app/api/[...nextauth]/route.ts b/app/api/[...nextauth]/route.ts
deleted file mode 100644
index bfc6e673..00000000
--- a/app/api/[...nextauth]/route.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { handlers } from '@/app/auth';
-
-export const { GET, POST } = handlers;
diff --git a/app/api/config/route.ts b/app/api/config/route.ts
deleted file mode 100644
index d5347380..00000000
--- a/app/api/config/route.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-import fs from 'node:fs/promises';
-import path from 'node:path';
-
-import { revalidatePath } from 'next/cache';
-
-import { withError } from '@/app/lib/withError';
-import { DATA_FOLDER } from '@/app/lib/storage/constants';
-import { service } from '@/app/lib/service';
-import { JiraService } from '@/app/lib/service/jira';
-import { env } from '@/app/config/env';
-import { cronService } from '@/app/lib/service/cron';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-const saveFile = async (file: File) => {
- const arrayBuffer = await file.arrayBuffer();
-
- const buffer = Buffer.from(arrayBuffer);
-
- await fs.writeFile(path.join(DATA_FOLDER, file.name), buffer, { encoding: 'binary' });
-};
-
-const parseHeaderLinks = async (headerLinks: string): Promise> => {
- return JSON.parse(headerLinks);
-};
-
-export async function PATCH(request: Request) {
- const { result: formData, error: formParseError } = await withError(request.formData());
-
- if (formParseError) {
- return Response.json({ error: formParseError.message }, { status: 400 });
- }
-
- if (!formData) {
- return Response.json({ error: 'Form data is missing' }, { status: 400 });
- }
-
- const logo = formData.get('logo') as File;
-
- if (logo) {
- const { error: logoError } = await withError(saveFile(logo));
-
- if (logoError) {
- return Response.json({ error: `failed to save logo: ${logoError?.message}` }, { status: 500 });
- }
- }
-
- const favicon = formData.get('favicon') as File;
-
- if (favicon) {
- const { error: faviconError } = await withError(saveFile(favicon));
-
- if (faviconError) {
- return Response.json({ error: `failed to save favicon: ${faviconError?.message}` }, { status: 500 });
- }
- }
-
- const title = formData.get('title');
- const logoPath = formData.get('logoPath');
- const faviconPath = formData.get('faviconPath');
- const reporterPaths = formData.get('reporterPaths');
- const headerLinks = formData.get('headerLinks');
- const jiraBaseUrl = formData.get('jiraBaseUrl');
- const jiraEmail = formData.get('jiraEmail');
- const jiraApiToken = formData.get('jiraApiToken');
- const jiraProjectKey = formData.get('jiraProjectKey');
- const resultExpireDays = formData.get('resultExpireDays');
- const resultExpireCronSchedule = formData.get('resultExpireCronSchedule');
- const reportExpireDays = formData.get('reportExpireDays');
- const reportExpireCronSchedule = formData.get('reportExpireCronSchedule');
-
- const config = await service.getConfig();
-
- if (!config) {
- return Response.json({ error: `failed to get config` }, { status: 500 });
- }
-
- if (title !== null) {
- config.title = title.toString();
- }
-
- if (logo) {
- config.logoPath = `/${logo.name}`;
- } else if (logoPath !== null) {
- config.logoPath = logoPath.toString();
- }
-
- if (favicon) {
- config.faviconPath = `/${favicon.name}`;
- } else if (faviconPath !== null) {
- config.faviconPath = faviconPath.toString();
- }
-
- if (reporterPaths !== null) {
- try {
- config.reporterPaths = JSON.parse(reporterPaths.toString());
- } catch {
- config.reporterPaths = [reporterPaths.toString()];
- }
- }
-
- if (headerLinks) {
- const { result: parsedHeaderLinks, error: parseHeaderLinksError } = await withError(
- parseHeaderLinks(headerLinks.toString()),
- );
-
- if (parseHeaderLinksError) {
- return Response.json(
- { error: `failed to parse header links: ${parseHeaderLinksError.message}` },
- { status: 400 },
- );
- }
-
- if (parsedHeaderLinks) config.headerLinks = parsedHeaderLinks;
- }
-
- if (!config.jira) {
- config.jira = {};
- }
-
- if (jiraBaseUrl !== null) config.jira.baseUrl = jiraBaseUrl.toString();
- if (jiraEmail !== null) config.jira.email = jiraEmail.toString();
- if (jiraApiToken !== null) config.jira.apiToken = jiraApiToken.toString();
- if (jiraProjectKey !== null) config.jira.projectKey = jiraProjectKey.toString();
-
- if (jiraBaseUrl || jiraEmail || jiraApiToken || jiraProjectKey) {
- JiraService.resetInstance();
- }
-
- if (!config.cron) {
- config.cron = {};
- }
-
- if (resultExpireDays || resultExpireCronSchedule || reportExpireDays || reportExpireCronSchedule) {
- if (resultExpireDays !== null) {
- config.cron.resultExpireDays = parseInt(resultExpireDays.toString());
- }
- if (resultExpireCronSchedule !== null) {
- config.cron.resultExpireCronSchedule = resultExpireCronSchedule.toString();
- }
- if (reportExpireDays !== null) {
- config.cron.reportExpireDays = parseInt(reportExpireDays.toString());
- }
- if (reportExpireCronSchedule !== null) {
- config.cron.reportExpireCronSchedule = reportExpireCronSchedule.toString();
- }
- }
-
- const { error: saveConfigError } = await withError(service.updateConfig(config));
-
- if (saveConfigError) {
- return Response.json({ error: `failed to save config: ${saveConfigError.message}` }, { status: 500 });
- }
-
- if (
- config.cron?.resultExpireDays ||
- config.cron?.resultExpireCronSchedule ||
- config.cron?.reportExpireDays ||
- config.cron?.reportExpireCronSchedule
- ) {
- await cronService.restart();
- }
-
- revalidatePath('/', 'layout');
- revalidatePath('/login', 'layout');
-
- return Response.json({ message: 'config saved' });
-}
-
-export async function GET() {
- const config = await service.getConfig();
-
- if (!config) {
- return Response.json({ error: 'Config not found' }, { status: 404 });
- }
-
- // Add environment info to config response
- const envInfo = {
- authRequired: !!env.API_TOKEN,
- serverCache: env.USE_SERVER_CACHE,
- dataStorage: env.DATA_STORAGE,
- s3Endpoint: env.S3_ENDPOINT,
- s3Bucket: env.S3_BUCKET,
- };
-
- return Response.json({ ...config, ...envInfo }, { status: 200 });
-}
diff --git a/app/api/info/route.ts b/app/api/info/route.ts
deleted file mode 100644
index c1f42c77..00000000
--- a/app/api/info/route.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET() {
- const { result, error } = await withError(service.getServerInfo());
-
- if (error) {
- return Response.json({ error: error.message }, { status: 500 });
- }
-
- return Response.json(result);
-}
diff --git a/app/api/jira/config/route.ts b/app/api/jira/config/route.ts
deleted file mode 100644
index bf0e308b..00000000
--- a/app/api/jira/config/route.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { JiraService } from '@/app/lib/service/jira';
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic';
-
-export async function GET() {
- try {
- const config = await service.getConfig();
- const jiraConfig = config.jira;
-
- const isConfigured = !!(jiraConfig?.baseUrl && jiraConfig?.email && jiraConfig?.apiToken);
-
- if (!isConfigured) {
- return Response.json({
- configured: false,
- message: 'Jira is not configured. Please configure Jira settings in the admin panel.',
- config: jiraConfig || {},
- });
- }
-
- const jiraService = JiraService.getInstance(jiraConfig);
-
- let issueTypes = [];
-
- if (jiraConfig?.projectKey) {
- try {
- const project = await jiraService.getProject(jiraConfig.projectKey);
-
- issueTypes = project.issueTypes || [];
- } catch (error) {
- console.warn(`Could not fetch project-specific issue types for ${jiraConfig.projectKey}:`, error);
- }
- }
-
- return Response.json({
- configured: true,
- baseUrl: jiraConfig.baseUrl,
- defaultProjectKey: jiraConfig.projectKey,
- issueTypes: issueTypes.map((type: any) => ({
- id: type.id,
- name: type.name,
- description: type.description,
- })),
- });
- } catch (error) {
- return Response.json(
- {
- configured: false,
- error: `Failed to connect to Jira: ${error instanceof Error ? error.message : 'Unknown error'}`,
- },
- { status: 500 },
- );
- }
-}
diff --git a/app/api/jira/create-ticket/route.ts b/app/api/jira/create-ticket/route.ts
deleted file mode 100644
index 3ddc4b5f..00000000
--- a/app/api/jira/create-ticket/route.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { withError } from '@/app/lib/withError';
-import { JiraService } from '@/app/lib/service/jira';
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic';
-
-interface CreateTicketRequest {
- summary: string;
- description: string;
- issueType: string;
- projectKey: string;
- testId: string;
- testTitle: string;
- testOutcome: string;
- testLocation: {
- file: string;
- line: number;
- column: number;
- };
- reportId: string;
- testAttachments?: Array<{
- name: string;
- path: string;
- contentType: string;
- }>;
-}
-
-export async function POST(request: Request) {
- const { result: data, error: parseError } = await withError(request.json());
-
- if (parseError) {
- return Response.json({ error: parseError.message }, { status: 400 });
- }
-
- if (!data) {
- return Response.json({ error: 'Request data is missing' }, { status: 400 });
- }
-
- const ticketData = data as CreateTicketRequest;
-
- try {
- const report = await service.getReport(ticketData.reportId);
- const projectPath = report.project ? `${report.project}/` : '';
-
- ticketData.testAttachments = ticketData.testAttachments?.map((att) => ({
- ...att,
- path: `${projectPath}${ticketData.reportId}/${att.path}`,
- }));
- } catch (error) {
- console.error(`Failed to get report ${ticketData.reportId}:`, error);
- }
-
- try {
- if (!ticketData.summary || !ticketData.projectKey) {
- return Response.json(
- {
- error: 'Summary and project key are required',
- },
- { status: 400 },
- );
- }
-
- const config = await service.getConfig();
- const jiraService = JiraService.getInstance(config.jira);
-
- const jiraResponse = await jiraService.createIssue(
- ticketData.summary,
- ticketData.description,
- ticketData.issueType,
- ticketData.projectKey,
- {
- testId: ticketData.testId,
- testTitle: ticketData.testTitle,
- testOutcome: ticketData.testOutcome,
- testLocation: ticketData.testLocation,
- },
- ticketData.testAttachments,
- );
-
- return Response.json(
- {
- success: true,
- issueKey: jiraResponse.key,
- issueId: jiraResponse.id,
- issueUrl: jiraResponse.self,
- message: 'Jira ticket created successfully',
- data: {
- ...ticketData,
- issueKey: jiraResponse.key,
- issueId: jiraResponse.id,
- issueUrl: jiraResponse.self,
- created: new Date().toISOString(),
- },
- },
- { status: 201 },
- );
- } catch (error) {
- console.error('Failed to create Jira ticket:', error);
-
- if (error instanceof Error && error.message.includes('Jira configuration is incomplete')) {
- return Response.json(
- {
- error:
- 'Jira is not configured. Please set up JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.',
- },
- { status: 500 },
- );
- }
-
- return Response.json(
- {
- error: `Failed to create Jira ticket: ${error instanceof Error ? error.message : 'Unknown error'}`,
- },
- { status: 500 },
- );
- }
-}
diff --git a/app/api/ping/route.ts b/app/api/ping/route.ts
deleted file mode 100644
index 24d07811..00000000
--- a/app/api/ping/route.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET() {
- return new Response('pong');
-}
diff --git a/app/api/report/[id]/route.ts b/app/api/report/[id]/route.ts
deleted file mode 100644
index 427d9296..00000000
--- a/app/api/report/[id]/route.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { type NextRequest } from 'next/server';
-
-import { withError } from '@/app/lib/withError';
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET(
- req: NextRequest,
- {
- params,
- }: {
- params: {
- id: string;
- };
- },
-) {
- const { id } = params;
-
- if (!id) {
- return new Response('report ID is required', { status: 400 });
- }
-
- const { result: report, error } = await withError(service.getReport(id));
-
- if (error) {
- return new Response(`failed to get report: ${error?.message ?? 'unknown error'}`, { status: 400 });
- }
-
- return Response.json(report);
-}
diff --git a/app/api/report/delete/route.ts b/app/api/report/delete/route.ts
deleted file mode 100644
index e9656991..00000000
--- a/app/api/report/delete/route.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-export async function DELETE(request: Request) {
- const { result: reqData, error: reqError } = await withError(request.json());
-
- if (reqError) {
- return new Response(reqError.message, { status: 400 });
- }
-
- const { error } = await withError(service.deleteReports(reqData.reportsIds));
-
- if (error) {
- return new Response(error.message, { status: 404 });
- }
-
- return Response.json({
- message: `Reports deleted successfully`,
- reportsIds: reqData.reportsIds,
- });
-}
diff --git a/app/api/report/generate/route.ts b/app/api/report/generate/route.ts
deleted file mode 100644
index afb8c379..00000000
--- a/app/api/report/generate/route.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function POST(request: Request) {
- const { result: reqBody, error: reqError } = await withError(request.json());
-
- if (reqError) {
- return new Response(reqError.message, { status: 400 });
- }
- const { resultsIds, project, playwrightVersion, ...rest } = reqBody;
-
- try {
- const result = await service.generateReport(resultsIds, { project, playwrightVersion, ...rest });
-
- if (!result?.reportId) {
- return new Response('failed to generate report', { status: 400 });
- }
-
- return Response.json(result);
- } catch (error) {
- console.error(`[report/generate] error: ${error}`);
- if (error instanceof Error && error.message.includes('ENOENT: no such file or directory')) {
- return Response.json({ error: `ResultID with not found: ${error.message}` }, { status: 404 });
- }
-
- return Response.json({ error: (error as Error).message }, { status: 500 });
- }
-}
diff --git a/app/api/report/list/route.ts b/app/api/report/list/route.ts
deleted file mode 100644
index ad216c87..00000000
--- a/app/api/report/list/route.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { type NextRequest } from 'next/server';
-
-import { withError } from '@/app/lib/withError';
-import { parseFromRequest } from '@/app/lib/storage/pagination';
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET(request: NextRequest) {
- const { searchParams } = new URL(request.url);
- const pagination = parseFromRequest(searchParams);
- const project = searchParams.get('project') ?? '';
- const search = searchParams.get('search') ?? '';
-
- const { result: reports, error } = await withError(service.getReports({ pagination, project, search }));
-
- if (error) {
- return new Response(error.message, { status: 400 });
- }
-
- return Response.json(reports!);
-}
diff --git a/app/api/report/projects/route.ts b/app/api/report/projects/route.ts
deleted file mode 100644
index 836cd7a1..00000000
--- a/app/api/report/projects/route.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET() {
- const { result: projects, error } = await withError(service.getReportsProjects());
-
- if (error) {
- return new Response(error.message, { status: 400 });
- }
-
- return Response.json(projects);
-}
diff --git a/app/api/report/trend/route.ts b/app/api/report/trend/route.ts
deleted file mode 100644
index 1bda6a47..00000000
--- a/app/api/report/trend/route.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { type NextRequest } from 'next/server';
-
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET(request: NextRequest) {
- const { searchParams } = new URL(request.url);
- const project = searchParams.get('project') ?? '';
- const { reports } = await service.getReports({ project });
-
- const latestReports = reports.slice(0, 20);
-
- return Response.json(latestReports);
-}
diff --git a/app/api/result/delete/route.ts b/app/api/result/delete/route.ts
deleted file mode 100644
index 46eda0d4..00000000
--- a/app/api/result/delete/route.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function DELETE(request: Request) {
- const { result: reqData, error: reqError } = await withError(request.json());
-
- if (reqError) {
- return new Response(reqError.message, { status: 400 });
- }
-
- const { error } = await withError(service.deleteResults(reqData.resultsIds));
-
- if (error) {
- return new Response(error.message, { status: 404 });
- }
-
- return Response.json({
- message: `Results files deleted successfully`,
- resultsIds: reqData.resultsIds,
- });
-}
diff --git a/app/api/result/list/route.ts b/app/api/result/list/route.ts
deleted file mode 100644
index 24515fb7..00000000
--- a/app/api/result/list/route.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { type NextRequest } from 'next/server';
-
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-import { parseFromRequest } from '@/app/lib/storage/pagination';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET(request: NextRequest) {
- const { searchParams } = new URL(request.url);
- const pagination = parseFromRequest(searchParams);
- const project = searchParams.get('project') ?? '';
- const tags = searchParams.get('tags')?.split(',').filter(Boolean) ?? [];
- const search = searchParams.get('search') ?? '';
-
- const { result, error } = await withError(service.getResults({ pagination, project, tags, search }));
-
- if (error) {
- return new Response(error.message, { status: 400 });
- }
-
- return Response.json(result);
-}
diff --git a/app/api/result/projects/route.ts b/app/api/result/projects/route.ts
deleted file mode 100644
index 1548a20e..00000000
--- a/app/api/result/projects/route.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { service } from '@/app/lib/service';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-export async function GET() {
- const { result: projects, error } = await withError(service.getResultsProjects());
-
- if (error) {
- return new Response(error.message, { status: 400 });
- }
-
- return Response.json(projects);
-}
diff --git a/app/api/result/tags/route.ts b/app/api/result/tags/route.ts
deleted file mode 100644
index 1439896e..00000000
--- a/app/api/result/tags/route.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { NextRequest } from 'next/server';
-
-import { withError } from '@/app/lib/withError';
-import { service } from '@/app/lib/service';
-
-export const dynamic = 'force-dynamic';
-
-export async function GET(request: NextRequest) {
- const { searchParams } = new URL(request.url);
- const project = searchParams.get('project') ?? '';
-
- const { result: tags, error } = await withError(service.getResultsTags(project));
-
- if (error) {
- return new Response(error.message, { status: 400 });
- }
-
- return Response.json(tags);
-}
diff --git a/app/api/serve/[[...filePath]]/route.ts b/app/api/serve/[[...filePath]]/route.ts
deleted file mode 100644
index ad7e4332..00000000
--- a/app/api/serve/[[...filePath]]/route.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import path from 'path';
-
-import mime from 'mime';
-import { type NextRequest, NextResponse } from 'next/server';
-import { redirect } from 'next/navigation';
-
-import { withError } from '@/app/lib/withError';
-import { storage } from '@/app/lib/storage';
-import { auth } from '@/app/auth';
-import { env } from '@/app/config/env';
-import { withBase } from '@/app/lib/url';
-
-interface ReportParams {
- reportId: string;
- filePath?: string[];
-}
-
-export async function GET(
- req: NextRequest,
- {
- params,
- }: {
- params: ReportParams;
- },
-) {
- // is not protected by the middleware
- // as we want to have callbackUrl in the query
-
- const authRequired = !!env.API_TOKEN;
- const session = await auth();
-
- const { filePath } = params;
-
- const uriPath = Array.isArray(filePath) ? filePath.join('/') : (filePath ?? '');
-
- const targetPath = decodeURI(uriPath);
-
- // Only check for session if auth is required
- if (authRequired && !session?.user?.jwtToken) {
- redirect(withBase(`/login?callbackUrl=${encodeURI(req.nextUrl.pathname)}`));
- }
-
- const contentType = mime.getType(path.basename(targetPath));
-
- if (!contentType && !path.extname(targetPath)) {
- return NextResponse.next();
- }
-
- const { result: content, error } = await withError(storage.readFile(targetPath, contentType));
-
- if (error ?? !content) {
- return NextResponse.json({ error: `Could not read file ${error?.message ?? ''}` }, { status: 404 });
- }
-
- const headers = {
- headers: {
- 'Content-Type': contentType ?? 'application/octet-stream',
- Authorization: `Bearer ${session?.user?.apiToken}`,
- },
- };
-
- return new Response(content, headers);
-}
diff --git a/app/api/static/[[...filePath]]/route.ts b/app/api/static/[[...filePath]]/route.ts
deleted file mode 100644
index 28ea2306..00000000
--- a/app/api/static/[[...filePath]]/route.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import path from 'node:path';
-import fs from 'node:fs/promises';
-
-import mime from 'mime';
-import { type NextRequest, NextResponse } from 'next/server';
-
-import { DATA_FOLDER } from '@/app/lib/storage/constants';
-import { withError } from '@/app/lib/withError';
-
-export const dynamic = 'force-dynamic'; // defaults to auto
-
-interface ServeParams {
- filePath?: string[];
-}
-
-export async function GET(
- _: NextRequest,
- {
- params,
- }: {
- params: ServeParams;
- },
-) {
- const { filePath } = params;
-
- const uriPath = Array.isArray(filePath) ? filePath.join('/') : (filePath ?? '');
-
- const targetPath = decodeURI(uriPath);
-
- const contentType = mime.getType(path.basename(targetPath));
-
- if (!contentType && !path.extname(targetPath)) {
- return NextResponse.next();
- }
-
- const imageDataPath = path.join(DATA_FOLDER, targetPath);
- const imagePublicPath = path.join('public', targetPath);
-
- const { error: dataAccessError } = await withError(fs.access(imageDataPath));
-
- const imagePath = dataAccessError ? imagePublicPath : imageDataPath;
-
- const imageBuffer = await fs.readFile(imagePath);
-
- const headers = {
- headers: {
- 'Content-Type': contentType ?? 'image/*',
- },
- };
-
- return new Response(imageBuffer, headers);
-}
diff --git a/app/auth.ts b/app/auth.ts
deleted file mode 100644
index a4427e6d..00000000
--- a/app/auth.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import NextAuth from 'next-auth';
-import { NextAuthConfig } from 'next-auth';
-import { type User } from 'next-auth';
-import CredentialsProvider from 'next-auth/providers/credentials';
-import jwt from 'jsonwebtoken';
-
-import { env } from './config/env';
-
-const useAuth = !!env.API_TOKEN;
-
-// strictly recommended to specify via env var
-// Use a stable default secret when AUTH_SECRET is not set to avoid JWT decryption errors
-// This is only acceptable when auth is disabled (no API_TOKEN)
-const secret = env.AUTH_SECRET ?? 'default-secret-for-non-auth-mode';
-
-// session expiration for api token auth
-const expirationHours = env.UI_AUTH_EXPIRE_HOURS ? parseInt(env.UI_AUTH_EXPIRE_HOURS) : 2;
-const expirationSeconds = expirationHours * 60 * 60;
-
-export const authConfig: NextAuthConfig = {
- secret,
- providers: [
- CredentialsProvider({
- name: 'API Token',
- credentials: {
- apiToken: { label: 'API Token', type: 'password' },
- },
- async authorize(credentials): Promise {
- if (credentials?.apiToken === env.API_TOKEN) {
- const token = jwt.sign({ authorized: true }, secret);
-
- return {
- apiToken: credentials.apiToken as string,
- jwtToken: token,
- };
- }
-
- return null;
- },
- }),
- ],
- callbacks: {
- async jwt({ token, user }) {
- if (user) {
- token.apiToken = user.apiToken;
- token.jwtToken = user.jwtToken;
- }
-
- return token;
- },
- async session({ session, token }) {
- session.user.apiToken = token.apiToken as string;
- session.user.jwtToken = token.jwtToken as string;
-
- return session;
- },
- },
- session: {
- strategy: 'jwt',
- maxAge: expirationSeconds,
- },
- trustHost: true,
- pages: {
- signIn: '/login',
- },
-};
-
-const getJwtStubToken = () => {
- return jwt.sign({ authorized: true }, secret);
-};
-
-const noAuth = {
- providers: [
- CredentialsProvider({
- name: 'No Auth',
- credentials: {},
- async authorize() {
- const token = getJwtStubToken();
-
- return { apiToken: token, jwtToken: token };
- },
- }),
- ],
- callbacks: {
- authorized: async () => {
- return true;
- },
- async jwt({ token, user }) {
- if (user) {
- token.apiToken = user.apiToken;
- token.jwtToken = user.jwtToken;
- }
-
- return token;
- },
- async session({ session, token }) {
- session.sessionToken = getJwtStubToken();
- session.user.jwtToken = session.sessionToken;
- session.user.apiToken = token.apiToken as string;
-
- return session;
- },
- },
- trustHost: true,
- session: {
- strategy: 'jwt',
- maxAge: expirationSeconds,
- },
- secret,
-} satisfies NextAuthConfig;
-
-export const { handlers, auth, signIn, signOut } = NextAuth(useAuth ? authConfig : noAuth);
diff --git a/app/components/page-layout.tsx b/app/components/page-layout.tsx
deleted file mode 100644
index 6b621161..00000000
--- a/app/components/page-layout.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-'use client';
-
-import { useLayoutEffect, useState, useEffect } from 'react';
-import { v4 as uuidv4 } from 'uuid';
-import { useSession } from 'next-auth/react';
-import { Spinner } from '@heroui/react';
-import { toast } from 'sonner';
-
-import useQuery from '@/app/hooks/useQuery';
-import { useAuthConfig } from '@/app/hooks/useAuthConfig';
-import { type ServerDataInfo } from '@/app/lib/storage';
-
-interface PageLayoutProps {
- render: (props: { info: ServerDataInfo; onUpdate: () => void }) => React.ReactNode;
-}
-
-export default function PageLayout({ render }: PageLayoutProps) {
- const { data: session, status } = useSession();
- const authIsLoading = status === 'loading';
- const { authRequired } = useAuthConfig();
-
- const { data: info, error, refetch, isLoading: isInfoLoading } = useQuery('/api/info');
- const [refreshId, setRefreshId] = useState(uuidv4());
-
- useEffect(() => {
- // Only show error if auth is required
- if (authRequired === false) {
- return;
- }
-
- if (!authIsLoading && !session && authRequired === true) {
- toast.error('You are not authenticated');
- }
- }, [authIsLoading, session, authRequired]);
-
- useLayoutEffect(() => {
- // Skip session check if auth is not required
- if (authRequired === false) {
- refetch();
-
- return;
- }
-
- if (authIsLoading || !session) {
- return;
- }
- refetch();
- }, [refreshId, session, authRequired]);
-
- if (authIsLoading || isInfoLoading) {
- return ;
- }
-
- const updateRefreshId = () => {
- setRefreshId(uuidv4());
- };
-
- if (error) {
- toast.error(error.message);
-
- return Error loading data: {error.message}
;
- }
-
- return (
- <>
- {!!info && (
-
-
{render({ info, onUpdate: updateRefreshId })}
-
- )}
- >
- );
-}
diff --git a/app/components/report-details/report-stats.tsx b/app/components/report-details/report-stats.tsx
deleted file mode 100644
index b37d0883..00000000
--- a/app/components/report-details/report-stats.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { FC } from 'react';
-
-import { StatChart } from '../stat-chart';
-
-import { type ReportStats } from '@/app/lib/parser';
-import { pluralize } from '@/app/lib/transformers';
-
-interface StatisticsProps {
- stats?: ReportStats;
-}
-
-const ReportStatistics: FC = ({ stats }) => {
- if (!stats || Object.keys(stats).length === 0) {
- return No statistics available
;
- }
-
- return (
-
-
- Total: {stats.total} {pluralize(stats.total, 'test', 'tests')}
-
-
-
- );
-};
-
-export default ReportStatistics;
diff --git a/app/components/report-details/test-info.tsx b/app/components/report-details/test-info.tsx
deleted file mode 100644
index dd312c65..00000000
--- a/app/components/report-details/test-info.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { FC } from 'react';
-import { Link, LinkIcon, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@heroui/react';
-
-import FormattedDate from '../date-format';
-
-import { subtitle } from '@/app/components/primitives';
-import { parseMilliseconds } from '@/app/lib/time';
-import { type TestHistory, type ReportHistory } from '@/app/lib/storage';
-import { type ReportTest } from '@/app/lib/parser/types';
-import { testStatusToColor } from '@/app/lib/tailwind';
-import { withBase } from '@/app/lib/url';
-
-interface TestInfoProps {
- history: ReportHistory[];
- test: ReportTest;
-}
-
-const getTestHistory = (testId: string, history: ReportHistory[]) => {
- return history
- .map((report) => {
- const file = report?.files?.find((file) => file.tests.some((test) => test.testId === testId));
-
- if (!file) {
- return;
- }
-
- const test = file.tests.find((test) => test.testId === testId);
-
- if (!test) {
- return;
- }
-
- return {
- ...test,
- createdAt: report.createdAt,
- reportID: report.reportID,
- reportUrl: report.reportUrl,
- };
- })
- .filter(Boolean) as unknown as TestHistory[];
-};
-
-const TestInfo: FC = ({ test, history }: TestInfoProps) => {
- const formatted = testStatusToColor(test.outcome);
-
- const testHistory = getTestHistory(test.testId, history);
-
- return (
-
-
-
- Outcome: {formatted.title}
-
-
Location: {`${test.location.file}:${test.location.line}:${test.location.column}`}
-
Duration: {parseMilliseconds(test.duration)}
- {test.annotations.length > 0 &&
Annotations: {test.annotations.map((a) => JSON.stringify(a)).join(', ')}
}
- {test.tags.length > 0 &&
Tags: {test.tags.join(', ')}
}
-
- {!!testHistory?.length && (
-
-
Results:
-
-
- Created At
- Status
- Duration
- Actions
-
-
- {(item) => {
- const itemOutcome = testStatusToColor(item?.outcome);
-
- return (
-
-
-
-
-
- {itemOutcome.title}
-
- {parseMilliseconds(item.duration)}
-
-
-
-
-
-
- );
- }}
-
-
-
- )}
-
- );
-};
-
-export default TestInfo;
diff --git a/app/components/results.tsx b/app/components/results.tsx
deleted file mode 100644
index 88bdf081..00000000
--- a/app/components/results.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-
-import { type Result } from '@/app/lib/storage';
-import ResultsTable from '@/app/components/results-table';
-import { title } from '@/app/components/primitives';
-import GenerateReportButton from '@/app/components/generate-report-button';
-import DeleteResultsButton from '@/app/components/delete-results-button';
-import UploadResultsButton from '@/app/components/upload-results-button';
-import { getUniqueProjectsList } from '@/app/lib/storage/format';
-
-interface ResultsProps {
- onChange: () => void;
-}
-
-export default function Results({ onChange }: Readonly) {
- const [selectedResults, setSelectedResults] = useState([]);
-
- const selectedResultIds = selectedResults.map((r) => r.resultID);
-
- const projects = getUniqueProjectsList(selectedResults);
-
- const onListUpdate = () => {
- setSelectedResults([]);
- onChange?.();
- };
-
- return (
- <>
-
-
-
Results
-
-
- {selectedResults.length > 0 && (
-
Results selected: {selectedResults.length}
- )}
-
-
-
-
-
-
-
- >
- );
-}
diff --git a/app/config/fonts.ts b/app/config/fonts.ts
deleted file mode 100644
index ebf5336e..00000000
--- a/app/config/fonts.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Fira_Code as FontMono, Inter as FontSans } from 'next/font/google';
-
-export const fontSans = FontSans({
- subsets: ['latin'],
- variable: '--font-sans',
-});
-
-export const fontMono = FontMono({
- subsets: ['latin'],
- variable: '--font-mono',
-});
diff --git a/app/config/runtime.ts b/app/config/runtime.ts
deleted file mode 100644
index 8ed6a6c0..00000000
--- a/app/config/runtime.ts
+++ /dev/null
@@ -1 +0,0 @@
-export const isBuildStage = process.env.NEXT_PHASE === 'phase-production-build';
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index d47239a8..00000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import '@/app/styles/globals.css';
-import { Metadata, Viewport } from 'next';
-import Link from 'next/link';
-import clsx from 'clsx';
-import { Toaster } from 'sonner';
-
-import { Providers } from './providers';
-
-import { siteConfig } from '@/app/config/site';
-import { fontSans } from '@/app/config/fonts';
-import { Navbar } from '@/app/components/navbar';
-import { Aside } from '@/app/components/aside';
-import { service } from '@/app/lib/service';
-import { withBase } from '@/app/lib/url';
-
-export async function generateMetadata(): Promise {
- const config = await service.getConfig();
-
- return {
- title: {
- default: siteConfig.name,
- template: `%s - ${siteConfig.name}`,
- },
- description: siteConfig.description,
- icons: {
- icon: withBase(config?.faviconPath ? `/api/static${config.faviconPath}` : '/favicon.ico'),
- },
- };
-}
-
-export async function generateViewport(): Promise {
- return {
- themeColor: [
- { media: '(prefers-color-scheme: light)', color: 'white' },
- { media: '(prefers-color-scheme: dark)', color: 'black' },
- ],
- };
-}
-
-export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Powered by
- CyborgTests
-
-
-
-
-
-
- );
-}
diff --git a/app/lib/query-cache.ts b/app/lib/query-cache.ts
deleted file mode 100644
index ecd92b0a..00000000
--- a/app/lib/query-cache.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { type QueryClient } from '@tanstack/react-query';
-
-interface InvalidateCacheOptions {
- queryKeys?: string[];
- predicate?: string;
-}
-
-export const invalidateCache = (client: QueryClient, options: InvalidateCacheOptions) => {
- if (options?.queryKeys) {
- client.invalidateQueries({ queryKey: options.queryKeys });
- }
-
- if (options?.predicate) {
- client.invalidateQueries({
- predicate: (q) => q.queryKey.some((key) => typeof key === 'string' && key.startsWith(options.predicate!)),
- });
- }
-};
diff --git a/app/lib/service/cache/index.ts b/app/lib/service/cache/index.ts
deleted file mode 100644
index 8218c98b..00000000
--- a/app/lib/service/cache/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './reports';
-export * from './results';
-export * from './config';
diff --git a/app/lib/service/cache/reports.ts b/app/lib/service/cache/reports.ts
deleted file mode 100644
index 470fac2e..00000000
--- a/app/lib/service/cache/reports.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-import { withError } from '../../withError';
-
-import { storage } from '@/app/lib/storage';
-import { type ReportHistory } from '@/app/lib/storage/types';
-import { env } from '@/app/config/env';
-
-type ReportsMap = Map;
-
-export class ReportCache {
- private static instance: ReportCache;
- public initialized = false;
- private readonly reports: ReportsMap;
-
- private constructor() {
- this.reports = new Map();
- }
-
- public static getInstance() {
- if (!ReportCache.instance) {
- ReportCache.instance = new ReportCache();
- }
-
- return ReportCache.instance;
- }
-
- public async init() {
- if (this.initialized || !env.USE_SERVER_CACHE) {
- return;
- }
-
- console.log('[report cache] initializing cache');
- const { result, error } = await withError(storage.readReports());
-
- if (error) {
- console.error('[report cache] failed to read reports:', error);
-
- return;
- }
-
- if (!result?.reports?.length) {
- return;
- }
-
- for (const report of result.reports) {
- ReportCache.getInstance().reports.set(report.reportID, report);
- }
-
- this.initialized = true;
- }
-
- public onDeleted(reportIds: string[]) {
- if (!env.USE_SERVER_CACHE) {
- return;
- }
-
- for (const id of reportIds) {
- this.reports.delete(id);
- }
- }
-
- public onCreated(report: ReportHistory) {
- if (!env.USE_SERVER_CACHE) {
- return;
- }
- this.reports.set(report.reportID, report);
- }
-
- public getAll(): ReportHistory[] {
- return Array.from(this.reports.values());
- }
-
- public getByID(reportID: string): ReportHistory | undefined {
- return this.reports.get(reportID);
- }
-}
-
-export const reportCache = ReportCache.getInstance();
diff --git a/app/lib/service/cache/results.ts b/app/lib/service/cache/results.ts
deleted file mode 100644
index e39e2c10..00000000
--- a/app/lib/service/cache/results.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { withError } from '../../withError';
-
-import { storage } from '@/app/lib/storage';
-import { type Result } from '@/app/lib/storage/types';
-import { env } from '@/app/config/env';
-
-type ResultsMap = Map;
-
-export class ResultCache {
- private static instance: ResultCache;
- public initialized = false;
- private readonly results: ResultsMap;
-
- private constructor() {
- this.results = new Map();
- }
-
- public static getInstance() {
- if (!ResultCache.instance) {
- ResultCache.instance = new ResultCache();
- }
-
- return ResultCache.instance;
- }
-
- public async init() {
- if (this.initialized || !env.USE_SERVER_CACHE) {
- return;
- }
-
- console.log('[result cache] initializing cache');
- const { result: resultsResponse, error } = await withError(storage.readResults());
-
- if (error) {
- console.error('[result cache] failed to read results:', error);
-
- return;
- }
-
- if (!resultsResponse?.results?.length) {
- return;
- }
-
- const cache = ResultCache.getInstance();
-
- for (const result of resultsResponse.results) {
- cache.results.set(result.resultID, result);
- }
-
- this.initialized = true;
- }
-
- public onDeleted(resultIds: string[]) {
- if (!env.USE_SERVER_CACHE) {
- return;
- }
-
- for (const id of resultIds) {
- this.results.delete(id);
- }
- }
-
- public onCreated(result: Result) {
- if (!env.USE_SERVER_CACHE) {
- return;
- }
-
- this.results.set(result.resultID, result);
- }
-
- public getAll(): Result[] {
- return Array.from(this.results.values());
- }
-
- public getByID(resultID: string): Result | undefined {
- return this.results.get(resultID);
- }
-}
-
-export const resultCache = ResultCache.getInstance();
diff --git a/app/lib/service/index.ts b/app/lib/service/index.ts
deleted file mode 100644
index 8a922a3d..00000000
--- a/app/lib/service/index.ts
+++ /dev/null
@@ -1,429 +0,0 @@
-import { PassThrough, Readable } from 'node:stream';
-
-import { withError } from '../withError';
-import { bytesToString, getUniqueProjectsList } from '../storage/format';
-import { serveReportRoute } from '../constants';
-import { DEFAULT_STREAM_CHUNK_SIZE } from '../storage/constants';
-
-import { lifecycle } from '@/app/lib/service/lifecycle';
-import { configCache, reportCache, resultCache } from '@/app/lib/service/cache';
-import {
- type ReadReportsInput,
- ReadResultsInput,
- ReadResultsOutput,
- ReportHistory,
- ReportMetadata,
- ResultDetails,
- ServerDataInfo,
- isReportHistory,
- storage,
-} from '@/app/lib/storage';
-import { handlePagination } from '@/app/lib/storage/pagination';
-import { SiteWhiteLabelConfig } from '@/app/types';
-import { defaultConfig } from '@/app/lib/config';
-import { env } from '@/app/config/env';
-import { type S3 } from '@/app/lib/storage/s3';
-import { isValidPlaywrightVersion } from '@/app/lib/pw';
-
-class Service {
- private static instance: Service;
-
- public static getInstance() {
- console.log(`[service] get instance`);
- if (!Service.instance) {
- Service.instance = new Service();
- }
-
- return Service.instance;
- }
-
- private shouldUseServerCache(): boolean {
- return env.USE_SERVER_CACHE && lifecycle.isInitialized();
- }
-
- public async getReports(input?: ReadReportsInput) {
- console.log(`[service] getReports`);
- const cached = this.shouldUseServerCache() && reportCache.initialized ? reportCache.getAll() : [];
-
- const shouldUseCache = !input?.ids;
-
- if (cached.length && shouldUseCache) {
- console.log(`[service] using cached reports`);
- const noFilters = !input?.project && !input?.ids;
- const shouldFilterByProject = (report: ReportHistory) => input?.project && report.project === input.project;
- const shouldFilterByID = (report: ReportHistory) => input?.ids?.includes(report.reportID);
-
- let reports = cached.filter((report) => noFilters || shouldFilterByProject(report) || shouldFilterByID(report));
-
- // Filter by search if provided
- if (input?.search?.trim()) {
- const searchTerm = input.search.toLowerCase().trim();
-
- reports = reports.filter((report) => {
- // Search in title, reportID, project, and all metadata fields
- const searchableFields = [
- report.title,
- report.reportID,
- report.project,
- ...Object.entries(report)
- .filter(
- ([key]) =>
- !['reportID', 'title', 'createdAt', 'size', 'sizeBytes', 'project', 'reportUrl', 'stats'].includes(
- key,
- ),
- )
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const getTimestamp = (date?: Date | string) => {
- if (!date) return 0;
- if (typeof date === 'string') return new Date(date).getTime();
-
- return date.getTime();
- };
-
- reports.sort((a, b) => getTimestamp(b.createdAt) - getTimestamp(a.createdAt));
- const currentReports = handlePagination(reports, input?.pagination);
-
- return {
- reports: currentReports,
- total: reports.length,
- };
- }
-
- console.log(`[service] using external reports`);
-
- return await storage.readReports(input);
- }
-
- public async getReport(id: string): Promise {
- console.log(`[service] getReport ${id}`);
- const cached = this.shouldUseServerCache() && reportCache.initialized ? reportCache.getByID(id) : undefined;
-
- if (isReportHistory(cached)) {
- console.log(`[service] using cached report`);
-
- return cached;
- }
-
- console.log(`[service] fetching report`);
-
- const { reports } = await this.getReports({ ids: [id] });
-
- const report = reports.find((report) => report.reportID === id);
-
- if (!report) {
- throw new Error(`report with id ${id} not found`);
- }
-
- return report;
- }
-
- private async findLatestPlaywrightVersionFromResults(resultIds: string[]) {
- for (const resultId of resultIds) {
- const { result: results, error } = await withError(this.getResults({ search: resultId }));
-
- if (error || !results) {
- continue;
- }
-
- const [latestResult] = results.results;
-
- if (!latestResult) {
- continue;
- }
-
- const latestVersion = latestResult?.playwrightVersion;
-
- if (latestVersion) {
- return latestVersion;
- }
- }
- }
-
- private async findLatestPlaywrightVersion(resultIds: string[]) {
- const versionFromResults = await this.findLatestPlaywrightVersionFromResults(resultIds);
-
- if (versionFromResults) {
- return versionFromResults;
- }
-
- // just in case version not found in results, we can try to get it from latest reports
- const { result: reportsArray, error } = await withError(this.getReports({ pagination: { limit: 10, offset: 0 } }));
-
- if (error || !reportsArray) {
- return '';
- }
-
- const reportWithVersion = reportsArray.reports.find((report) => !!report.metadata?.playwrightVersion);
-
- if (!reportWithVersion) {
- return '';
- }
-
- return reportWithVersion.metadata.playwrightVersion;
- }
-
- public async generateReport(
- resultsIds: string[],
- metadata?: ReportMetadata,
- ): Promise<{ reportId: string; reportUrl: string; metadata: ReportMetadata }> {
- const version = isValidPlaywrightVersion(metadata?.playwrightVersion)
- ? metadata?.playwrightVersion
- : await this.findLatestPlaywrightVersion(resultsIds);
-
- const metadataWithVersion = { ...(metadata ?? {}), playwrightVersion: version ?? '' };
-
- const reportId = await storage.generateReport(resultsIds, metadataWithVersion);
-
- const report = await this.getReport(reportId);
-
- reportCache.onCreated(report);
-
- const projectPath = metadata?.project ? `${encodeURI(metadata.project)}/` : '';
- const reportUrl = `${serveReportRoute}/${projectPath}${reportId}/index.html`;
-
- return { reportId, reportUrl, metadata: metadataWithVersion };
- }
-
- public async deleteReports(reportIDs: string[]) {
- const { error } = await withError(storage.deleteReports(reportIDs));
-
- if (error) {
- throw error;
- }
-
- reportCache.onDeleted(reportIDs);
- }
-
- public async getReportsProjects(): Promise {
- const { reports } = await this.getReports();
- const projects = getUniqueProjectsList(reports);
-
- return projects;
- }
-
- public async getResults(input?: ReadResultsInput): Promise {
- console.log(`[results service] getResults`);
- const cached = this.shouldUseServerCache() && resultCache.initialized ? resultCache.getAll() : [];
-
- if (!cached.length) {
- return await storage.readResults(input);
- }
-
- const getTimestamp = (date?: Date | string) => {
- if (!date) return 0;
- if (typeof date === 'string') return new Date(date).getTime();
-
- return date.getTime();
- };
-
- cached.sort((a, b) => getTimestamp(b.createdAt) - getTimestamp(a.createdAt));
-
- let filtered = input?.project
- ? cached.filter((file) => (input?.project ? file.project === input.project : file))
- : cached;
-
- if (input?.testRun) {
- filtered = filtered.filter((file) => file.testRun === input.testRun);
- }
-
- // Filter by tags if provided
- if (input?.tags && input.tags.length > 0) {
- const notMetadataKeys = ['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'];
-
- filtered = filtered.filter((result) => {
- const resultTags = Object.entries(result)
- .filter(([key]) => !notMetadataKeys.includes(key))
- .map(([key, value]) => `${key}: ${value}`);
-
- return input.tags!.some((selectedTag) => resultTags.includes(selectedTag));
- });
- }
-
- // Filter by search if provided
- if (input?.search?.trim()) {
- const searchTerm = input.search.toLowerCase().trim();
-
- filtered = filtered.filter((result) => {
- // Search in title, resultID, project, and all metadata fields
- const searchableFields = [
- result.title,
- result.resultID,
- result.project,
- ...Object.entries(result)
- .filter(([key]) => !['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'].includes(key))
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const results = !input?.pagination ? filtered : handlePagination(filtered, input?.pagination);
-
- return {
- results,
- total: filtered.length,
- };
- }
-
- public async deleteResults(resultIDs: string[]): Promise {
- const { error } = await withError(storage.deleteResults(resultIDs));
-
- if (error) {
- throw error;
- }
-
- resultCache.onDeleted(resultIDs);
- }
-
- public async getPresignedUrl(fileName: string): Promise {
- console.log(`[service] getPresignedUrl for ${fileName}`);
-
- if (env.DATA_STORAGE !== 's3') {
- console.log(`[service] fs storage detected, no presigned URL needed`);
-
- return '';
- }
-
- console.log(`[service] s3 detected, generating presigned URL`);
-
- const { result: presignedUrl, error } = await withError((storage as S3).generatePresignedUploadUrl(fileName));
-
- if (error) {
- console.error(`[service] getPresignedUrl | error: ${error.message}`);
-
- return '';
- }
-
- return presignedUrl!;
- }
-
- public async saveResult(filename: string, stream: PassThrough, presignedUrl?: string, contentLength?: string) {
- if (!presignedUrl) {
- console.log(`[service] saving result`);
-
- return await storage.saveResult(filename, stream);
- }
-
- console.log(`[service] using direct upload via presigned URL`, presignedUrl);
-
- const { error } = await withError(
- fetch(presignedUrl, {
- method: 'PUT',
- body: Readable.toWeb(stream, {
- strategy: {
- highWaterMark: DEFAULT_STREAM_CHUNK_SIZE,
- },
- }),
- headers: {
- 'Content-Type': 'application/zip',
- 'Content-Length': contentLength,
- },
- duplex: 'half',
- } as RequestInit),
- );
-
- if (error) {
- console.error(`[s3] saveResult | error: ${error.message}`);
- throw error;
- }
- }
-
- public async saveResultDetails(resultID: string, resultDetails: ResultDetails, size: number) {
- const result = await storage.saveResultDetails(resultID, resultDetails, size);
-
- resultCache.onCreated(result);
-
- return result;
- }
-
- public async getResultsProjects(): Promise {
- const { results } = await this.getResults();
- const projects = getUniqueProjectsList(results);
-
- const reportProjects = await this.getReportsProjects();
-
- return Array.from(new Set([...projects, ...reportProjects]));
- }
-
- public async getResultsTags(project?: string): Promise {
- const { results } = await this.getResults(project ? { project } : undefined);
-
- const notMetadataKeys = ['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'];
- const allTags = new Set();
-
- results.forEach((result) => {
- Object.entries(result).forEach(([key, value]) => {
- if (!notMetadataKeys.includes(key) && value !== undefined && value !== null) {
- allTags.add(`${key}: ${value}`);
- }
- });
- });
-
- return Array.from(allTags).sort();
- }
-
- public async getServerInfo(): Promise {
- console.log(`[service] getServerInfo`);
- const canCalculateFromCache = this.shouldUseServerCache() && reportCache.initialized && resultCache.initialized;
-
- if (!canCalculateFromCache) {
- return await storage.getServerDataInfo();
- }
-
- const reports = reportCache.getAll();
- const results = resultCache.getAll();
-
- const getTotalSizeBytes = (entity: T) =>
- entity.reduce((total, item) => total + item.sizeBytes, 0);
-
- const reportsFolderSize = getTotalSizeBytes(reports);
- const resultsFolderSize = getTotalSizeBytes(results);
- const dataFolderSize = reportsFolderSize + resultsFolderSize;
-
- return {
- dataFolderSizeinMB: bytesToString(dataFolderSize),
- numOfResults: results.length,
- resultsFolderSizeinMB: bytesToString(resultsFolderSize),
- numOfReports: reports.length,
- reportsFolderSizeinMB: bytesToString(reportsFolderSize),
- };
- }
-
- public async getConfig() {
- const cached = this.shouldUseServerCache() && configCache.initialized ? configCache.config : undefined;
-
- if (cached) {
- console.log(`[service] using cached config`);
-
- return cached;
- }
-
- const { result, error } = await storage.readConfigFile();
-
- if (error) console.error(`[service] getConfig | error: ${error.message}`);
-
- return { ...defaultConfig, ...(result ?? {}) };
- }
-
- public async updateConfig(config: Partial) {
- console.log(`[service] updateConfig`, config);
- const { result, error } = await storage.saveConfigFile(config);
-
- if (error) {
- throw error;
- }
-
- configCache.onChanged(result);
-
- return result;
- }
-}
-
-export const service = Service.getInstance();
diff --git a/app/lib/service/lifecycle.ts b/app/lib/service/lifecycle.ts
deleted file mode 100644
index db2e3d4b..00000000
--- a/app/lib/service/lifecycle.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { configCache, reportCache, resultCache } from '@/app/lib/service/cache';
-import { cronService } from '@/app/lib/service/cron';
-import { env } from '@/app/config/env';
-import { isBuildStage } from '@/app/config/runtime';
-
-const processKey = Symbol.for('playwright.reports.lifecycle');
-
-export class Lifecycle {
- private initialized = false;
- private initPromise?: Promise;
-
- public static getInstance(): Lifecycle {
- const nodeJsProcess = process as typeof process & { [key: symbol]: any };
-
- if (!nodeJsProcess[processKey]) {
- nodeJsProcess[processKey] = new Lifecycle();
- }
-
- return nodeJsProcess[processKey];
- }
-
- public async initialize(): Promise {
- if (this.initialized) return;
-
- this.initPromise ??= this._performInitialization();
-
- return this.initPromise;
- }
-
- private async _performInitialization(): Promise {
- console.log('[lifecycle] Starting application initialization');
-
- try {
- if (env.USE_SERVER_CACHE) {
- await Promise.all([configCache.init(), reportCache.init(), resultCache.init()]);
- console.log('[lifecycle] Caches initialized successfully');
- }
-
- if (!cronService.initialized && !isBuildStage) {
- await cronService.init();
- console.log('[lifecycle] Cron service initialized successfully');
- }
-
- this.initialized = true;
- console.log('[lifecycle] Application initialization complete');
- } catch (error) {
- console.error('[lifecycle] Initialization failed:', error);
- throw error;
- }
- }
-
- public isInitialized(): boolean {
- return this.initialized;
- }
-}
-
-export const lifecycle = Lifecycle.getInstance();
diff --git a/app/lib/storage/index.ts b/app/lib/storage/index.ts
deleted file mode 100644
index cf3abbe7..00000000
--- a/app/lib/storage/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export * from './types';
-import { FS } from './fs';
-import { S3 } from './s3';
-
-import { env } from '@/app/config/env';
-
-export const storage = env.DATA_STORAGE === 's3' ? S3.getInstance() : FS;
diff --git a/app/lib/storage/pagination.ts b/app/lib/storage/pagination.ts
deleted file mode 100644
index 80f58e25..00000000
--- a/app/lib/storage/pagination.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export interface Pagination {
- limit: number;
- offset: number;
-}
-
-export const handlePagination = (items: T[], pagination?: Pagination): T[] => {
- if (!pagination) {
- return items;
- }
-
- return items.slice(pagination.offset, pagination.offset + pagination.limit);
-};
-
-export const parseFromRequest = (searchParams: URLSearchParams): Pagination => {
- const limitQuery = searchParams.get('limit') ?? '';
- const offsetQuery = searchParams.get('offset') ?? '';
-
- const limit = limitQuery ? parseInt(limitQuery, 10) : 20;
- const offset = offsetQuery ? parseInt(offsetQuery, 10) : 0;
-
- return { limit, offset };
-};
diff --git a/app/lib/storage/s3.ts b/app/lib/storage/s3.ts
deleted file mode 100644
index e69c76f3..00000000
--- a/app/lib/storage/s3.ts
+++ /dev/null
@@ -1,900 +0,0 @@
-import { randomUUID, type UUID } from 'crypto';
-import fs from 'fs/promises';
-import path from 'node:path';
-import { PassThrough, Readable } from 'node:stream';
-
-import { type BucketItem, Client } from 'minio';
-
-import { processBatch } from './batch';
-import {
- Result,
- Report,
- ResultDetails,
- ServerDataInfo,
- isReportHistory,
- ReadReportsInput,
- ReadReportsOutput,
- ReadResultsInput,
- ReadResultsOutput,
- ReportHistory,
- ReportMetadata,
- Storage,
-} from './types';
-import { bytesToString } from './format';
-import {
- REPORTS_FOLDER,
- TMP_FOLDER,
- REPORTS_BUCKET,
- RESULTS_BUCKET,
- REPORTS_PATH,
- REPORT_METADATA_FILE,
- APP_CONFIG_S3,
- DATA_PATH,
- DATA_FOLDER,
-} from './constants';
-import { handlePagination } from './pagination';
-import { getFileReportID } from './file';
-
-import { parse } from '@/app/lib/parser';
-import { serveReportRoute } from '@/app/lib/constants';
-import { generatePlaywrightReport } from '@/app/lib/pw';
-import { withError } from '@/app/lib/withError';
-import { env } from '@/app/config/env';
-import { SiteWhiteLabelConfig } from '@/app/types';
-import { defaultConfig, isConfigValid } from '@/app/lib/config';
-
-const createClient = () => {
- const endPoint = env.S3_ENDPOINT;
- const accessKey = env.S3_ACCESS_KEY;
- const secretKey = env.S3_SECRET_KEY;
- const port = env.S3_PORT;
- const region = env.S3_REGION;
-
- if (!endPoint) {
- throw new Error('S3_ENDPOINT is required');
- }
-
- if (!accessKey) {
- throw new Error('S3_ACCESS_KEY is required');
- }
-
- if (!secretKey) {
- throw new Error('S3_SECRET_KEY is required');
- }
-
- console.log('[s3] creating client');
-
- const client = new Client({
- endPoint,
- accessKey,
- secretKey,
- region,
- port,
- useSSL: true,
- });
-
- return client;
-};
-
-export class S3 implements Storage {
- private static instance: S3;
- private readonly client: Client;
- private readonly bucket: string;
- private readonly batchSize: number;
-
- private constructor() {
- this.client = createClient();
- this.bucket = env.S3_BUCKET;
- this.batchSize = env.S3_BATCH_SIZE;
- }
-
- public static getInstance() {
- if (!S3.instance) {
- S3.instance = new S3();
- }
-
- return S3.instance;
- }
-
- private async ensureBucketExist() {
- const { result: exist, error } = await withError(this.client.bucketExists(this.bucket));
-
- if (exist && !error) {
- return;
- }
-
- if (error) {
- console.error(error);
- }
-
- const { error: bucketError } = await withError(this.client.makeBucket(this.bucket, env.S3_REGION));
-
- if (bucketError) {
- console.error(bucketError);
- }
- }
-
- private async write(dir: string, files: { name: string; content: Readable | Buffer | string; size?: number }[]) {
- await this.ensureBucketExist();
- for (const file of files) {
- const filePath = path.join(dir, file.name);
-
- console.log(`[s3] writing ${filePath}`);
-
- const content = typeof file.content === 'string' ? Buffer.from(file.content) : file.content;
-
- const contentSize = file.size ?? (Buffer.isBuffer(content) ? content.length : undefined);
-
- await this.client.putObject(this.bucket, path.normalize(filePath), content, contentSize);
- }
- }
-
- private async read(targetPath: string, contentType?: string | null) {
- await this.ensureBucketExist();
- console.log(`[s3] read ${targetPath}`);
-
- const remotePath = targetPath.includes(REPORTS_BUCKET) ? targetPath : `${REPORTS_BUCKET}/${targetPath}`;
-
- console.log(`[s3] reading from remote path: ${remotePath}`);
-
- const { result: stream, error } = await withError(this.client.getObject(this.bucket, remotePath));
-
- if (error ?? !stream) {
- return { result: null, error };
- }
-
- const readStream = new Promise((resolve, reject) => {
- const chunks: Uint8Array[] = [];
-
- stream.on('data', (chunk: Uint8Array) => {
- chunks.push(chunk);
- });
-
- stream.on('end', () => {
- const fullContent = Buffer.concat(chunks);
-
- resolve(fullContent);
- });
-
- stream.on('error', (error) => {
- console.error(`[s3] failed to read stream: ${error.message}`);
- reject(error);
- });
- });
-
- const { result, error: readError } = await withError(readStream);
-
- return {
- result: contentType === 'text/html' ? result?.toString('utf-8') : result,
- error: error ?? readError ?? null,
- };
- }
-
- async clear(...path: string[]) {
- console.log(`[s3] clearing ${path}`);
- // avoid using "removeObjects" as it is not supported by every S3-compatible provider
- // for example, Google Cloud Storage.
- await processBatch(this, path, this.batchSize, async (object) => {
- await this.client.removeObject(this.bucket, object);
- });
- }
-
- async getFolderSize(folderPath: string): Promise<{ size: number; resultCount: number; indexCount: number }> {
- let resultCount = 0;
- let indexCount = 0;
- let totalSize = 0;
- const stream = this.client.listObjects(this.bucket, folderPath, true);
-
- return new Promise((resolve, reject) => {
- stream.on('data', (obj) => {
- if (obj.name?.endsWith('.zip')) {
- resultCount += 1;
- }
-
- if (obj.name?.endsWith('index.html') && !obj.name.includes('/trace/index.html')) {
- indexCount += 1;
- }
-
- totalSize += obj?.size ?? 0;
- });
-
- stream.on('error', (err) => {
- reject(err);
- });
-
- stream.on('end', () => {
- resolve({ size: totalSize, resultCount, indexCount });
- });
- });
- }
-
- async getServerDataInfo(): Promise {
- await this.ensureBucketExist();
- console.log('[s3] getting server data');
-
- const [results, reports] = await Promise.all([
- this.getFolderSize(RESULTS_BUCKET),
- this.getFolderSize(REPORTS_BUCKET),
- ]);
-
- const dataSize = results.size + reports.size;
-
- return {
- dataFolderSizeinMB: bytesToString(dataSize),
- numOfResults: results.resultCount,
- resultsFolderSizeinMB: bytesToString(results.size),
- numOfReports: reports.indexCount,
- reportsFolderSizeinMB: bytesToString(reports.size),
- };
- }
-
- async readFile(targetPath: string, contentType: string | null): Promise {
- console.log(`[s3] reading ${targetPath} | ${contentType}`);
- const { result, error } = await this.read(targetPath, contentType);
-
- if (error) {
- console.error(`[s3] failed to read file ${targetPath}: ${error.message}`);
- throw new Error(`[s3] failed to read file: ${error.message}`);
- }
-
- return result!;
- }
-
- async readResults(input?: ReadResultsInput): Promise {
- await this.ensureBucketExist();
-
- console.log('[s3] reading results');
- const listResultsStream = this.client.listObjectsV2(this.bucket, RESULTS_BUCKET, true);
-
- const files: BucketItem[] = [];
- const resultSizes = new Map();
-
- const findJsonFiles = new Promise((resolve, reject) => {
- listResultsStream.on('data', async (file) => {
- if (!file?.name) {
- return;
- }
-
- if (file.name.endsWith('.zip')) {
- const resultID = path.basename(file.name, '.zip');
-
- resultSizes.set(resultID, file.size);
- }
-
- if (!file.name.endsWith('.json')) {
- return;
- }
-
- files.push(file);
- });
-
- listResultsStream.on('error', (err) => {
- reject(err);
- });
-
- listResultsStream.on('end', () => {
- resolve(files);
- });
- });
-
- const { result: jsonFiles } = await withError(findJsonFiles);
-
- console.log(`[s3] found ${(jsonFiles ?? [])?.length} json files`);
-
- if (!jsonFiles) {
- return {
- results: [],
- total: 0,
- };
- }
-
- const getTimestamp = (date?: Date | string) => {
- if (!date) return 0;
- if (typeof date === 'string') return new Date(date).getTime();
-
- return date.getTime();
- };
-
- jsonFiles.sort((a, b) => getTimestamp(b.lastModified) - getTimestamp(a.lastModified));
-
- // check if we can apply pagination early
- const noFilters = !input?.project && !input?.pagination;
-
- const resultFiles = noFilters ? handlePagination(jsonFiles, input?.pagination) : jsonFiles;
-
- const results = await processBatch(this, resultFiles, this.batchSize, async (file) => {
- console.log(`[s3.batch] reading result: ${JSON.stringify(file)}`);
- const dataStream = await this.client.getObject(this.bucket, file.name!);
-
- let jsonString = '';
-
- for await (const chunk of dataStream) {
- jsonString += chunk.toString();
- }
-
- const parsed = JSON.parse(jsonString);
-
- return parsed;
- });
-
- let filteredResults = results.filter((file) => (input?.project ? file.project === input.project : file));
-
- // Filter by tags if provided
- if (input?.tags && input.tags.length > 0) {
- const notMetadataKeys = ['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'];
-
- filteredResults = filteredResults.filter((result) => {
- const resultTags = Object.entries(result)
- .filter(([key]) => !notMetadataKeys.includes(key))
- .map(([key, value]) => `${key}: ${value}`);
-
- return input.tags!.some((selectedTag) => resultTags.includes(selectedTag));
- });
- }
-
- // Filter by search if provided
- if (input?.search?.trim()) {
- const searchTerm = input.search.toLowerCase().trim();
-
- filteredResults = filteredResults.filter((result) => {
- // Search in title, resultID, project, and all metadata fields
- const searchableFields = [
- result.title,
- result.resultID,
- result.project,
- ...Object.entries(result)
- .filter(([key]) => !['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'].includes(key))
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const currentFiles = noFilters ? results : handlePagination(filteredResults, input?.pagination);
-
- return {
- results: currentFiles.map((result) => {
- const sizeBytes = resultSizes.get(result.resultID) ?? 0;
-
- return {
- ...result,
- sizeBytes,
- size: result.size ?? bytesToString(sizeBytes),
- };
- }) as Result[],
- total: noFilters ? jsonFiles.length : filteredResults.length,
- };
- }
-
- async readReports(input?: ReadReportsInput): Promise {
- await this.ensureBucketExist();
-
- console.log(`[s3] reading reports from external storage`);
- const reportsStream = this.client.listObjectsV2(this.bucket, REPORTS_BUCKET, true);
-
- const reports: Report[] = [];
- const reportSizes = new Map();
-
- return new Promise((resolve, reject) => {
- reportsStream.on('data', (file) => {
- if (!file?.name) {
- return;
- }
-
- const reportID = getFileReportID(file.name);
-
- const newSize = (reportSizes.get(reportID) ?? 0) + file.size;
-
- reportSizes.set(reportID, newSize);
-
- if (!file.name.endsWith('index.html') || file.name.includes('trace')) {
- return;
- }
-
- const dir = path.dirname(file.name);
- const id = path.basename(dir);
- const parentDir = path.basename(path.dirname(dir));
-
- const projectName = parentDir === REPORTS_PATH ? '' : parentDir;
-
- const noFilters = !input?.project && !input?.ids;
-
- const shouldFilterByProject = input?.project && projectName === input.project;
-
- const shouldFilterByID = input?.ids?.includes(id);
-
- const report = {
- reportID: id,
- project: projectName,
- createdAt: file.lastModified,
- reportUrl: `${serveReportRoute}/${projectName ? encodeURIComponent(projectName) : ''}/${id}/index.html`,
- size: '',
- sizeBytes: 0,
- };
-
- if (noFilters || shouldFilterByProject || shouldFilterByID) {
- reports.push(report);
- }
- });
-
- reportsStream.on('error', (err) => {
- reject(err);
- });
-
- reportsStream.on('end', async () => {
- const getTimestamp = (date?: Date | string) => {
- if (!date) return 0;
- if (typeof date === 'string') return new Date(date).getTime();
-
- return date.getTime();
- };
-
- reports.sort((a, b) => getTimestamp(b.createdAt) - getTimestamp(a.createdAt));
-
- const currentReports = handlePagination(reports, input?.pagination);
-
- const withMetadata = await this.getReportsMetadata(currentReports as ReportHistory[]);
-
- let filteredReports = withMetadata;
-
- // Filter by search if provided
- if (input?.search && input.search.trim()) {
- const searchTerm = input.search.toLowerCase().trim();
-
- filteredReports = filteredReports.filter((report) => {
- // Search in title, reportID, project, and all metadata fields
- const searchableFields = [
- report.title,
- report.reportID,
- report.project,
- ...Object.entries(report)
- .filter(
- ([key]) =>
- !['reportID', 'title', 'createdAt', 'size', 'sizeBytes', 'project', 'reportUrl', 'stats'].includes(
- key,
- ),
- )
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const finalReports = handlePagination(filteredReports, input?.pagination);
-
- resolve({
- reports: finalReports.map((report) => {
- const sizeBytes = reportSizes.get(report.reportID) ?? 0;
-
- return {
- ...report,
- sizeBytes,
- size: bytesToString(sizeBytes),
- };
- }),
- total: filteredReports.length,
- });
- });
- });
- }
-
- async getReportsMetadata(reports: ReportHistory[]): Promise {
- return await processBatch(this, reports, this.batchSize, async (report) => {
- console.log(`[s3.batch] reading report ${report.reportID} metadata`);
-
- const { result: metadata, error: metadataError } = await withError(
- this.readOrParseReportMetadata(report.reportID, report.project),
- );
-
- if (metadataError) {
- console.error(`[s3] failed to read or create metadata for ${report.reportID}: ${metadataError.message}`);
-
- return report;
- }
-
- if (!metadata) {
- return report;
- }
-
- return Object.assign(metadata, report);
- });
- }
-
- async readOrParseReportMetadata(id: string, projectName: string): Promise {
- const { result: metadataContent, error: metadataError } = await withError(
- this.readFile(path.join(REPORTS_BUCKET, projectName, id, REPORT_METADATA_FILE), 'utf-8'),
- );
-
- if (metadataError) console.error(`[s3] failed to read metadata for ${id}: ${metadataError.message}`);
-
- const metadata = metadataContent && !metadataError ? JSON.parse(metadataContent.toString()) : {};
-
- if (isReportHistory(metadata)) {
- console.log(`metadata found for report ${id}`);
-
- return metadata;
- }
-
- console.log(`metadata file not found for ${id}, creating new metadata`);
- try {
- const { result: htmlContent, error: htmlError } = await withError(
- this.readFile(path.join(REPORTS_BUCKET, projectName, id, 'index.html'), 'utf-8'),
- );
-
- if (htmlError) console.error(`[s3] failed to read index.html for ${id}: ${htmlError.message}`);
-
- const created = await this.parseReportMetadata(
- id,
- path.join(REPORTS_FOLDER, projectName, id),
- {
- project: projectName,
- reportID: id,
- },
- htmlContent?.toString(),
- );
-
- console.log(`metadata object created for ${id}: ${JSON.stringify(created)}`);
-
- await this.saveReportMetadata(id, path.join(REPORTS_FOLDER, projectName, id), created);
-
- Object.assign(metadata, created);
- } catch (e) {
- console.error(`failed to create metadata for ${id}: ${(e as Error).message}`);
- }
-
- return metadata;
- }
-
- async deleteResults(resultIDs: string[]): Promise {
- const objects = resultIDs.flatMap((id) => [`${RESULTS_BUCKET}/${id}.json`, `${RESULTS_BUCKET}/${id}.zip`]);
-
- await withError(this.clear(...objects));
- }
-
- private async getReportObjects(reportsIDs: string[]): Promise {
- const reportStream = this.client.listObjectsV2(this.bucket, REPORTS_BUCKET, true);
-
- const files: string[] = [];
-
- return new Promise((resolve, reject) => {
- reportStream.on('data', (file) => {
- if (!file?.name) {
- return;
- }
-
- const reportID = path.basename(path.dirname(file.name));
-
- if (reportsIDs.includes(reportID)) {
- files.push(file.name);
- }
- });
-
- reportStream.on('error', (err) => {
- reject(err);
- });
-
- reportStream.on('end', () => {
- resolve(files);
- });
- });
- }
-
- async deleteReports(reportIDs: string[]): Promise {
- const objects = await this.getReportObjects(reportIDs);
-
- await withError(this.clear(...objects));
- }
-
- async generatePresignedUploadUrl(fileName: string) {
- await this.ensureBucketExist();
- const objectKey = path.join(RESULTS_BUCKET, fileName);
- const expiry = 30 * 60; // 30 minutes
-
- return this.client.presignedPutObject(this.bucket, objectKey, expiry);
- }
-
- async saveResult(filename: string, stream: PassThrough) {
- return await this.write(RESULTS_BUCKET, [
- {
- name: filename,
- content: stream,
- },
- ]);
- }
-
- async saveResultDetails(resultID: string, resultDetails: ResultDetails, size: number): Promise {
- const metaData = {
- resultID,
- createdAt: new Date().toISOString(),
- project: resultDetails?.project ?? '',
- ...resultDetails,
- size: bytesToString(size),
- sizeBytes: size,
- };
-
- await this.write(RESULTS_BUCKET, [
- {
- name: `${resultID}.json`,
- content: JSON.stringify(metaData),
- },
- ]);
-
- return metaData as Result;
- }
-
- private async uploadReport(reportId: string, reportPath: string, remotePath: string) {
- console.log(`[s3] upload report: ${reportPath}`);
-
- const files = await fs.readdir(reportPath, { recursive: true, withFileTypes: true });
-
- await processBatch(this, files, this.batchSize, async (file) => {
- if (!file.isFile()) {
- return;
- }
-
- console.log(`[s3] uploading file: ${JSON.stringify(file)}`);
-
- const nestedPath = (file as any).path.split(reportId).pop();
- const s3Path = path.join(remotePath, nestedPath ?? '', file.name);
-
- console.log(`[s3] uploading to ${s3Path}`);
-
- const { error } = await withError(this.uploadFileWithRetry(s3Path, path.join((file as any).path, file.name)));
-
- if (error) {
- console.error(`[s3] failed to upload report: ${error.message}`);
- throw new Error(`[s3] failed to upload report: ${error.message}`);
- }
- });
- }
-
- private async uploadFileWithRetry(remotePath: string, filePath: string, attempt = 1): Promise {
- if (attempt > 3) {
- throw new Error(`[s3] failed to upload file after ${attempt} attempts: ${filePath}`);
- }
- const { error } = await withError(this.client.fPutObject(this.bucket, remotePath, filePath, {}));
-
- if (error) {
- console.error(`[s3] failed to upload file: ${error.message}`);
- console.log(`[s3] will retry in 3s...`);
-
- return await this.uploadFileWithRetry(remotePath, filePath, attempt + 1);
- }
- }
-
- private async clearTempFolders(id?: string) {
- const withReportPathMaybe = id ? ` for report ${id}` : '';
-
- console.log(`[s3] clear temp folders${withReportPathMaybe}`);
-
- await withError(fs.rm(path.join(TMP_FOLDER, id ?? ''), { recursive: true, force: true }));
- await withError(fs.rm(REPORTS_FOLDER, { recursive: true, force: true }));
- }
-
- async generateReport(resultsIds: string[], metadata?: ReportMetadata): Promise {
- console.log(`[s3] generate report from results: ${JSON.stringify(resultsIds)}`);
- console.log(`[s3] create temp folders`);
- const { error: mkdirReportsError } = await withError(fs.mkdir(REPORTS_FOLDER, { recursive: true }));
-
- if (mkdirReportsError) {
- console.error(`[s3] failed to create reports folder: ${mkdirReportsError.message}`);
- }
-
- const reportId = randomUUID();
- const tempFolder = path.join(TMP_FOLDER, reportId);
-
- const { error: mkdirTempError } = await withError(fs.mkdir(tempFolder, { recursive: true }));
-
- if (mkdirTempError) {
- console.error(`[s3] failed to create temporary folder: ${mkdirTempError.message}`);
- }
-
- const resultsStream = this.client.listObjects(this.bucket, RESULTS_BUCKET, true);
-
- console.log(`[s3] start processing...`);
- for await (const result of resultsStream) {
- const fileName = path.basename(result.name);
-
- const id = fileName.replace(path.extname(fileName), '');
-
- if (resultsIds.includes(id)) {
- console.log(`[s3] file id is in target results, downloading...`);
- const localFilePath = path.join(tempFolder, fileName);
-
- const { error } = await withError(this.client.fGetObject(this.bucket, result.name, localFilePath));
-
- if (error) {
- console.error(`[s3] failed to download ${result.name}: ${error.message}`);
-
- throw new Error(`failed to download ${result.name}: ${error.message}`);
- }
-
- console.log(`[s3] Downloaded: ${result.name} to ${localFilePath}`);
- }
- }
-
- const { reportPath } = await generatePlaywrightReport(reportId, metadata!);
-
- console.log(`[s3] report generated: ${reportId} | ${reportPath}`);
-
- const { result: info, error: parseReportMetadataError } = await withError(
- this.parseReportMetadata(reportId, reportPath, metadata),
- );
-
- if (parseReportMetadataError) console.error(parseReportMetadataError.message);
-
- const remotePath = path.join(REPORTS_BUCKET, metadata?.project ?? '', reportId);
-
- const { error: uploadError } = await withError(this.uploadReport(reportId, reportPath, remotePath));
-
- if (uploadError) {
- console.error(`[s3] failed to upload report: ${uploadError.message}`);
- } else {
- const { error } = await withError(this.saveReportMetadata(reportId, reportPath, info ?? metadata ?? {}));
-
- if (error) console.error(`[s3] failed to save report metadata: ${error.message}`);
- }
-
- await this.clearTempFolders(reportId);
-
- return reportId;
- }
-
- private async saveReportMetadata(reportId: string, reportPath: string, metadata: ReportMetadata) {
- console.log(`[s3] report uploaded: ${reportId}, uploading metadata to ${reportPath}`);
- const { error: metadataError } = await withError(
- this.write(path.join(REPORTS_BUCKET, metadata.project ?? '', reportId), [
- {
- name: REPORT_METADATA_FILE,
- content: JSON.stringify(metadata),
- },
- ]),
- );
-
- if (metadataError) console.error(`[s3] failed to upload report metadata: ${metadataError.message}`);
- }
-
- private async parseReportMetadata(
- reportId: string,
- reportPath: string,
- metadata?: Record,
- htmlContent?: string, // to pass file content if stored on s3
- ): Promise {
- console.log(`[s3] creating report metadata for ${reportId} and ${reportPath}`);
- const html = htmlContent ?? (await fs.readFile(path.join(reportPath, 'index.html'), 'utf-8'));
-
- const info = await parse(html as string);
-
- const content = Object.assign(info, metadata, {
- reportId,
- createdAt: new Date().toISOString(),
- });
-
- return content;
- }
-
- async readConfigFile(): Promise<{ result?: SiteWhiteLabelConfig; error: Error | null }> {
- await this.ensureBucketExist();
- console.log(`[s3] checking config file`);
-
- const { result: stream, error } = await withError(this.client.getObject(this.bucket, APP_CONFIG_S3));
-
- if (error) {
- console.error(`[s3] failed to read config file: ${error.message}`);
-
- return { error };
- }
-
- let existingConfig = '';
-
- for await (const chunk of stream ?? []) {
- existingConfig += chunk.toString();
- }
-
- try {
- const parsed = JSON.parse(existingConfig);
-
- const isValid = isConfigValid(parsed);
-
- if (!isValid) {
- return { error: new Error('invalid config') };
- }
-
- // ensure custom images available locally in data folder
- for (const image of [
- { path: parsed.faviconPath, default: defaultConfig.faviconPath },
- { path: parsed.logoPath, default: defaultConfig.logoPath },
- ]) {
- if (!image) continue;
- if (image.path === image.default) continue;
-
- const localPath = path.join(DATA_FOLDER, image.path);
- const { error: accessError } = await withError(fs.access(localPath));
-
- if (accessError) {
- const remotePath = path.join(DATA_PATH, image.path);
-
- console.log(`[s3] downloading config image: ${remotePath} to ${localPath}`);
- await this.client.fGetObject(this.bucket, remotePath, localPath);
- }
- }
-
- return { result: parsed, error: null };
- } catch (e) {
- return { error: new Error(`failed to parse config: ${e instanceof Error ? e.message : e}`) };
- }
- }
-
- async saveConfigFile(config: Partial) {
- console.log(`[s3] writing config file`);
-
- const { result: existingConfig, error: readExistingConfigError } = await this.readConfigFile();
-
- if (readExistingConfigError) {
- console.error(`[s3] failed to read existing config file: ${readExistingConfigError.message}`);
- }
-
- const { error: clearExistingConfigError } = await withError(this.clear(APP_CONFIG_S3));
-
- if (clearExistingConfigError) {
- console.error(`[s3] failed to clear existing config file: ${clearExistingConfigError.message}`);
- }
-
- const uploadConfig = { ...(existingConfig ?? {}), ...config } as SiteWhiteLabelConfig;
-
- const isDefaultImage = (key: keyof SiteWhiteLabelConfig) => config[key] && config[key] === defaultConfig[key];
-
- const shouldBeUploaded = async (key: keyof SiteWhiteLabelConfig) => {
- if (!config[key]) return false;
- if (isDefaultImage(key)) return false;
-
- const { result } = await withError(this.client.statObject(this.bucket, uploadConfig.logoPath));
-
- if (!result) {
- return true;
- }
-
- return false;
- };
-
- if (await shouldBeUploaded('logoPath')) {
- await this.uploadConfigImage(uploadConfig.logoPath);
- }
-
- if (await shouldBeUploaded('faviconPath')) {
- await this.uploadConfigImage(uploadConfig.faviconPath);
- }
-
- const { error } = await withError(
- this.write(DATA_PATH, [
- {
- name: 'config.json',
- content: JSON.stringify(uploadConfig, null, 2),
- },
- ]),
- );
-
- if (error) console.error(`[s3] failed to write config file: ${error.message}`);
-
- return { result: uploadConfig, error };
- }
-
- private async uploadConfigImage(imagePath: string): Promise {
- console.log(`[s3] uploading config image: ${imagePath}`);
-
- const localPath = path.join(DATA_FOLDER, imagePath);
- const remotePath = path.join(DATA_PATH, imagePath);
-
- const { error } = await withError(this.uploadFileWithRetry(remotePath, localPath));
-
- if (error) {
- console.error(`[s3] failed to upload config image: ${error.message}`);
-
- return error;
- }
-
- return null;
- }
-}
diff --git a/app/lib/tailwind.ts b/app/lib/tailwind.ts
deleted file mode 100644
index 72595ae7..00000000
--- a/app/lib/tailwind.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { type ClassValue, clsx } from 'clsx';
-import { twMerge } from 'tailwind-merge';
-
-import { ReportTestOutcome } from './parser';
-
-export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs));
-}
-
-enum OutcomeColors {
- Success = 'success',
- Primary = 'primary',
- Danger = 'danger',
- Warning = 'warning',
- Default = 'default',
-}
-
-export function testStatusToColor(outcome?: ReportTestOutcome): {
- title: string;
- colorName: OutcomeColors;
- color: string;
-} {
- const outcomes = {
- [ReportTestOutcome.Expected]: {
- title: 'Passed',
- colorName: OutcomeColors.Success,
- color: 'text-success-500',
- },
- [ReportTestOutcome.Unexpected]: {
- title: 'Failed',
- colorName: OutcomeColors.Danger,
- color: 'text-danger-500',
- },
- [ReportTestOutcome.Flaky]: {
- title: 'Flaky',
- colorName: OutcomeColors.Warning,
- color: 'text-warning-500',
- },
- [ReportTestOutcome.Skipped]: {
- title: 'Skipped',
- colorName: OutcomeColors.Default,
- color: 'text-gray-500',
- },
- unknown: {
- title: 'N/A',
- colorName: OutcomeColors.Primary,
- color: 'text-gray-200',
- },
- };
-
- return outcome ? outcomes[outcome] : outcomes.unknown;
-}
diff --git a/app/login/page.tsx b/app/login/page.tsx
deleted file mode 100644
index 97f0312b..00000000
--- a/app/login/page.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Suspense } from 'react';
-
-import LoginForm from '@/app/components/login-form';
-
-export default async function LoginPage() {
- // useSearchParams() should be wrapped in a suspense boundary.
- // Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout
- return (
-
-
-
- );
-}
diff --git a/app/page.tsx b/app/page.tsx
deleted file mode 100644
index c3b483b3..00000000
--- a/app/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { redirect } from 'next/navigation';
-
-export default function HomePage() {
- redirect('/reports');
-}
diff --git a/app/settings/types.ts b/app/settings/types.ts
deleted file mode 100644
index d95082f6..00000000
--- a/app/settings/types.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-export interface ServerConfig {
- title?: string;
- headerLinks?: Record;
- logoPath?: string;
- faviconPath?: string;
- reporterPaths?: string[];
- cron?: {
- resultExpireDays?: number;
- resultExpireCronSchedule?: string;
- reportExpireDays?: number;
- reportExpireCronSchedule?: string;
- };
- jira?: {
- baseUrl?: string;
- email?: string;
- apiToken?: string;
- projectKey?: string;
- };
-}
-
-export interface JiraConfig {
- configured: boolean;
- baseUrl?: string;
- defaultProjectKey?: string;
- issueTypes?: Array<{
- id: string;
- name: string;
- description: string;
- }>;
- error?: string;
-}
diff --git a/app/styles/globals.css b/app/styles/globals.css
deleted file mode 100644
index f42870f0..00000000
--- a/app/styles/globals.css
+++ /dev/null
@@ -1,21 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-@layer base {
- :root {
- --chart-1: 160 60% 45%;
- --chart-2: 350 70% 56%;
- --chart-3: 0 8% 65%;
- --chart-4: 280 65% 60%;
- --chart-5: 340 75% 55%;
- }
-
- .dark {
- --chart-1: 160 60% 45%;
- --chart-2: 350 70% 56%;
- --chart-3: 0 8% 65%;
- --chart-4: 280 65% 60%;
- --chart-5: 340 75% 55%;
- }
- }
\ No newline at end of file
diff --git a/app/types/index.ts b/app/types/index.ts
deleted file mode 100644
index 4e9c2265..00000000
--- a/app/types/index.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { SVGProps } from 'react';
-
-import { type HeaderLinks } from '@/app/config/site';
-
-export type IconSvgProps = SVGProps & {
- size?: number;
-};
-
-export type UUID = `${string}-${string}-${string}-${string}-${string}`;
-
-export interface JiraConfig {
- baseUrl?: string;
- email?: string;
- apiToken?: string;
- projectKey?: string;
-}
-
-export interface SiteWhiteLabelConfig {
- title: string;
- headerLinks: HeaderLinks;
- logoPath: string;
- faviconPath: string;
- reporterPaths?: string[];
- authRequired?: boolean;
- serverCache?: boolean;
- dataStorage?: string;
- s3Endpoint?: string;
- s3Bucket?: string;
- cron?: {
- resultExpireDays?: number;
- resultExpireCronSchedule?: string;
- reportExpireDays?: number;
- reportExpireCronSchedule?: string;
- };
- jira?: JiraConfig;
-}
-
-export interface EnvInfo {
- authRequired: boolean;
- serverCache: boolean | undefined;
- dataStorage: string | undefined;
- s3Endpoint: string | undefined;
- s3Bucket: string | undefined;
-}
diff --git a/apps/backend/.env.example b/apps/backend/.env.example
new file mode 100644
index 00000000..d3a50aa2
--- /dev/null
+++ b/apps/backend/.env.example
@@ -0,0 +1,24 @@
+# Server configuration
+PORT=3001
+HOST=0.0.0.0
+
+# Auth configuration
+AUTH_SECRET=
+AUTH_URL=http://localhost:3000
+
+# API token details
+API_TOKEN='my-api-token'
+UI_AUTH_EXPIRE_HOURS='2'
+
+# Storage details
+DATA_STORAGE=fs # could be s3
+
+# S3 related configuration if DATA_STORAGE is "s3"
+S3_ENDPOINT="s3.endpoint"
+S3_ACCESS_KEY="some_access_key"
+S3_SECRET_KEY="some_secret_key"
+S3_PORT=9000 # optional
+S3_REGION="us-east-1"
+S3_BUCKET="bucket_name" # by default "playwright-reports-server"
+S3_BATCH_SIZE=10 # by default 10
+S3_MULTIPART_CHUNK_SIZE_MB=25 # by default 25MB, controls chunk size for multipart uploads to reduce RAM usage
\ No newline at end of file
diff --git a/apps/backend/package-lock.json b/apps/backend/package-lock.json
new file mode 100644
index 00000000..1f76d0c3
--- /dev/null
+++ b/apps/backend/package-lock.json
@@ -0,0 +1,6452 @@
+{
+ "name": "playwright-reports-server-backend",
+ "version": "5.7.4",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "playwright-reports-server-backend",
+ "version": "5.7.4",
+ "dependencies": {
+ "@aws-sdk/client-s3": "^3.932.0",
+ "@aws-sdk/s3-request-presigner": "^3.932.0",
+ "@fastify/cookie": "^10.0.1",
+ "@fastify/cors": "^10.0.1",
+ "@fastify/jwt": "^9.0.1",
+ "@fastify/multipart": "^9.0.1",
+ "@fastify/static": "^8.0.2",
+ "@playwright/test": "latest",
+ "better-sqlite3": "^12.4.1",
+ "busboy": "^1.6.0",
+ "croner": "^9.0.0",
+ "dotenv": "^17.2.3",
+ "envalid": "^8.0.0",
+ "fastify": "^5.2.0",
+ "get-folder-size": "5.0.0",
+ "jsonwebtoken": "^9.0.2",
+ "jszip": "^3.10.1",
+ "mime": "^4.0.4",
+ "sharp": "^0.33.5",
+ "uuid": "^10.0.0",
+ "zod": "^4.1.13"
+ },
+ "devDependencies": {
+ "@types/better-sqlite3": "^7.6.13",
+ "@types/busboy": "^1.5.4",
+ "@types/jsonwebtoken": "^9.0.7",
+ "@types/node": ">=22.10.2",
+ "@types/uuid": "^10.0.0",
+ "concurrently": "^9.2.1",
+ "tsx": "^4.21.0",
+ "typescript": "5.2.2",
+ "vite": "^6.0.3"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/crc32": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz",
+ "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/crc32c": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz",
+ "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz",
+ "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz",
+ "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-js": "^5.2.0",
+ "@aws-crypto/supports-web-crypto": "^5.2.0",
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "@aws-sdk/util-locate-window": "^3.0.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/sha256-js": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz",
+ "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/util": "^5.2.0",
+ "@aws-sdk/types": "^3.222.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/supports-web-crypto": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz",
+ "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz",
+ "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "^3.222.0",
+ "@smithy/util-utf8": "^2.0.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz",
+ "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz",
+ "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz",
+ "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^2.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-s3": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.943.0.tgz",
+ "integrity": "sha512-UOX8/1mmNaRmEkxoIVP2+gxd5joPJqz+fygRqlIXON1cETLGoctinMwQs7qU8g8hghm76TU2G6ZV6sLH8cySMw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha1-browser": "5.2.0",
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/credential-provider-node": "3.943.0",
+ "@aws-sdk/middleware-bucket-endpoint": "3.936.0",
+ "@aws-sdk/middleware-expect-continue": "3.936.0",
+ "@aws-sdk/middleware-flexible-checksums": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-location-constraint": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-sdk-s3": "3.943.0",
+ "@aws-sdk/middleware-ssec": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/signature-v4-multi-region": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/eventstream-serde-browser": "^4.2.5",
+ "@smithy/eventstream-serde-config-resolver": "^4.3.5",
+ "@smithy/eventstream-serde-node": "^4.2.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-blob-browser": "^4.2.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/hash-stream-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/md5-js": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-stream": "^4.5.6",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/util-waiter": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/client-sso": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.943.0.tgz",
+ "integrity": "sha512-kOTO2B8Ks2qX73CyKY8PAajtf5n39aMe2spoiOF5EkgSzGV7hZ/HONRDyADlyxwfsX39Q2F2SpPUaXzon32IGw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/core": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.943.0.tgz",
+ "integrity": "sha512-8CBy2hI9ABF7RBVQuY1bgf/ue+WPmM/hl0adrXFlhnhkaQP0tFY5zhiy1Y+n7V+5f3/ORoHBmCCQmcHDDYJqJQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/xml-builder": "3.930.0",
+ "@smithy/core": "^3.18.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/signature-v4": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-env": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.943.0.tgz",
+ "integrity": "sha512-WnS5w9fK9CTuoZRVSIHLOMcI63oODg9qd1vXMYb7QGLGlfwUm4aG3hdu7i9XvYrpkQfE3dzwWLtXF4ZBuL1Tew==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-http": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.943.0.tgz",
+ "integrity": "sha512-SA8bUcYDEACdhnhLpZNnWusBpdmj4Vl67Vxp3Zke7SvoWSYbuxa+tiDiC+c92Z4Yq6xNOuLPW912ZPb9/NsSkA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-stream": "^4.5.6",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-ini": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.943.0.tgz",
+ "integrity": "sha512-BcLDb8l4oVW+NkuqXMlO7TnM6lBOWW318ylf4FRED/ply5eaGxkQYqdGvHSqGSN5Rb3vr5Ek0xpzSjeYD7C8Kw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/credential-provider-env": "3.943.0",
+ "@aws-sdk/credential-provider-http": "3.943.0",
+ "@aws-sdk/credential-provider-login": "3.943.0",
+ "@aws-sdk/credential-provider-process": "3.943.0",
+ "@aws-sdk/credential-provider-sso": "3.943.0",
+ "@aws-sdk/credential-provider-web-identity": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-login": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.943.0.tgz",
+ "integrity": "sha512-9iCOVkiRW+evxiJE94RqosCwRrzptAVPhRhGWv4osfYDhjNAvUMyrnZl3T1bjqCoKNcETRKEZIU3dqYHnUkcwQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-node": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.943.0.tgz",
+ "integrity": "sha512-14eddaH/gjCWoLSAELVrFOQNyswUYwWphIt+PdsJ/FqVfP4ay2HsiZVEIYbQtmrKHaoLJhiZKwBQRjcqJDZG0w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/credential-provider-env": "3.943.0",
+ "@aws-sdk/credential-provider-http": "3.943.0",
+ "@aws-sdk/credential-provider-ini": "3.943.0",
+ "@aws-sdk/credential-provider-process": "3.943.0",
+ "@aws-sdk/credential-provider-sso": "3.943.0",
+ "@aws-sdk/credential-provider-web-identity": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-process": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.943.0.tgz",
+ "integrity": "sha512-GIY/vUkthL33AdjOJ8r9vOosKf/3X+X7LIiACzGxvZZrtoOiRq0LADppdiKIB48vTL63VvW+eRIOFAxE6UDekw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-sso": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.943.0.tgz",
+ "integrity": "sha512-1c5G11syUrru3D9OO6Uk+ul5e2lX1adb+7zQNyluNaLPXP6Dina6Sy6DFGRLu7tM8+M7luYmbS3w63rpYpaL+A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/client-sso": "3.943.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/token-providers": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/credential-provider-web-identity": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.943.0.tgz",
+ "integrity": "sha512-VtyGKHxICSb4kKGuaqotxso8JVM8RjCS3UYdIMOxUt9TaFE/CZIfZKtjTr+IJ7M0P7t36wuSUb/jRLyNmGzUUA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-bucket-endpoint": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.936.0.tgz",
+ "integrity": "sha512-XLSVVfAorUxZh6dzF+HTOp4R1B5EQcdpGcPliWr0KUj2jukgjZEcqbBmjyMF/p9bmyQsONX80iURF1HLAlW0qg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-arn-parser": "3.893.0",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-config-provider": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-expect-continue": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.936.0.tgz",
+ "integrity": "sha512-Eb4ELAC23bEQLJmUMYnPWcjD3FZIsmz2svDiXEcxRkQU9r7NRID7pM7C5NPH94wOfiCk0b2Y8rVyFXW0lGQwbA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-flexible-checksums": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.943.0.tgz",
+ "integrity": "sha512-J2oYbAQXTFEezs5m2Vij6H3w71K1hZfCtb85AsR/2Ovp/FjABMnK+Es1g1edRx6KuMTc9HkL/iGU4e+ek+qCZw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/crc32": "5.2.0",
+ "@aws-crypto/crc32c": "5.2.0",
+ "@aws-crypto/util": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/is-array-buffer": "^4.2.0",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-stream": "^4.5.6",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-host-header": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz",
+ "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-location-constraint": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.936.0.tgz",
+ "integrity": "sha512-SCMPenDtQMd9o5da9JzkHz838w3327iqXk3cbNnXWqnNRx6unyW8FL0DZ84gIY12kAyVHz5WEqlWuekc15ehfw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-logger": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz",
+ "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-recursion-detection": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz",
+ "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@aws/lambda-invoke-store": "^0.2.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-sdk-s3": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.943.0.tgz",
+ "integrity": "sha512-kd2mALfthU+RS9NsPS+qvznFcPnVgVx9mgmStWCPn5Qc5BTnx4UAtm+HPA+XZs+zxOopp+zmAfE4qxDHRVONBA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-arn-parser": "3.893.0",
+ "@smithy/core": "^3.18.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/signature-v4": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-config-provider": "^4.2.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-stream": "^4.5.6",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-ssec": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.936.0.tgz",
+ "integrity": "sha512-/GLC9lZdVp05ozRik5KsuODR/N7j+W+2TbfdFL3iS+7un+gnP6hC8RDOZd6WhpZp7drXQ9guKiTAxkZQwzS8DA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-user-agent": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.943.0.tgz",
+ "integrity": "sha512-956n4kVEwFNXndXfhSAN5wO+KRgqiWEEY+ECwLvxmmO8uQ0NWOa8l6l65nTtyuiWzMX81c9BvlyNR5EgUeeUvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@smithy/core": "^3.18.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/nested-clients": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.943.0.tgz",
+ "integrity": "sha512-anFtB0p2FPuyUnbOULwGmKYqYKSq1M73c9uZ08jR/NCq6Trjq9cuF5TFTeHwjJyPRb4wMf2Qk859oiVfFqnQiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/sha256-browser": "5.2.0",
+ "@aws-crypto/sha256-js": "5.2.0",
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/middleware-host-header": "3.936.0",
+ "@aws-sdk/middleware-logger": "3.936.0",
+ "@aws-sdk/middleware-recursion-detection": "3.936.0",
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/region-config-resolver": "3.936.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-endpoints": "3.936.0",
+ "@aws-sdk/util-user-agent-browser": "3.936.0",
+ "@aws-sdk/util-user-agent-node": "3.943.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/core": "^3.18.5",
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/hash-node": "^4.2.5",
+ "@smithy/invalid-dependency": "^4.2.5",
+ "@smithy/middleware-content-length": "^4.2.5",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/middleware-retry": "^4.4.12",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-body-length-node": "^4.2.1",
+ "@smithy/util-defaults-mode-browser": "^4.3.11",
+ "@smithy/util-defaults-mode-node": "^4.2.14",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/region-config-resolver": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz",
+ "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/s3-request-presigner": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.943.0.tgz",
+ "integrity": "sha512-r79MlUb7jeydV0caSz/emoyttzDdxgSkS/8ZU3lawkoTTnWCt+1nB4VA2xzOAPzP2dSdwNVpuAdY7uD1t2f0wA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/signature-v4-multi-region": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@aws-sdk/util-format-url": "3.936.0",
+ "@smithy/middleware-endpoint": "^4.3.12",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/smithy-client": "^4.9.8",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/signature-v4-multi-region": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.943.0.tgz",
+ "integrity": "sha512-KKvmxNQ/FZbM6ml6nKd8ltDulsUojsXnMJNgf1VHTcJEbADC/6mVWOq0+e9D0WP1qixUBEuMjlS2HqD5KoqwEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-sdk-s3": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/signature-v4": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/token-providers": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.943.0.tgz",
+ "integrity": "sha512-cRKyIzwfkS+XztXIFPoWORuaxlIswP+a83BJzelX4S1gUZ7FcXB4+lj9Jxjn8SbQhR4TPU3Owbpu+S7pd6IRbQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "3.943.0",
+ "@aws-sdk/nested-clients": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/types": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz",
+ "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-arn-parser": {
+ "version": "3.893.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz",
+ "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-endpoints": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz",
+ "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-endpoints": "^3.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-format-url": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.936.0.tgz",
+ "integrity": "sha512-MS5eSEtDUFIAMHrJaMERiHAvDPdfxc/T869ZjDNFAIiZhyc037REw0aoTNeimNXDNy2txRNZJaAUn/kE4RwN+g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/querystring-builder": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-locate-window": {
+ "version": "3.893.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz",
+ "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-browser": {
+ "version": "3.936.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz",
+ "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/types": "^4.9.0",
+ "bowser": "^2.11.0",
+ "tslib": "^2.6.2"
+ }
+ },
+ "node_modules/@aws-sdk/util-user-agent-node": {
+ "version": "3.943.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.943.0.tgz",
+ "integrity": "sha512-gn+ILprVRrgAgTIBk2TDsJLRClzIOdStQFeFTcN0qpL8Z4GBCqMFhw7O7X+MM55Stt5s4jAauQ/VvoqmCADnQg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/middleware-user-agent": "3.943.0",
+ "@aws-sdk/types": "3.936.0",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "aws-crt": ">=1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "aws-crt": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@aws-sdk/xml-builder": {
+ "version": "3.930.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz",
+ "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "fast-xml-parser": "5.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@aws/lambda-invoke-store": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.1.tgz",
+ "integrity": "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
+ "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@fastify/accept-negotiator": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-2.0.1.tgz",
+ "integrity": "sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/ajv-compiler": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz",
+ "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.12.0",
+ "ajv-formats": "^3.0.1",
+ "fast-uri": "^3.0.0"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+ "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
+ "license": "MIT"
+ },
+ "node_modules/@fastify/cookie": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-10.0.1.tgz",
+ "integrity": "sha512-NV/wbCUv4ETJ5KM1KMu0fLx0nSCm9idIxwg66NZnNbfPQH3rdbx6k0qRs5uy0y+MhBgvDudYRA30KlK659chyw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie-signature": "^1.2.1",
+ "fastify-plugin": "^5.0.0"
+ }
+ },
+ "node_modules/@fastify/cors": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-10.1.0.tgz",
+ "integrity": "sha512-MZyBCBJtII60CU9Xme/iE4aEy8G7QpzGR8zkdXZkDFt7ElEMachbE61tfhAG/bvSaULlqlf0huMT12T7iqEmdQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "fastify-plugin": "^5.0.0",
+ "mnemonist": "0.40.0"
+ }
+ },
+ "node_modules/@fastify/deepmerge": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.1.0.tgz",
+ "integrity": "sha512-lCVONBQINyNhM6LLezB6+2afusgEYR4G8xenMsfe+AT+iZ7Ca6upM5Ha8UkZuYSnuMw3GWl/BiPXnLMi/gSxuQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/error": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
+ "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/fast-json-stringify-compiler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz",
+ "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "fast-json-stringify": "^6.0.0"
+ }
+ },
+ "node_modules/@fastify/forwarded": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz",
+ "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/jwt": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/jwt/-/jwt-9.1.0.tgz",
+ "integrity": "sha512-CiGHCnS5cPMdb004c70sUWhQTfzrJHAeTywt7nVw6dAiI0z1o4WRvU94xfijhkaId4bIxTCOjFgn4sU+Gvk43w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/error": "^4.0.0",
+ "@lukeed/ms": "^2.0.2",
+ "fast-jwt": "^5.0.0",
+ "fastify-plugin": "^5.0.0",
+ "steed": "^1.1.3"
+ }
+ },
+ "node_modules/@fastify/merge-json-schemas": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz",
+ "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/@fastify/multipart": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.3.0.tgz",
+ "integrity": "sha512-NpeKipTOjjL1dA7SSlRMrOWWtrE8/0yKOmeudkdQoEaz4sVDJw5MVdZIahsWhvpc3YTN7f04f9ep/Y65RKoOWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/busboy": "^3.0.0",
+ "@fastify/deepmerge": "^3.0.0",
+ "@fastify/error": "^4.0.0",
+ "fastify-plugin": "^5.0.0",
+ "secure-json-parse": "^4.0.0"
+ }
+ },
+ "node_modules/@fastify/proxy-addr": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz",
+ "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/forwarded": "^3.0.0",
+ "ipaddr.js": "^2.1.0"
+ }
+ },
+ "node_modules/@fastify/send": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/send/-/send-4.1.0.tgz",
+ "integrity": "sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.2",
+ "escape-html": "~1.0.3",
+ "fast-decode-uri-component": "^1.0.1",
+ "http-errors": "^2.0.0",
+ "mime": "^3"
+ }
+ },
+ "node_modules/@fastify/send/node_modules/mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@fastify/static": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/@fastify/static/-/static-8.3.0.tgz",
+ "integrity": "sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/accept-negotiator": "^2.0.0",
+ "@fastify/send": "^4.0.0",
+ "content-disposition": "^0.5.4",
+ "fastify-plugin": "^5.0.0",
+ "fastq": "^1.17.1",
+ "glob": "^11.0.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+ "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+ "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+ "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+ "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+ "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+ "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+ "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+ "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+ "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+ "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+ "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.0.5"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+ "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+ "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+ "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+ "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+ "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+ "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.2.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+ "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+ "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/balanced-match": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+ "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@isaacs/brace-expansion": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+ "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@isaacs/balanced-match": "^4.0.1"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@lukeed/ms": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
+ "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@pinojs/redact": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
+ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
+ "license": "MIT"
+ },
+ "node_modules/@playwright/test": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz",
+ "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright": "1.57.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@smithy/abort-controller": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz",
+ "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/chunked-blob-reader": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz",
+ "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/chunked-blob-reader-native": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz",
+ "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-base64": "^4.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/config-resolver": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz",
+ "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-config-provider": "^4.2.0",
+ "@smithy/util-endpoints": "^3.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/core": {
+ "version": "3.18.6",
+ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.6.tgz",
+ "integrity": "sha512-8Q/ugWqfDUEU1Exw71+DoOzlONJ2Cn9QA8VeeDzLLjzO/qruh9UKFzbszy4jXcIYgGofxYiT0t1TT6+CT/GupQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-body-length-browser": "^4.2.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-stream": "^4.5.6",
+ "@smithy/util-utf8": "^4.2.0",
+ "@smithy/uuid": "^1.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/credential-provider-imds": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz",
+ "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-codec": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.5.tgz",
+ "integrity": "sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-crypto/crc32": "5.2.0",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-hex-encoding": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-browser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.5.tgz",
+ "integrity": "sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-serde-universal": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-config-resolver": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.5.tgz",
+ "integrity": "sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-node": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.5.tgz",
+ "integrity": "sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-serde-universal": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/eventstream-serde-universal": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.5.tgz",
+ "integrity": "sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/eventstream-codec": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/fetch-http-handler": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz",
+ "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/querystring-builder": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-blob-browser": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.6.tgz",
+ "integrity": "sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/chunked-blob-reader": "^5.2.0",
+ "@smithy/chunked-blob-reader-native": "^4.2.1",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-node": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz",
+ "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/hash-stream-node": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.5.tgz",
+ "integrity": "sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/invalid-dependency": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz",
+ "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/is-array-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz",
+ "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/md5-js": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.5.tgz",
+ "integrity": "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-content-length": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz",
+ "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-endpoint": {
+ "version": "4.3.13",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.13.tgz",
+ "integrity": "sha512-X4za1qCdyx1hEVVXuAWlZuK6wzLDv1uw1OY9VtaYy1lULl661+frY7FeuHdYdl7qAARUxH2yvNExU2/SmRFfcg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.18.6",
+ "@smithy/middleware-serde": "^4.2.6",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "@smithy/url-parser": "^4.2.5",
+ "@smithy/util-middleware": "^4.2.5",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-retry": {
+ "version": "4.4.13",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.13.tgz",
+ "integrity": "sha512-RzIDF9OrSviXX7MQeKOm8r/372KTyY8Jmp6HNKOOYlrguHADuM3ED/f4aCyNhZZFLG55lv5beBin7nL0Nzy1Dw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/service-error-classification": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-retry": "^4.2.5",
+ "@smithy/uuid": "^1.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-serde": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz",
+ "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/middleware-stack": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz",
+ "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-config-provider": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz",
+ "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/shared-ini-file-loader": "^4.4.0",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/node-http-handler": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz",
+ "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/abort-controller": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/querystring-builder": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/property-provider": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz",
+ "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/protocol-http": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz",
+ "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-builder": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz",
+ "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-uri-escape": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/querystring-parser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz",
+ "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/service-error-classification": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz",
+ "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/shared-ini-file-loader": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz",
+ "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/signature-v4": {
+ "version": "5.3.5",
+ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz",
+ "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.0",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-hex-encoding": "^4.2.0",
+ "@smithy/util-middleware": "^4.2.5",
+ "@smithy/util-uri-escape": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/smithy-client": {
+ "version": "4.9.9",
+ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.9.tgz",
+ "integrity": "sha512-SUnZJMMo5yCmgjopJbiNeo1vlr8KvdnEfIHV9rlD77QuOGdRotIVBcOrBuMr+sI9zrnhtDtLP054bZVbpZpiQA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/core": "^3.18.6",
+ "@smithy/middleware-endpoint": "^4.3.13",
+ "@smithy/middleware-stack": "^4.2.5",
+ "@smithy/protocol-http": "^5.3.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-stream": "^4.5.6",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/types": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz",
+ "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/url-parser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz",
+ "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/querystring-parser": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-base64": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz",
+ "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-browser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz",
+ "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-body-length-node": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz",
+ "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-buffer-from": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz",
+ "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/is-array-buffer": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-config-provider": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz",
+ "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-browser": {
+ "version": "4.3.12",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.12.tgz",
+ "integrity": "sha512-TKc6FnOxFULKxLgTNHYjcFqdOYzXVPFFVm5JhI30F3RdhT7nYOtOsjgaOwfDRmA/3U66O9KaBQ3UHoXwayRhAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-defaults-mode-node": {
+ "version": "4.2.15",
+ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.15.tgz",
+ "integrity": "sha512-94NqfQVo+vGc5gsQ9SROZqOvBkGNMQu6pjXbnn8aQvBUhc31kx49gxlkBEqgmaZQHUUfdRUin5gK/HlHKmbAwg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/config-resolver": "^4.4.3",
+ "@smithy/credential-provider-imds": "^4.2.5",
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/property-provider": "^4.2.5",
+ "@smithy/smithy-client": "^4.9.9",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-endpoints": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz",
+ "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/node-config-provider": "^4.3.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-hex-encoding": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz",
+ "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-middleware": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz",
+ "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-retry": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz",
+ "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/service-error-classification": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-stream": {
+ "version": "4.5.6",
+ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz",
+ "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/fetch-http-handler": "^5.3.6",
+ "@smithy/node-http-handler": "^4.4.5",
+ "@smithy/types": "^4.9.0",
+ "@smithy/util-base64": "^4.3.0",
+ "@smithy/util-buffer-from": "^4.2.0",
+ "@smithy/util-hex-encoding": "^4.2.0",
+ "@smithy/util-utf8": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-uri-escape": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz",
+ "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-utf8": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz",
+ "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/util-buffer-from": "^4.2.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/util-waiter": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.5.tgz",
+ "integrity": "sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@smithy/abort-controller": "^4.2.5",
+ "@smithy/types": "^4.9.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@smithy/uuid": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz",
+ "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@types/better-sqlite3": {
+ "version": "7.6.13",
+ "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
+ "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/busboy": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.4.tgz",
+ "integrity": "sha512-kG7WrUuAKK0NoyxfQHsVE6j1m01s6kMma64E+OZenQABMQyTJop1DumUWcLwAQ2JzpefU7PDYoRDKl8uZosFjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+ "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/abstract-logging": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
+ "license": "MIT"
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "license": "MIT",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/avvio": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.1.0.tgz",
+ "integrity": "sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/error": "^4.0.0",
+ "fastq": "^1.17.1"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/better-sqlite3": {
+ "version": "12.5.0",
+ "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz",
+ "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "prebuild-install": "^7.1.1"
+ },
+ "engines": {
+ "node": "20.x || 22.x || 23.x || 24.x || 25.x"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/bn.js": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
+ "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
+ "license": "MIT"
+ },
+ "node_modules/bowser": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz",
+ "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==",
+ "license": "MIT"
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "license": "ISC"
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/concurrently": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
+ "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.8.3",
+ "supports-color": "8.1.1",
+ "tree-kill": "1.2.2",
+ "yargs": "17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "license": "MIT"
+ },
+ "node_modules/croner": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz",
+ "integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/envalid": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.1.tgz",
+ "integrity": "sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/expand-template": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+ "license": "(MIT OR WTFPL)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fast-decode-uri-component": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
+ "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stringify": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.1.1.tgz",
+ "integrity": "sha512-DbgptncYEXZqDUOEl4krff4mUiVrTZZVI7BBrQR/T3BqMj/eM1flTC1Uk2uUoLcWCxjT95xKulV/Lc6hhOZsBQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/merge-json-schemas": "^0.2.0",
+ "ajv": "^8.12.0",
+ "ajv-formats": "^3.0.1",
+ "fast-uri": "^3.0.0",
+ "json-schema-ref-resolver": "^3.0.0",
+ "rfdc": "^1.2.0"
+ }
+ },
+ "node_modules/fast-jwt": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-5.0.6.tgz",
+ "integrity": "sha512-LPE7OCGUl11q3ZgW681cEU2d0d2JZ37hhJAmetCgNyW8waVaJVZXhyFF6U2so1Iim58Yc7pfxJe2P7MNetQH2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.2",
+ "asn1.js": "^5.4.1",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "mnemonist": "^0.40.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/fast-querystring": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
+ "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-decode-uri-component": "^1.0.1"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz",
+ "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^2.1.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/fastfall": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz",
+ "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "reusify": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fastify": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.6.2.tgz",
+ "integrity": "sha512-dPugdGnsvYkBlENLhCgX8yhyGCsCPrpA8lFWbTNU428l+YOnLgYHR69hzV8HWPC79n536EqzqQtvhtdaCE0dKg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/ajv-compiler": "^4.0.0",
+ "@fastify/error": "^4.0.0",
+ "@fastify/fast-json-stringify-compiler": "^5.0.0",
+ "@fastify/proxy-addr": "^5.0.0",
+ "abstract-logging": "^2.0.1",
+ "avvio": "^9.0.0",
+ "fast-json-stringify": "^6.0.0",
+ "find-my-way": "^9.0.0",
+ "light-my-request": "^6.0.0",
+ "pino": "^10.1.0",
+ "process-warning": "^5.0.0",
+ "rfdc": "^1.3.1",
+ "secure-json-parse": "^4.0.0",
+ "semver": "^7.6.0",
+ "toad-cache": "^3.7.0"
+ }
+ },
+ "node_modules/fastify-plugin": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz",
+ "integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fastparallel": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz",
+ "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4",
+ "xtend": "^4.0.2"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fastseries": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-1.7.2.tgz",
+ "integrity": "sha512-dTPFrPGS8SNSzAt7u/CbMKCJ3s01N04s4JFbORHcmyvVfVKmbhMD1VtRbh5enGHxkaQDqWyLefiKOGGmohGDDQ==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "license": "MIT"
+ },
+ "node_modules/find-my-way": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.3.0.tgz",
+ "integrity": "sha512-eRoFWQw+Yv2tuYlK2pjFS2jGXSxSppAs3hSQjfxVKxM5amECzIgYYc1FEI8ZmhSh/Ig+FrKEz43NLRKJjYCZVg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-querystring": "^1.0.0",
+ "safe-regex2": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "license": "MIT"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-folder-size": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-5.0.0.tgz",
+ "integrity": "sha512-+fgtvbL83tSDypEK+T411GDBQVQtxv+qtQgbV+HVa/TYubqDhNd5ghH/D6cOHY9iC5/88GtOZB7WI8PXy2A3bg==",
+ "license": "MIT",
+ "bin": {
+ "get-folder-size": "bin/get-folder-size.js"
+ },
+ "engines": {
+ "node": ">=18.11.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.13.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+ "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/github-from-package": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+ "license": "MIT"
+ },
+ "node_modules/glob": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz",
+ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "foreground-child": "^3.3.1",
+ "jackspeak": "^4.1.1",
+ "minimatch": "^10.1.1",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^2.0.0"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "license": "MIT"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz",
+ "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT"
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
+ "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/json-schema-ref-resolver": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz",
+ "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "license": "(MIT OR GPL-3.0-or-later)",
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/light-my-request": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
+ "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "process-warning": "^4.0.0",
+ "set-cookie-parser": "^2.6.0"
+ }
+ },
+ "node_modules/light-my-request/node_modules/process-warning": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
+ "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "11.2.4",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
+ "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/mime": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+ "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
+ "funding": [
+ "https://github.com/sponsors/broofa"
+ ],
+ "license": "MIT",
+ "bin": {
+ "mime": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "license": "ISC"
+ },
+ "node_modules/minimatch": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
+ "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/brace-expansion": "^5.0.0"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "license": "MIT"
+ },
+ "node_modules/mnemonist": {
+ "version": "0.40.0",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.40.0.tgz",
+ "integrity": "sha512-kdd8AFNig2AD5Rkih7EPCXhu/iMvwevQFX/uEiGhZyPZi7fHqOoF4V4kHLpCfysxXMgQ4B52kdPMCwARshKvEg==",
+ "license": "MIT",
+ "dependencies": {
+ "obliterator": "^2.0.4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-build-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+ "license": "MIT"
+ },
+ "node_modules/node-abi": {
+ "version": "3.85.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
+ "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/obliterator": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz",
+ "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==",
+ "license": "MIT"
+ },
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz",
+ "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pino": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-10.1.0.tgz",
+ "integrity": "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==",
+ "license": "MIT",
+ "dependencies": {
+ "@pinojs/redact": "^0.4.0",
+ "atomic-sleep": "^1.0.0",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^5.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^3.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-std-serializers": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
+ "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
+ "license": "MIT"
+ },
+ "node_modules/playwright": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz",
+ "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.57.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz",
+ "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prebuild-install": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.0",
+ "expand-template": "^2.0.3",
+ "github-from-package": "0.0.0",
+ "minimist": "^1.2.3",
+ "mkdirp-classic": "^0.5.3",
+ "napi-build-utils": "^2.0.0",
+ "node-abi": "^3.3.0",
+ "pump": "^3.0.0",
+ "rc": "^1.2.7",
+ "simple-get": "^4.0.0",
+ "tar-fs": "^2.0.0",
+ "tunnel-agent": "^0.6.0"
+ },
+ "bin": {
+ "prebuild-install": "bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "license": "MIT"
+ },
+ "node_modules/process-warning": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "license": "MIT"
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz",
+ "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.53.3",
+ "@rollup/rollup-android-arm64": "4.53.3",
+ "@rollup/rollup-darwin-arm64": "4.53.3",
+ "@rollup/rollup-darwin-x64": "4.53.3",
+ "@rollup/rollup-freebsd-arm64": "4.53.3",
+ "@rollup/rollup-freebsd-x64": "4.53.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+ "@rollup/rollup-linux-arm64-musl": "4.53.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-musl": "4.53.3",
+ "@rollup/rollup-openharmony-arm64": "4.53.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+ "@rollup/rollup-win32-x64-gnu": "4.53.3",
+ "@rollup/rollup-win32-x64-msvc": "4.53.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-regex2": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz",
+ "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.5.0"
+ }
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/secure-json-parse": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
+ "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "license": "MIT"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/sharp": {
+ "version": "0.33.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.3",
+ "semver": "^7.6.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.33.5",
+ "@img/sharp-darwin-x64": "0.33.5",
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
+ "@img/sharp-libvips-linux-arm": "1.0.5",
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
+ "@img/sharp-libvips-linux-x64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+ "@img/sharp-linux-arm": "0.33.5",
+ "@img/sharp-linux-arm64": "0.33.5",
+ "@img/sharp-linux-s390x": "0.33.5",
+ "@img/sharp-linux-x64": "0.33.5",
+ "@img/sharp-linuxmusl-arm64": "0.33.5",
+ "@img/sharp-linuxmusl-x64": "0.33.5",
+ "@img/sharp-wasm32": "0.33.5",
+ "@img/sharp-win32-ia32": "0.33.5",
+ "@img/sharp-win32-x64": "0.33.5"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shell-quote": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
+ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/simple-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/simple-get": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decompress-response": "^6.0.0",
+ "once": "^1.3.1",
+ "simple-concat": "^1.0.0"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/sonic-boom": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
+ "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/steed": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/steed/-/steed-1.1.3.tgz",
+ "integrity": "sha512-EUkci0FAUiE4IvGTSKcDJIQ/eRUP2JJb56+fvZ4sdnguLTqIdKjSxUe138poW8mkvKWXW2sFPrgTsxqoISnmoA==",
+ "license": "MIT",
+ "dependencies": {
+ "fastfall": "^1.5.0",
+ "fastparallel": "^2.2.0",
+ "fastq": "^1.3.0",
+ "fastseries": "^1.7.0",
+ "reusify": "^1.0.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz",
+ "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar-stream/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/thread-stream": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
+ "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^0.2.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/toad-cache": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz",
+ "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
+ "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
+ "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
+ "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
+ "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
+ "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
+ "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
+ "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
+ "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
+ "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
+ "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
+ "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
+ "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
+ "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
+ "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
+ "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
+ "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
+ "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
+ "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
+ "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
+ "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
+ "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
+ "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
+ "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
+ "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.27.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
+ "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.1",
+ "@esbuild/android-arm": "0.27.1",
+ "@esbuild/android-arm64": "0.27.1",
+ "@esbuild/android-x64": "0.27.1",
+ "@esbuild/darwin-arm64": "0.27.1",
+ "@esbuild/darwin-x64": "0.27.1",
+ "@esbuild/freebsd-arm64": "0.27.1",
+ "@esbuild/freebsd-x64": "0.27.1",
+ "@esbuild/linux-arm": "0.27.1",
+ "@esbuild/linux-arm64": "0.27.1",
+ "@esbuild/linux-ia32": "0.27.1",
+ "@esbuild/linux-loong64": "0.27.1",
+ "@esbuild/linux-mips64el": "0.27.1",
+ "@esbuild/linux-ppc64": "0.27.1",
+ "@esbuild/linux-riscv64": "0.27.1",
+ "@esbuild/linux-s390x": "0.27.1",
+ "@esbuild/linux-x64": "0.27.1",
+ "@esbuild/netbsd-arm64": "0.27.1",
+ "@esbuild/netbsd-x64": "0.27.1",
+ "@esbuild/openbsd-arm64": "0.27.1",
+ "@esbuild/openbsd-x64": "0.27.1",
+ "@esbuild/openharmony-arm64": "0.27.1",
+ "@esbuild/sunos-x64": "0.27.1",
+ "@esbuild/win32-arm64": "0.27.1",
+ "@esbuild/win32-ia32": "0.27.1",
+ "@esbuild/win32-x64": "0.27.1"
+ }
+ },
+ "node_modules/tsx/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
+ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
+ "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/apps/backend/package.json b/apps/backend/package.json
new file mode 100644
index 00000000..a86d0d78
--- /dev/null
+++ b/apps/backend/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "@playwright-reports/backend",
+ "version": "5.7.4",
+ "description": "Playwright Reports Server Backend",
+ "type": "module",
+ "scripts": {
+ "dev": "tsx watch --clear-screen=false src/index.ts",
+ "build": "tsc",
+ "start": "node dist/index.js",
+ "typecheck": "tsc --noEmit",
+ "test": "npx playwright test ../../tests/api/",
+ "lint": "biome check src/",
+ "lint:fix": "biome check src/ --write",
+ "format": "biome format src/ --write",
+ "format:check": "biome format src/",
+ "clean": "rm -rf dist"
+ },
+ "dependencies": {
+ "@aws-sdk/client-s3": "^3.932.0",
+ "@aws-sdk/s3-request-presigner": "^3.932.0",
+ "@fastify/cookie": "^10.0.1",
+ "@fastify/cors": "^10.0.1",
+ "@fastify/jwt": "^9.0.1",
+ "@fastify/multipart": "^9.0.1",
+ "@fastify/static": "^8.0.2",
+ "@playwright-reports/shared": "*",
+ "@playwright/test": "latest",
+ "better-sqlite3": "^12.4.1",
+ "busboy": "^1.6.0",
+ "croner": "^9.0.0",
+ "dotenv": "^17.2.3",
+ "envalid": "^8.0.0",
+ "fastify": "^5.2.0",
+ "get-folder-size": "5.0.0",
+ "jsonwebtoken": "^9.0.2",
+ "jszip": "^3.10.1",
+ "mime": "^4.0.4",
+ "sharp": "^0.33.5",
+ "uuid": "^10.0.0",
+ "zod": "^4.1.13"
+ },
+ "devDependencies": {
+ "@types/better-sqlite3": "^7.6.13",
+ "@types/busboy": "^1.5.4",
+ "@types/jsonwebtoken": "^9.0.7",
+ "@types/node": ">=22.10.2",
+ "@types/uuid": "^10.0.0",
+ "tsx": "^4.21.0",
+ "typescript": "5.2.2"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+}
diff --git a/public/favicon.ico b/apps/backend/public/favicon.ico
similarity index 100%
rename from public/favicon.ico
rename to apps/backend/public/favicon.ico
diff --git a/public/logo.svg b/apps/backend/public/logo.svg
similarity index 100%
rename from public/logo.svg
rename to apps/backend/public/logo.svg
diff --git a/app/config/env.ts b/apps/backend/src/config/env.ts
similarity index 51%
rename from app/config/env.ts
rename to apps/backend/src/config/env.ts
index 922dd3c4..94dce098 100644
--- a/app/config/env.ts
+++ b/apps/backend/src/config/env.ts
@@ -1,11 +1,13 @@
-import { cleanEnv, str, num, bool } from 'envalid';
+import { cleanEnv, num, str } from 'envalid';
export const env = cleanEnv(process.env, {
API_TOKEN: str({ desc: 'API token for authorization', default: undefined }),
- UI_AUTH_EXPIRE_HOURS: str({ desc: 'How much hours are allowed to keep auth session valid', default: '2' }),
+ UI_AUTH_EXPIRE_HOURS: str({
+ desc: 'How much hours are allowed to keep auth session valid',
+ default: '2',
+ }),
AUTH_SECRET: str({ desc: 'Secret for JWT', default: undefined }),
DATA_STORAGE: str({ desc: 'Where to store data', default: 'fs' }),
- USE_SERVER_CACHE: bool({ desc: 'Use server side indexed cache for backend queries', default: false }),
S3_ENDPOINT: str({ desc: 'S3 endpoint', default: undefined }),
S3_ACCESS_KEY: str({ desc: 'S3 access key', default: undefined }),
S3_SECRET_KEY: str({ desc: 'S3 secret key', default: undefined }),
@@ -13,14 +15,35 @@ export const env = cleanEnv(process.env, {
S3_REGION: str({ desc: 'S3 region', default: undefined }),
S3_BUCKET: str({ desc: 'S3 bucket', default: 'playwright-reports-server' }),
S3_BATCH_SIZE: num({ desc: 'S3 batch size', default: 10 }),
- RESULT_EXPIRE_DAYS: num({ desc: 'How much days to keep results', default: undefined }),
- RESULT_EXPIRE_CRON_SCHEDULE: str({ desc: 'Cron schedule for results cleanup', default: '33 3 * * *' }),
- REPORT_EXPIRE_DAYS: num({ desc: 'How much days to keep reports', default: undefined }),
- REPORT_EXPIRE_CRON_SCHEDULE: str({ desc: 'Cron schedule for reports cleanup', default: '44 4 * * *' }),
- // Jira API Configuration
- JIRA_BASE_URL: str({ desc: 'Jira base URL (e.g., https://your-domain.atlassian.net)', default: undefined }),
+ S3_MULTIPART_CHUNK_SIZE_MB: num({
+ desc: 'S3 multipart upload chunk size in MB',
+ default: 25,
+ }),
+ RESULT_EXPIRE_DAYS: num({
+ desc: 'How much days to keep results',
+ default: undefined,
+ }),
+ RESULT_EXPIRE_CRON_SCHEDULE: str({
+ desc: 'Cron schedule for results cleanup',
+ default: '33 3 * * *',
+ }),
+ REPORT_EXPIRE_DAYS: num({
+ desc: 'How much days to keep reports',
+ default: undefined,
+ }),
+ REPORT_EXPIRE_CRON_SCHEDULE: str({
+ desc: 'Cron schedule for reports cleanup',
+ default: '44 4 * * *',
+ }),
+ JIRA_BASE_URL: str({
+ desc: 'Jira base URL (e.g., https://your-domain.atlassian.net)',
+ default: undefined,
+ }),
JIRA_EMAIL: str({ desc: 'Jira user email', default: undefined }),
JIRA_API_TOKEN: str({ desc: 'Jira API token', default: undefined }),
- JIRA_PROJECT_KEY: str({ desc: 'Default Jira project key (optional)', default: undefined }),
+ JIRA_PROJECT_KEY: str({
+ desc: 'Default Jira project key (optional)',
+ default: undefined,
+ }),
API_BASE_PATH: str({ desc: 'Base path for the API', default: '' }),
});
diff --git a/app/config/site.ts b/apps/backend/src/config/site.ts
similarity index 100%
rename from app/config/site.ts
rename to apps/backend/src/config/site.ts
diff --git a/apps/backend/src/index.ts b/apps/backend/src/index.ts
new file mode 100644
index 00000000..889a4e8b
--- /dev/null
+++ b/apps/backend/src/index.ts
@@ -0,0 +1,108 @@
+import { join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import fastifyCookie from '@fastify/cookie';
+import fastifyCors from '@fastify/cors';
+import fastifyJwt from '@fastify/jwt';
+import fastifyMultipart from '@fastify/multipart';
+import fastifyStatic from '@fastify/static';
+import { config } from 'dotenv';
+import Fastify from 'fastify';
+import { lifecycle } from './lib/service/lifecycle.js';
+import { registerApiRoutes } from './routes/index.js';
+
+const __dirname = fileURLToPath(new URL('.', import.meta.url));
+config({ path: join(__dirname, '../../../.env') });
+
+const PORT = Number.parseInt(process.env.PORT || '3001', 10);
+const HOST = process.env.HOST || '0.0.0.0';
+const AUTH_SECRET = process.env.AUTH_SECRET || 'development-secret-change-in-production';
+
+async function start() {
+ const fastify = Fastify({
+ logger: {
+ level: process.env.LOG_LEVEL || 'info',
+ },
+ });
+
+ await fastify.register(fastifyCors, {
+ origin: process.env.CORS_ORIGIN || true,
+ credentials: true,
+ });
+
+ await fastify.register(fastifyCookie);
+
+ await fastify.register(fastifyJwt, {
+ secret: AUTH_SECRET,
+ cookie: {
+ cookieName: 'token',
+ signed: false,
+ },
+ });
+
+ await fastify.register(fastifyMultipart);
+
+ fastify.get('/api/ping', async () => {
+ return {
+ status: 'ok',
+ timestamp: new Date().toISOString(),
+ };
+ });
+
+ fastify.get('/api/health', async () => {
+ return {
+ status: 'healthy',
+ timestamp: new Date().toISOString(),
+ };
+ });
+
+ await registerApiRoutes(fastify);
+
+ const dataDir = process.env.DATA_DIR || join(process.cwd(), 'data');
+ await fastify.register(fastifyStatic, {
+ root: dataDir,
+ prefix: '/data/',
+ decorateReply: false,
+ });
+
+ if (process.env.NODE_ENV === 'production') {
+ const frontendDistPath =
+ process.env.FRONTEND_DIST || join(process.cwd(), '..', '..', 'apps', 'frontend', 'dist');
+
+ await fastify.register(fastifyStatic, {
+ root: frontendDistPath,
+ decorateReply: true,
+ });
+
+ // spa fallback for non-api routes
+ fastify.setNotFoundHandler(async (request, reply) => {
+ if (!request.url.startsWith('/api') && !request.url.startsWith('/data')) {
+ return reply.sendFile('index.html');
+ }
+ return reply.code(404).send({ error: 'Not Found' });
+ });
+ }
+
+ console.log('[server] Initializing databases and services...');
+ await lifecycle.initialize();
+ console.log('[server] Initialization complete');
+
+ const closeGracefully = async (signal: string) => {
+ fastify.log.info(`Received signal to terminate: ${signal}`);
+ await lifecycle.cleanup();
+ await fastify.close();
+ process.exit(0);
+ };
+
+ process.on('SIGINT', () => closeGracefully('SIGINT'));
+ process.on('SIGTERM', () => closeGracefully('SIGTERM'));
+
+ try {
+ await fastify.listen({ port: PORT, host: HOST });
+ fastify.log.info(`Server listening on http://${HOST}:${PORT}`);
+ } catch (err) {
+ fastify.log.error(err);
+ process.exit(1);
+ }
+}
+
+await start();
diff --git a/app/lib/auth.ts b/apps/backend/src/lib/auth.ts
similarity index 100%
rename from app/lib/auth.ts
rename to apps/backend/src/lib/auth.ts
diff --git a/app/lib/config.ts b/apps/backend/src/lib/config.ts
similarity index 89%
rename from app/lib/config.ts
rename to apps/backend/src/lib/config.ts
index 6fb51709..f2b6e59b 100644
--- a/app/lib/config.ts
+++ b/apps/backend/src/lib/config.ts
@@ -1,5 +1,5 @@
-import { SiteWhiteLabelConfig } from '@/app/types';
-import { defaultLinks } from '@/app/config/site';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import { defaultLinks } from '../config/site.js';
export const defaultConfig: SiteWhiteLabelConfig = {
title: 'Cyborg Tests',
diff --git a/app/lib/constants.ts b/apps/backend/src/lib/constants.ts
similarity index 100%
rename from app/lib/constants.ts
rename to apps/backend/src/lib/constants.ts
diff --git a/app/lib/network.ts b/apps/backend/src/lib/network.ts
similarity index 60%
rename from app/lib/network.ts
rename to apps/backend/src/lib/network.ts
index 29e90f9b..6e852805 100644
--- a/app/lib/network.ts
+++ b/apps/backend/src/lib/network.ts
@@ -1,10 +1,8 @@
-import { defaultProjectName } from './constants';
+import { defaultProjectName } from './constants.js';
-export class CommonResponseFactory {
- static buildUnauthorizedResponse(): Response {
- return new Response('Unauthorized', { status: 401 });
- }
-}
+export const buildUnauthorizedResponse = (): Response => {
+ return new Response('Unauthorized', { status: 401 });
+};
export const withQueryParams = (url: string, params: Record): string => {
if (params?.project === defaultProjectName) {
diff --git a/app/lib/parser/index.ts b/apps/backend/src/lib/parser/index.ts
similarity index 88%
rename from app/lib/parser/index.ts
rename to apps/backend/src/lib/parser/index.ts
index b4f1e491..3b34c816 100644
--- a/app/lib/parser/index.ts
+++ b/apps/backend/src/lib/parser/index.ts
@@ -1,10 +1,8 @@
import JSZip from 'jszip';
+import { withError } from '../../lib/withError.js';
+import type { ReportInfo } from './types.js';
-import { type ReportInfo } from './types';
-
-import { withError } from '@/app/lib/withError';
-
-export * from './types';
+export * from './types.js';
/**
*
@@ -42,7 +40,7 @@ export const parse = async (html: string): Promise => {
const reportFile = zip.file('report.json');
if (!reportFile) {
- throw Error('[report parser] no report.json file found in the zip');
+ throw new Error('[report parser] no report.json file found in the zip');
}
const reportJson = await reportFile.async('string');
diff --git a/app/lib/parser/types.ts b/apps/backend/src/lib/parser/types.ts
similarity index 100%
rename from app/lib/parser/types.ts
rename to apps/backend/src/lib/parser/types.ts
diff --git a/app/lib/pw.ts b/apps/backend/src/lib/pw.ts
similarity index 92%
rename from app/lib/pw.ts
rename to apps/backend/src/lib/pw.ts
index d1973445..aba33969 100644
--- a/app/lib/pw.ts
+++ b/apps/backend/src/lib/pw.ts
@@ -1,15 +1,14 @@
import { exec } from 'node:child_process';
-import util from 'node:util';
-import { type UUID } from 'node:crypto';
-import path from 'node:path';
-import fs from 'node:fs/promises';
+import type { UUID } from 'node:crypto';
import { existsSync } from 'node:fs';
-
-import { REPORTS_FOLDER, TMP_FOLDER } from './storage/constants';
-import { createDirectory } from './storage/folders';
-import { ReportMetadata } from './storage/types';
-import { defaultConfig } from './config';
-import { storage } from './storage';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import util from 'node:util';
+import { defaultConfig } from './config.js';
+import { storage } from './storage/index.js';
+import { REPORTS_FOLDER, TMP_FOLDER } from './storage/constants.js';
+import { createDirectory } from './storage/folders.js';
+import type { ReportMetadata } from './storage/types.js';
const execAsync = util.promisify(exec);
@@ -22,7 +21,7 @@ export const isValidPlaywrightVersion = (version?: string): boolean => {
export const generatePlaywrightReport = async (
reportId: UUID,
- metadata: ReportMetadata,
+ metadata: ReportMetadata
): Promise<{ reportPath: string }> => {
const { project, playwrightVersion } = metadata;
diff --git a/apps/backend/src/lib/schemas/index.ts b/apps/backend/src/lib/schemas/index.ts
new file mode 100644
index 00000000..4c495337
--- /dev/null
+++ b/apps/backend/src/lib/schemas/index.ts
@@ -0,0 +1,160 @@
+import { z } from 'zod';
+
+export const UUIDSchema = z.uuid();
+
+export const PaginationSchema = z.object({
+ limit: z.coerce.number().min(1).max(100).default(10),
+ offset: z.coerce.number().min(0).default(0),
+});
+
+export const ReportMetadataSchema = z.looseObject({
+ project: z.string().optional(),
+ title: z.string().optional(),
+ playwrightVersion: z.string().optional(),
+ testRun: z.string().optional(),
+ tags: z.array(z.string()).optional(),
+});
+
+export const ReportHistorySchema = z.looseObject({
+ reportID: UUIDSchema,
+ project: z.string(),
+ title: z.string().optional(),
+ createdAt: z.string(),
+ reportUrl: z.string(),
+ size: z.string().optional(),
+ sizeBytes: z.number(),
+ stats: z
+ .object({
+ total: z.number(),
+ expected: z.number(),
+ unexpected: z.number(),
+ flaky: z.number(),
+ skipped: z.number(),
+ ok: z.boolean(),
+ })
+ .optional(),
+});
+
+export const ResultDetailsSchema = z.looseObject({
+ resultID: UUIDSchema,
+ project: z.string().optional(),
+ title: z.string().optional(),
+ createdAt: z.string(),
+ size: z.string().optional(),
+ sizeBytes: z.number(),
+ playwrightVersion: z.string().optional(),
+ testRun: z.string().optional(),
+ shardCurrent: z.number().optional(),
+ shardTotal: z.number().optional(),
+ triggerReportGeneration: z.coerce.boolean().optional(),
+});
+
+export const GenerateReportRequestSchema = z.object({
+ resultsIds: z.array(z.string()).min(1),
+ project: z.string().optional(),
+ playwrightVersion: z.string().optional(),
+ title: z.string().optional(),
+});
+
+export const GenerateReportResponseSchema = z.object({
+ reportId: z.string(),
+ reportUrl: z.string(),
+ metadata: ReportMetadataSchema,
+});
+
+export const ListReportsQuerySchema = z.object({
+ project: z.string().default(''),
+ search: z.string().default(''),
+ limit: z.coerce.number().min(1).max(100).optional(),
+ offset: z.coerce.number().min(0).optional(),
+});
+
+export const ListReportsResponseSchema = z.object({
+ reports: z.array(ReportHistorySchema),
+ total: z.number(),
+});
+
+export const DeleteReportsRequestSchema = z.object({
+ reportsIds: z.array(z.string()).min(1),
+});
+
+export const DeleteReportsResponseSchema = z.object({
+ message: z.string(),
+ reportsIds: z.array(z.string()),
+});
+
+export const ListResultsQuerySchema = z.object({
+ project: z.string().default(''),
+ search: z.string().default(''),
+ tags: z.string().optional(), // comma-separated
+ limit: z.coerce.number().min(1).max(100).optional(),
+ offset: z.coerce.number().min(0).optional(),
+});
+
+export const ListResultsResponseSchema = z.object({
+ results: z.array(ResultDetailsSchema),
+ total: z.number(),
+});
+
+export const DeleteResultsRequestSchema = z.object({
+ resultsIds: z.array(z.string()).min(1),
+});
+
+export const DeleteResultsResponseSchema = z.object({
+ message: z.string(),
+ resultsIds: z.array(z.string()),
+});
+
+export const GetReportParamsSchema = z.object({
+ id: z.string(),
+});
+
+export const GetReportResponseSchema = ReportHistorySchema;
+
+export const UploadResultResponseSchema = z.object({
+ message: z.string(),
+ data: z.object({
+ resultID: UUIDSchema,
+ generatedReport: GenerateReportResponseSchema.optional().nullable(),
+ testRun: z.string().optional(),
+ }),
+});
+
+export const ServerInfoSchema = z.object({
+ dataFolderSizeinMB: z.string(),
+ numOfResults: z.number(),
+ resultsFolderSizeinMB: z.string(),
+ numOfReports: z.number(),
+ reportsFolderSizeinMB: z.string(),
+});
+
+export const ConfigSchema = z.looseObject({
+ siteName: z.string().optional(),
+ logoUrl: z.string().optional(),
+ theme: z.string().optional(),
+});
+
+export const ErrorResponseSchema = z.object({
+ error: z.string(),
+});
+
+export type GenerateReportRequest = z.infer;
+export type GenerateReportResponse = z.infer;
+export type ListReportsQuery = z.infer;
+export type ListReportsResponse = z.infer;
+export type DeleteReportsRequest = z.infer;
+export type DeleteReportsResponse = z.infer;
+export type ListResultsQuery = z.infer;
+export type ListResultsResponse = z.infer;
+export type DeleteResultsRequest = z.infer;
+export type DeleteResultsResponse = z.infer;
+export type GetReportParams = z.infer;
+export type GetReportResponse = z.infer;
+export type UploadResultResponse = z.infer;
+export type ServerInfo = z.infer;
+export type Config = z.infer;
+export type ErrorResponse = z.infer;
+export type ReportMetadata = z.infer;
+export type ReportHistory = z.infer;
+export type ResultDetails = z.infer;
+export type Pagination = z.infer;
diff --git a/app/lib/service/cache/config.ts b/apps/backend/src/lib/service/cache/config.ts
similarity index 58%
rename from app/lib/service/cache/config.ts
rename to apps/backend/src/lib/service/cache/config.ts
index d6527f3a..fc9db420 100644
--- a/app/lib/service/cache/config.ts
+++ b/apps/backend/src/lib/service/cache/config.ts
@@ -1,25 +1,26 @@
-import { storage } from '@/app/lib/storage';
-import { env } from '@/app/config/env';
-import { SiteWhiteLabelConfig } from '@/app/types';
-import { defaultConfig } from '@/app/lib/config';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import { defaultConfig } from '../../config.js';
+import { storage } from '../../storage/index.js';
+
+const initiatedConfigDb = Symbol.for('playwright.reports.db.config');
+const instance = globalThis as typeof globalThis & {
+ [initiatedConfigDb]?: ConfigCache;
+};
export class ConfigCache {
- private static instance: ConfigCache;
public initialized = false;
public config: SiteWhiteLabelConfig | undefined;
private constructor() {}
public static getInstance() {
- if (!ConfigCache.instance) {
- ConfigCache.instance = new ConfigCache();
- }
+ instance[initiatedConfigDb] ??= new ConfigCache();
- return ConfigCache.instance;
+ return instance[initiatedConfigDb];
}
public async init(): Promise {
- if (this.initialized || !env.USE_SERVER_CACHE) {
+ if (this.initialized) {
return;
}
@@ -42,12 +43,14 @@ export class ConfigCache {
}
public onChanged(config: SiteWhiteLabelConfig) {
- if (!env.USE_SERVER_CACHE) {
- return;
- }
-
this.config = config;
}
+
+ public refresh(): void {
+ console.log('[config cache] refreshing cache');
+ this.initialized = false;
+ this.config = undefined;
+ }
}
export const configCache = ConfigCache.getInstance();
diff --git a/app/lib/service/cron.ts b/apps/backend/src/lib/service/cron.ts
similarity index 80%
rename from app/lib/service/cron.ts
rename to apps/backend/src/lib/service/cron.ts
index d592dbbf..8abe575a 100644
--- a/app/lib/service/cron.ts
+++ b/apps/backend/src/lib/service/cron.ts
@@ -1,22 +1,23 @@
import { Cron } from 'croner';
+import { env } from '../../config/env.js';
+import { withError } from '../../lib/withError.js';
+import { service } from './index.js';
-import { service } from '@/app/lib/service';
-import { env } from '@/app/config/env';
-import { withError } from '@/app/lib/withError';
+const runningCron = Symbol.for('playwright.reports.cron.service');
+const instance = globalThis as typeof globalThis & {
+ [runningCron]?: CronService;
+};
export class CronService {
- private static instance: CronService;
public initialized = false;
private clearResultsJob: Cron | undefined;
private clearReportsJob: Cron | undefined;
public static getInstance() {
- if (!CronService.instance) {
- CronService.instance = new CronService();
- }
+ instance[runningCron] ??= new CronService();
- return CronService.instance;
+ return instance[runningCron];
}
private constructor() {
@@ -27,15 +28,12 @@ export class CronService {
public async restart() {
console.log('[cron-job] restarting cron tasks...');
- // Stop existing jobs
this.clearResultsJob?.stop();
this.clearReportsJob?.stop();
- // Recreate jobs with new settings
this.clearResultsJob = this.clearResultsTask();
this.clearReportsJob = this.clearReportsTask();
- // Reinitialize
this.initialized = false;
await this.init();
}
@@ -53,8 +51,10 @@ export class CronService {
const cfg = await service.getConfig();
const reportExpireDays = cfg.cron?.reportExpireDays || env.REPORT_EXPIRE_DAYS;
const resultExpireDays = cfg.cron?.resultExpireDays || env.RESULT_EXPIRE_DAYS;
- const reportExpireCronSchedule = cfg.cron?.reportExpireCronSchedule || env.REPORT_EXPIRE_CRON_SCHEDULE;
- const resultExpireCronSchedule = cfg.cron?.resultExpireCronSchedule || env.RESULT_EXPIRE_CRON_SCHEDULE;
+ const reportExpireCronSchedule =
+ cfg.cron?.reportExpireCronSchedule || env.REPORT_EXPIRE_CRON_SCHEDULE;
+ const resultExpireCronSchedule =
+ cfg.cron?.resultExpireCronSchedule || env.RESULT_EXPIRE_CRON_SCHEDULE;
console.log(`[cron-job] initiating cron tasks...`);
for (const schedule of [
@@ -92,7 +92,11 @@ export class CronService {
}
private createJob(scheduleExpression: string, task: () => Promise) {
- return new Cron(scheduleExpression, { catch: true, unref: true, paused: true, protect: true }, task);
+ return new Cron(
+ scheduleExpression,
+ { catch: true, unref: true, paused: true, protect: true },
+ task
+ );
}
private clearReportsTask() {
@@ -112,7 +116,8 @@ export class CronService {
const reportsOutput = await service.getReports();
const outdated = reportsOutput.reports.filter((report) => {
- const createdDate = typeof report.createdAt === 'string' ? new Date(report.createdAt) : report.createdAt;
+ const createdDate =
+ typeof report.createdAt === 'string' ? new Date(report.createdAt) : report.createdAt;
return expireDays ? this.isExpired(createdDate, expireDays) : false;
});
@@ -143,7 +148,10 @@ export class CronService {
const resultsOutput = await service.getResults();
const outdated = resultsOutput.results
- .map((result) => ({ ...result, createdDate: new Date(result.createdAt) }))
+ .map((result) => ({
+ ...result,
+ createdDate: new Date(result.createdAt),
+ }))
.filter((result) => (expireDays ? this.isExpired(result.createdDate, expireDays) : false));
console.log(`[cron-job] found ${outdated.length} outdated results`);
diff --git a/apps/backend/src/lib/service/db/db.ts b/apps/backend/src/lib/service/db/db.ts
new file mode 100644
index 00000000..b77e9cb4
--- /dev/null
+++ b/apps/backend/src/lib/service/db/db.ts
@@ -0,0 +1,175 @@
+import fs from 'node:fs';
+import path from 'node:path';
+
+import Database from 'better-sqlite3';
+
+const initiatedDb = Symbol.for('playwright.reports.db');
+const instance = globalThis as typeof globalThis & {
+ [initiatedDb]?: Database.Database;
+};
+
+export function createDatabase(): Database.Database {
+ if (instance[initiatedDb]) {
+ return instance[initiatedDb];
+ }
+
+ const dbDir = path.join(process.cwd(), 'data');
+
+ if (!fs.existsSync(dbDir)) {
+ fs.mkdirSync(dbDir, { recursive: true });
+ }
+
+ const dbPath = path.join(dbDir, 'metadata.db');
+
+ console.log(`[db] creating database at ${dbPath}`);
+
+ const db = new Database(dbPath, {
+ verbose: undefined, // for debugging: console.log
+ });
+
+ db.pragma('journal_mode = WAL'); // better concurrency
+ db.pragma('synchronous = NORMAL'); // faster writes, still safe with WAL
+ db.pragma('cache_size = -8000'); // 8MB page cache (balance of speed and memory)
+ db.pragma('mmap_size = 134217728'); // 128MB memory-mapped I/O
+ db.pragma('temp_store = MEMORY'); // store temporary tables in RAM
+ db.pragma('foreign_keys = ON'); // enforce referential integrity
+ db.pragma('auto_vacuum = INCREMENTAL'); // manage file size
+
+ console.log('[db] database is configured');
+
+ initializeSchema(db);
+ instance[initiatedDb] = db;
+
+ return db;
+}
+
+function initializeSchema(db: Database.Database): void {
+ console.log('[db] initializing schema');
+
+ db.exec(`
+ CREATE TABLE IF NOT EXISTS results (
+ resultID TEXT PRIMARY KEY,
+ project TEXT NOT NULL,
+ title TEXT,
+ createdAt TEXT NOT NULL,
+ size TEXT,
+ sizeBytes INTEGER,
+ metadata TEXT, -- JSON string for additional metadata
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
+ );
+
+ -- Indexes for common queries
+ CREATE INDEX IF NOT EXISTS idx_results_ids ON results(resultID);
+ CREATE INDEX IF NOT EXISTS idx_results_project ON results(project);
+ CREATE INDEX IF NOT EXISTS idx_results_createdAt ON results(createdAt DESC);
+ CREATE INDEX IF NOT EXISTS idx_results_updatedAt ON results(updatedAt DESC);
+ `);
+
+ db.exec(`
+ CREATE TABLE IF NOT EXISTS reports (
+ reportID TEXT PRIMARY KEY,
+ project TEXT NOT NULL,
+ title TEXT,
+ createdAt TEXT NOT NULL,
+ reportUrl TEXT NOT NULL,
+ size TEXT,
+ sizeBytes INTEGER,
+ stats TEXT, -- JSON string for report stats
+ metadata TEXT, -- JSON string for additional metadata
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
+ );
+
+ -- Indexes for common queries
+ CREATE INDEX IF NOT EXISTS idx_reports_ids ON reports(reportID);
+ CREATE INDEX IF NOT EXISTS idx_reports_project ON reports(project);
+ CREATE INDEX IF NOT EXISTS idx_reports_createdAt ON reports(createdAt DESC);
+ CREATE INDEX IF NOT EXISTS idx_reports_updatedAt ON reports(updatedAt DESC);
+ `);
+
+ db.exec(`
+ CREATE TABLE IF NOT EXISTS cache_metadata (
+ key TEXT PRIMARY KEY,
+ value TEXT NOT NULL,
+ updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
+ );
+ `);
+
+ console.log('[db] schema initialized');
+}
+
+export function getDatabase(): Database.Database {
+ if (!instance[initiatedDb]) {
+ return createDatabase();
+ }
+
+ return instance[initiatedDb];
+}
+
+export function closeDatabase(): void {
+ if (instance[initiatedDb]) {
+ console.log('[db] closing database connection');
+ const db = getDatabase();
+
+ db.close();
+ instance[initiatedDb] = undefined;
+ }
+}
+
+export function getDatabaseStats(): {
+ results: number;
+ reports: number;
+ sizeOnDisk: string;
+ estimatedRAM: string;
+} {
+ const db = getDatabase();
+
+ const resultsCount = db.prepare('SELECT COUNT(*) as count FROM results').get() as {
+ count: number;
+ };
+ const reportsCount = db.prepare('SELECT COUNT(*) as count FROM reports').get() as {
+ count: number;
+ };
+
+ const stats = {
+ pageCount: db.pragma('page_count', { simple: true }) as number,
+ pageSize: db.pragma('page_size', { simple: true }) as number,
+ cacheSize: db.pragma('cache_size', { simple: true }) as number,
+ };
+
+ const dbSizeBytes = stats.pageCount * stats.pageSize;
+ const cacheSizeBytes = Math.abs(stats.cacheSize) * (stats.cacheSize < 0 ? 1024 : stats.pageSize);
+
+ return {
+ results: resultsCount.count,
+ reports: reportsCount.count,
+ sizeOnDisk: `${(dbSizeBytes / 1024 / 1024).toFixed(2)} MB`,
+ estimatedRAM: `~${(cacheSizeBytes / 1024 / 1024).toFixed(2)} MB`,
+ };
+}
+
+export function clearAll(): void {
+ const db = getDatabase();
+
+ console.log('[db] clearing all data');
+
+ db.exec(`
+ DELETE FROM results;
+ DELETE FROM reports;
+ DELETE FROM cache_metadata;
+ `);
+
+ db.exec('VACUUM;');
+
+ console.log('[db] cleared');
+}
+
+export function optimizeDB(): void {
+ const db = getDatabase();
+
+ console.log('[db] optimizing database');
+
+ db.exec('ANALYZE;');
+ db.exec('PRAGMA incremental_vacuum;');
+
+ console.log('[db] optimization complete');
+}
diff --git a/apps/backend/src/lib/service/db/forceInit.ts b/apps/backend/src/lib/service/db/forceInit.ts
new file mode 100644
index 00000000..7cfff516
--- /dev/null
+++ b/apps/backend/src/lib/service/db/forceInit.ts
@@ -0,0 +1,21 @@
+import { reportDb, resultDb } from './index.js';
+import { withError } from '../../withError.js';
+
+export const forceInitDatabase = async () => {
+ const { error: cleanupError } = await withError(
+ Promise.all([reportDb.clear(), resultDb.clear()])
+ );
+
+ if (cleanupError) {
+ throw new Error(`failed to clear db: ${cleanupError.message}`);
+ }
+
+ reportDb.initialized = false;
+ resultDb.initialized = false;
+
+ const { error } = await withError(Promise.all([reportDb.init(), resultDb.init()]));
+
+ if (error) {
+ throw new Error(`failed to initialize db: ${error.message}`);
+ }
+};
diff --git a/apps/backend/src/lib/service/db/index.ts b/apps/backend/src/lib/service/db/index.ts
new file mode 100644
index 00000000..1e896822
--- /dev/null
+++ b/apps/backend/src/lib/service/db/index.ts
@@ -0,0 +1,4 @@
+export * from './db.js';
+export * from './forceInit.js';
+export * from './reports.sqlite.js';
+export * from './results.sqlite.js';
diff --git a/apps/backend/src/lib/service/db/reports.sqlite.ts b/apps/backend/src/lib/service/db/reports.sqlite.ts
new file mode 100644
index 00000000..7915554c
--- /dev/null
+++ b/apps/backend/src/lib/service/db/reports.sqlite.ts
@@ -0,0 +1,335 @@
+import type Database from 'better-sqlite3';
+import { storage } from '../../storage/index.js';
+import type { ReadReportsInput, ReadReportsOutput, ReportHistory } from '../../storage/types.js';
+import { withError } from '../../withError.js';
+import { getDatabase } from './db.js';
+
+const initiatedReportsDb = Symbol.for('playwright.reports.db.reports');
+const instance = globalThis as typeof globalThis & {
+ [initiatedReportsDb]?: ReportDatabase;
+};
+
+export class ReportDatabase {
+ public initialized = false;
+ private readonly db = getDatabase();
+
+ private readonly insertStmt: Database.Statement<
+ [string, string, string | null, string, string, string | null, number, string | null, string]
+ >;
+ private readonly updateStmt: Database.Statement<
+ [string, string | null, string, string | null, number, string | null, string, string]
+ >;
+ private readonly deleteStmt: Database.Statement<[string]>;
+ private readonly getByIDStmt: Database.Statement<[string]>;
+ private readonly getAllStmt: Database.Statement<[]>;
+ private readonly getByProjectStmt: Database.Statement<[string]>;
+ private readonly searchStmt: Database.Statement<[string, string, string, string]>;
+
+ private constructor() {
+ this.insertStmt = this.db.prepare(`
+ INSERT OR REPLACE INTO reports (reportID, project, title, createdAt, reportUrl, size, sizeBytes, stats, metadata, updatedAt)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
+ `);
+
+ this.updateStmt = this.db.prepare(`
+ UPDATE reports
+ SET project = ?, title = ?, reportUrl = ?, size = ?, sizeBytes = ?, stats = ?, metadata = ?, updatedAt = CURRENT_TIMESTAMP
+ WHERE reportID = ?
+ `);
+
+ this.deleteStmt = this.db.prepare('DELETE FROM reports WHERE reportID = ?');
+
+ this.getByIDStmt = this.db.prepare('SELECT * FROM reports WHERE reportID = ?');
+
+ this.getAllStmt = this.db.prepare('SELECT * FROM reports ORDER BY createdAt DESC');
+
+ this.getByProjectStmt = this.db.prepare(
+ 'SELECT * FROM reports WHERE project = ? ORDER BY createdAt DESC'
+ );
+
+ this.searchStmt = this.db.prepare(`
+ SELECT * FROM reports
+ WHERE title LIKE ? OR reportID LIKE ? OR project LIKE ? OR metadata LIKE ?
+ ORDER BY createdAt DESC
+ `);
+ }
+
+ public static getInstance(): ReportDatabase {
+ instance[initiatedReportsDb] ??= new ReportDatabase();
+
+ return instance[initiatedReportsDb];
+ }
+
+ public async init() {
+ if (this.initialized) {
+ return;
+ }
+
+ console.log('[report db] initializing SQLite for reports');
+ const { result, error } = await withError(storage.readReports());
+
+ if (error) {
+ console.error('[report db] failed to read reports:', error);
+
+ return;
+ }
+
+ if (!result?.reports?.length) {
+ console.log('[report db] no reports to store');
+ this.initialized = true;
+
+ return;
+ }
+
+ console.log(`[report db] caching ${result.reports.length} reports`);
+
+ const insertMany = this.db.transaction((reports: ReportHistory[]) => {
+ for (const report of reports) {
+ this.insertReport(report);
+ }
+ });
+
+ insertMany(result.reports as ReportHistory[]);
+
+ this.initialized = true;
+ console.log('[report db] initialization complete');
+ }
+
+ private insertReport(report: ReportHistory): void {
+ const { reportID, project, title, createdAt, reportUrl, size, sizeBytes, stats, ...metadata } =
+ report;
+
+ const createdAtStr =
+ createdAt instanceof Date
+ ? createdAt.toDateString()
+ : typeof createdAt === 'string'
+ ? createdAt
+ : String(createdAt);
+
+ this.insertStmt.run(
+ reportID,
+ project || '',
+ title || null,
+ createdAtStr,
+ reportUrl,
+ size || null,
+ sizeBytes || 0,
+ stats ? JSON.stringify(stats) : null,
+ JSON.stringify(metadata)
+ );
+ }
+
+ public onDeleted(reportIds: string[]) {
+ console.log(`[report db] deleting ${reportIds.length} reports`);
+
+ const deleteMany = this.db.transaction((ids: string[]) => {
+ for (const id of ids) {
+ this.deleteStmt.run(id);
+ }
+ });
+
+ deleteMany(reportIds);
+ }
+
+ public onCreated(report: ReportHistory) {
+ console.log(`[report db] adding report ${report.reportID}`);
+ this.insertReport(report);
+ }
+
+ public onUpdated(report: ReportHistory) {
+ console.log(`[report db] updating report ${report.reportID}`);
+ const { reportID, project, title, reportUrl, size, sizeBytes, stats, ...metadata } = report;
+
+ this.updateStmt.run(
+ project || '',
+ title || null,
+ reportUrl,
+ size || null,
+ sizeBytes || 0,
+ stats ? JSON.stringify(stats) : null,
+ JSON.stringify(metadata),
+ reportID
+ );
+ }
+
+ public getAll(): ReportHistory[] {
+ const rows = this.getAllStmt.all() as Array<{
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToReport);
+ }
+
+ public getByID(reportID: string): ReportHistory | undefined {
+ const row = this.getByIDStmt.get(reportID) as
+ | {
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }
+ | undefined;
+
+ return row ? this.rowToReport(row) : undefined;
+ }
+
+ public getByProject(project: string): ReportHistory[] {
+ const rows = this.getByProjectStmt.all(project) as Array<{
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToReport);
+ }
+
+ public search(query: string): ReportHistory[] {
+ const searchPattern = `%${query}%`;
+ const rows = this.searchStmt.all(
+ searchPattern,
+ searchPattern,
+ searchPattern,
+ searchPattern
+ ) as Array<{
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToReport);
+ }
+
+ public getCount(): number {
+ const result = this.db.prepare('SELECT COUNT(*) as count FROM reports').get() as {
+ count: number;
+ };
+
+ return result.count;
+ }
+
+ public clear(): void {
+ console.log('[report db] clearing all reports');
+ this.db.prepare('DELETE FROM reports').run();
+ }
+
+ public query(input?: ReadReportsInput): ReadReportsOutput {
+ let query = 'SELECT * FROM reports';
+ const params: string[] = [];
+ const conditions: string[] = [];
+
+ if (input?.ids && input.ids.length > 0) {
+ conditions.push(`reportID IN (${input.ids.map(() => '?').join(', ')})`);
+ params.push(...input.ids);
+ }
+
+ if (input?.project) {
+ conditions.push('project = ?');
+ params.push(input.project);
+ }
+
+ if (input?.search?.trim()) {
+ const searchTerm = `%${input.search.toLowerCase().trim()}%`;
+
+ conditions.push(
+ '(LOWER(title) LIKE ? OR LOWER(reportID) LIKE ? OR LOWER(project) LIKE ? OR LOWER(metadata) LIKE ?)'
+ );
+ params.push(searchTerm, searchTerm, searchTerm, searchTerm);
+ }
+
+ if (conditions.length > 0) {
+ query += ` WHERE ${conditions.join(' AND ')}`;
+ }
+
+ query += ' ORDER BY createdAt DESC';
+
+ const countQuery = query.replace('SELECT *', 'SELECT COUNT(*) as count');
+ const countResult = this.db.prepare(countQuery).get(...params) as {
+ count: number;
+ };
+ const total = countResult.count;
+
+ if (input?.pagination) {
+ query += ' LIMIT ? OFFSET ?';
+ params.push(input.pagination.limit.toString(), input.pagination.offset.toString());
+ }
+
+ const rows = this.db.prepare(query).all(...params) as Array<{
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }>;
+
+ return {
+ reports: rows.map((row) => this.rowToReport(row)),
+ total,
+ };
+ }
+
+ public async refresh(): Promise {
+ console.log('[report db] refreshing cache');
+ this.clear();
+ this.initialized = false;
+ // Re-initialize immediately by reading from filesystem
+ await this.init();
+ }
+
+ private rowToReport(row: {
+ reportID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ reportUrl: string;
+ size: string | null;
+ sizeBytes: number;
+ stats: string | null;
+ metadata: string;
+ }): ReportHistory {
+ const metadata = JSON.parse(row.metadata || '{}');
+ const stats = row.stats ? JSON.parse(row.stats) : undefined;
+
+ return {
+ reportID: row.reportID,
+ project: row.project,
+ title: row.title || undefined,
+ createdAt: row.createdAt,
+ reportUrl: row.reportUrl,
+ size: row.size || undefined,
+ sizeBytes: row.sizeBytes,
+ stats,
+ ...metadata,
+ };
+ }
+}
+
+export const reportDb = ReportDatabase.getInstance();
diff --git a/apps/backend/src/lib/service/db/results.sqlite.ts b/apps/backend/src/lib/service/db/results.sqlite.ts
new file mode 100644
index 00000000..a4e2d3fe
--- /dev/null
+++ b/apps/backend/src/lib/service/db/results.sqlite.ts
@@ -0,0 +1,321 @@
+import type Database from 'better-sqlite3';
+import { storage } from '../../storage/index.js';
+import type { ReadResultsInput, ReadResultsOutput, Result } from '../../storage/types.js';
+import { withError } from '../../withError.js';
+import { getDatabase } from './db.js';
+
+const initiatedResultsDb = Symbol.for('playwright.reports.db.results');
+const instance = globalThis as typeof globalThis & {
+ [initiatedResultsDb]?: ResultDatabase;
+};
+
+export class ResultDatabase {
+ public initialized = false;
+ private readonly db = getDatabase();
+
+ private readonly insertStmt: Database.Statement<
+ [string, string, string | null, string, string | null, number, string]
+ >;
+ private readonly updateStmt: Database.Statement<
+ [string, string | null, string | null, number, string, string]
+ >;
+ private readonly deleteStmt: Database.Statement<[string]>;
+ private readonly getByIDStmt: Database.Statement<[string]>;
+ private readonly getAllStmt: Database.Statement<[]>;
+ private readonly getByProjectStmt: Database.Statement<[string]>;
+ private readonly searchStmt: Database.Statement<[string, string, string, string]>;
+
+ private constructor() {
+ this.insertStmt = this.db.prepare(`
+ INSERT OR REPLACE INTO results (resultID, project, title, createdAt, size, sizeBytes, metadata, updatedAt)
+ VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
+ `);
+
+ this.updateStmt = this.db.prepare(`
+ UPDATE results
+ SET project = ?, title = ?, size = ?, sizeBytes = ?, metadata = ?, updatedAt = CURRENT_TIMESTAMP
+ WHERE resultID = ?
+ `);
+
+ this.deleteStmt = this.db.prepare('DELETE FROM results WHERE resultID = ?');
+
+ this.getByIDStmt = this.db.prepare('SELECT * FROM results WHERE resultID = ?');
+
+ this.getAllStmt = this.db.prepare('SELECT * FROM results ORDER BY createdAt DESC');
+
+ this.getByProjectStmt = this.db.prepare(
+ 'SELECT * FROM results WHERE project = ? ORDER BY createdAt DESC'
+ );
+
+ this.searchStmt = this.db.prepare(`
+ SELECT * FROM results
+ WHERE title LIKE ? OR resultID LIKE ? OR project LIKE ? OR metadata LIKE ?
+ ORDER BY createdAt DESC
+ `);
+ }
+
+ public static getInstance(): ResultDatabase {
+ instance[initiatedResultsDb] ??= new ResultDatabase();
+
+ return instance[initiatedResultsDb];
+ }
+
+ public async init() {
+ if (this.initialized) {
+ return;
+ }
+
+ console.log('[result db] initializing SQLite for results');
+ const { result: resultsResponse, error } = await withError(storage.readResults());
+
+ if (error) {
+ console.error('[result db] failed to read results:', error);
+
+ return;
+ }
+
+ if (!resultsResponse?.results?.length) {
+ console.log('[result db] no results to store');
+ this.initialized = true;
+
+ return;
+ }
+
+ console.log(`[result db] caching ${resultsResponse.results.length} results`);
+
+ const insertMany = this.db.transaction((results: Result[]) => {
+ for (const result of results) {
+ this.insertResult(result);
+ }
+ });
+
+ insertMany(resultsResponse.results);
+
+ this.initialized = true;
+ console.log('[result db] initialization complete');
+ }
+
+ private insertResult(result: Result): void {
+ const { resultID, project, title, createdAt, size, sizeBytes, ...metadata } = result;
+
+ this.insertStmt.run(
+ resultID,
+ project || '',
+ title || null,
+ createdAt,
+ size || null,
+ sizeBytes || 0,
+ JSON.stringify(metadata)
+ );
+ }
+
+ public onDeleted(resultIds: string[]) {
+ console.log(`[result db] deleting ${resultIds.length} results`);
+
+ const deleteMany = this.db.transaction((ids: string[]) => {
+ for (const id of ids) {
+ this.deleteStmt.run(id);
+ }
+ });
+
+ deleteMany(resultIds);
+ }
+
+ public onCreated(result: Result) {
+ console.log(`[result db] adding result ${result.resultID}`);
+ this.insertResult(result);
+ }
+
+ public onUpdated(result: Result) {
+ console.log(`[result db] updating result ${result.resultID}`);
+ const { resultID, project, title, size, sizeBytes, ...metadata } = result;
+
+ this.updateStmt.run(
+ project || '',
+ title || null,
+ size || null,
+ sizeBytes || 0,
+ JSON.stringify(metadata),
+ resultID
+ );
+ }
+
+ public getAll(): Result[] {
+ const rows = this.getAllStmt.all() as Array<{
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToResult);
+ }
+
+ public getByID(resultID: string): Result | undefined {
+ const row = this.getByIDStmt.get(resultID) as
+ | {
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }
+ | undefined;
+
+ return row ? this.rowToResult(row) : undefined;
+ }
+
+ public getByProject(project: string): Result[] {
+ const rows = this.getByProjectStmt.all(project) as Array<{
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToResult);
+ }
+
+ public search(query: string): Result[] {
+ const searchPattern = `%${query}%`;
+ const rows = this.searchStmt.all(
+ searchPattern,
+ searchPattern,
+ searchPattern,
+ searchPattern
+ ) as Array<{
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }>;
+
+ return rows.map(this.rowToResult);
+ }
+
+ public getCount(): number {
+ const result = this.db.prepare('SELECT COUNT(*) as count FROM results').get() as {
+ count: number;
+ };
+
+ return result.count;
+ }
+
+ public clear(): void {
+ console.log('[result db] clearing all results');
+ this.db.prepare('DELETE FROM results').run();
+ }
+
+ public query(input?: ReadResultsInput): ReadResultsOutput {
+ let query = 'SELECT * FROM results';
+ const params: string[] = [];
+ const conditions: string[] = [];
+
+ if (input?.project) {
+ conditions.push('project = ?');
+ params.push(input.project);
+ }
+
+ if (input?.testRun) {
+ conditions.push('metadata LIKE ?');
+ params.push(`%"testRun":"${input.testRun}"%`);
+ }
+
+ if (input?.tags && input.tags.length > 0) {
+ console.log('Filtering by tags:', input.tags);
+
+ for (const tag of input.tags) {
+ const [key, value] = tag.split(':').map((part) => part.trim());
+
+ conditions.push('metadata LIKE ?');
+ params.push(`%"${key}":"${value}"%`);
+ }
+ }
+
+ if (input?.search?.trim()) {
+ const searchTerm = `%${input.search.toLowerCase().trim()}%`;
+
+ conditions.push(
+ '(LOWER(title) LIKE ? OR LOWER(resultID) LIKE ? OR LOWER(project) LIKE ? OR LOWER(metadata) LIKE ?)'
+ );
+ params.push(searchTerm, searchTerm, searchTerm, searchTerm);
+ }
+
+ if (conditions.length > 0) {
+ query += ` WHERE ${conditions.join(' AND ')}`;
+ }
+
+ query += ' ORDER BY createdAt DESC';
+
+ const countQuery = query.replace('SELECT *', 'SELECT COUNT(*) as count');
+ const countResult = this.db.prepare(countQuery).get(...params) as {
+ count: number;
+ };
+ const total = countResult.count;
+
+ if (input?.pagination) {
+ query += ' LIMIT ? OFFSET ?';
+ params.push(input.pagination.limit.toString(), input.pagination.offset.toString());
+ }
+
+ console.log('Executing query:', query, 'with params:', params);
+
+ const rows = this.db.prepare(query).all(...params) as Array<{
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }>;
+
+ return {
+ results: rows.map((row) => this.rowToResult(row)),
+ total,
+ };
+ }
+
+ public async refresh(): Promise {
+ console.log('[result db] refreshing cache');
+ this.clear();
+ this.initialized = false;
+ // Re-initialize immediately by reading from filesystem
+ await this.init();
+ }
+
+ private rowToResult(row: {
+ resultID: string;
+ project: string;
+ title: string | null;
+ createdAt: string;
+ size: string | null;
+ sizeBytes: number;
+ metadata: string;
+ }): Result {
+ const metadata = JSON.parse(row.metadata || '{}');
+
+ return {
+ resultID: row.resultID,
+ project: row.project,
+ title: row.title || undefined,
+ createdAt: row.createdAt,
+ size: row.size || undefined,
+ sizeBytes: row.sizeBytes,
+ ...metadata,
+ };
+ }
+}
+
+export const resultDb = ResultDatabase.getInstance();
diff --git a/apps/backend/src/lib/service/index.ts b/apps/backend/src/lib/service/index.ts
new file mode 100644
index 00000000..89cab81f
--- /dev/null
+++ b/apps/backend/src/lib/service/index.ts
@@ -0,0 +1,375 @@
+import { type PassThrough, Readable } from 'node:stream';
+import { env } from '../../config/env.js';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import { defaultConfig } from '../config.js';
+import { serveReportRoute } from '../constants.js';
+import { isValidPlaywrightVersion } from '../pw.js';
+import { DEFAULT_STREAM_CHUNK_SIZE } from '../storage/constants.js';
+import { bytesToString, getUniqueProjectsList } from '../storage/format.js';
+import {
+ type ReadReportsInput,
+ type ReadResultsInput,
+ type ReadResultsOutput,
+ type ReportMetadata,
+ type ReportPath,
+ type ResultDetails,
+ type ServerDataInfo,
+ storage,
+} from '../storage/index.js';
+import type { S3 } from '../storage/s3.js';
+import { withError } from '../withError.js';
+import { configCache } from './cache/config.js';
+import { reportDb, resultDb } from './db/index.js';
+import { lifecycle } from './lifecycle.js';
+
+class Service {
+ private static instance: Service | null = null;
+
+ public static getInstance(): Service {
+ Service.instance ??= new Service();
+ return Service.instance;
+ }
+
+ public async getReports(input?: ReadReportsInput) {
+ console.log(`[service] getReports`);
+
+ return reportDb.query(input);
+ }
+
+ public async getReport(id: string, path?: string) {
+ console.log(`[service] getReport ${id}`);
+
+ const report = reportDb.getByID(id);
+
+ if (!report && path) {
+ console.warn(`[service] getReport ${id} - not found in db, fetching from storage`);
+ const { result: reportFromStorage, error } = await withError(storage.readReport(id, path));
+
+ if (error) {
+ console.error(`[service] getReport ${id} - error fetching from storage: ${error.message}`);
+ throw error;
+ }
+
+ if (!reportFromStorage) {
+ throw new Error(`report ${id} not found`);
+ }
+
+ return reportFromStorage;
+ }
+
+ if (!report) {
+ throw new Error(`report ${id} not found`);
+ }
+
+ return report;
+ }
+
+ private async findLatestPlaywrightVersionFromResults(resultIds: string[]) {
+ for (const resultId of resultIds) {
+ const { result: results, error } = await withError(this.getResults({ search: resultId }));
+
+ if (error || !results) {
+ continue;
+ }
+
+ const [latestResult] = results.results;
+
+ if (!latestResult) {
+ continue;
+ }
+
+ const latestVersion = latestResult?.playwrightVersion;
+
+ if (latestVersion) {
+ return latestVersion;
+ }
+ }
+ }
+
+ private async findLatestPlaywrightVersion(resultIds: string[]) {
+ const versionFromResults = await this.findLatestPlaywrightVersionFromResults(resultIds);
+
+ if (versionFromResults) {
+ return versionFromResults;
+ }
+
+ // just in case version not found in results, we can try to get it from latest reports
+ const { result: reportsArray, error } = await withError(
+ this.getReports({ pagination: { limit: 10, offset: 0 } })
+ );
+
+ if (error || !reportsArray) {
+ return '';
+ }
+
+ const reportWithVersion = reportsArray.reports.find(
+ (report) => !!report.metadata?.playwrightVersion
+ );
+
+ if (!reportWithVersion) {
+ return '';
+ }
+
+ return reportWithVersion.metadata.playwrightVersion;
+ }
+
+ public async generateReport(
+ resultsIds: string[],
+ metadata?: ReportMetadata
+ ): Promise<{
+ reportId: string;
+ reportUrl: string;
+ metadata: ReportMetadata;
+ }> {
+ const version = isValidPlaywrightVersion(metadata?.playwrightVersion)
+ ? metadata?.playwrightVersion
+ : await this.findLatestPlaywrightVersion(resultsIds);
+
+ const metadataWithVersion = {
+ ...(metadata ?? {}),
+ playwrightVersion: version ?? '',
+ };
+
+ const { reportId, reportPath } = await storage.generateReport(resultsIds, metadataWithVersion);
+
+ console.log(`[service] reading report ${reportId} from path: ${reportPath}`);
+ const { result: report, error } = await withError(storage.readReport(reportId, reportPath));
+
+ if (error) {
+ throw new Error(`Failed to read generated report: ${error.message}`);
+ }
+
+ if (!report) {
+ throw new Error(`Generated report ${reportId} not found`);
+ }
+
+ reportDb.onCreated(report);
+
+ const projectPath = metadata?.project ? `${encodeURI(metadata.project)}/` : '';
+ const reportUrl = `${serveReportRoute}/${projectPath}${reportId}/index.html`;
+
+ return { reportId, reportUrl, metadata: metadataWithVersion };
+ }
+
+ public async deleteReports(reportIDs: string[]) {
+ const entries: ReportPath[] = [];
+
+ for (const id of reportIDs) {
+ const report = await this.getReport(id);
+
+ entries.push({ reportID: id, project: report.project });
+ }
+
+ const { error } = await withError(storage.deleteReports(entries));
+
+ if (error) {
+ throw error;
+ }
+
+ reportDb.onDeleted(reportIDs);
+ }
+
+ public async getReportsProjects(): Promise {
+ const { reports } = await this.getReports();
+ const projects = getUniqueProjectsList(reports);
+
+ return projects;
+ }
+
+ public async getResults(input?: ReadResultsInput): Promise {
+ console.log(`[results service] getResults`);
+ console.log(`querying results:`);
+ console.log(JSON.stringify(input, null, 2));
+
+ return resultDb.query(input);
+ }
+
+ public async deleteResults(resultIDs: string[]): Promise {
+ console.log(`[service] deleteResults`);
+ console.log(`deleting results:`, resultIDs);
+
+ const { error } = await withError(storage.deleteResults(resultIDs));
+
+ if (error) {
+ console.error(`[service] deleteResults - storage deletion failed:`, error);
+ throw error;
+ }
+
+ console.log(
+ `[service] deleteResults - storage deletion successful, removing from database cache`
+ );
+ resultDb.onDeleted(resultIDs);
+ console.log(`[service] deleteResults - database cache cleanup completed`);
+ }
+
+ public async getPresignedUrl(fileName: string): Promise {
+ console.log(`[service] getPresignedUrl for ${fileName}`);
+
+ if (env.DATA_STORAGE !== 's3') {
+ console.log(`[service] fs storage detected, no presigned URL needed`);
+
+ return '';
+ }
+
+ console.log(`[service] s3 detected, generating presigned URL`);
+
+ const { result: presignedUrl, error } = await withError(
+ (storage as S3).generatePresignedUploadUrl(fileName)
+ );
+
+ if (error) {
+ console.error(`[service] getPresignedUrl | error: ${error.message}`);
+
+ return '';
+ }
+
+ if (!presignedUrl) {
+ console.error(`[service] getPresignedUrl | presigned URL is null or undefined`);
+
+ return '';
+ }
+
+ return presignedUrl;
+ }
+
+ public async saveResult(
+ filename: string,
+ stream: PassThrough,
+ presignedUrl?: string,
+ contentLength?: string
+ ) {
+ if (!presignedUrl) {
+ console.log(`[service] saving result`);
+
+ return await storage.saveResult(filename, stream);
+ }
+
+ console.log(`[service] using direct upload via presigned URL`, presignedUrl);
+
+ const { error } = await withError(
+ fetch(presignedUrl, {
+ method: 'PUT',
+ body: Readable.toWeb(stream, {
+ strategy: {
+ highWaterMark: DEFAULT_STREAM_CHUNK_SIZE,
+ },
+ }),
+ headers: {
+ 'Content-Type': 'application/zip',
+ 'Content-Length': contentLength,
+ },
+ duplex: 'half',
+ } as RequestInit)
+ );
+
+ if (error) {
+ console.error(`[s3] saveResult | error: ${error.message}`);
+ throw error;
+ }
+ }
+
+ public async saveResultDetails(resultID: string, resultDetails: ResultDetails, size: number) {
+ const result = await storage.saveResultDetails(resultID, resultDetails, size);
+
+ resultDb.onCreated(result);
+
+ return result;
+ }
+
+ public async getResultsProjects(): Promise {
+ const { results } = await this.getResults();
+ const projects = getUniqueProjectsList(results);
+
+ const reportProjects = await this.getReportsProjects();
+
+ return Array.from(new Set([...projects, ...reportProjects]));
+ }
+
+ public async getResultsTags(project?: string): Promise {
+ const { results } = await this.getResults(project ? { project } : undefined);
+
+ const notMetadataKeys = ['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'];
+ const allTags = new Set();
+
+ results.forEach((result) => {
+ Object.entries(result).forEach(([key, value]) => {
+ if (!notMetadataKeys.includes(key) && value !== undefined && value !== null) {
+ allTags.add(`${key}: ${value}`);
+ }
+ });
+ });
+
+ return Array.from(allTags).sort();
+ }
+
+ public async getServerInfo(): Promise {
+ console.log(`[service] getServerInfo`);
+ const canCalculateFromCache =
+ lifecycle.isInitialized() && reportDb.initialized && resultDb.initialized;
+
+ if (!canCalculateFromCache) {
+ return await storage.getServerDataInfo();
+ }
+
+ const reports = reportDb.getAll();
+ const results = resultDb.getAll();
+
+ const getTotalSizeBytes = (entity: T) =>
+ entity.reduce((total, item) => total + item.sizeBytes, 0);
+
+ const reportsFolderSize = getTotalSizeBytes(reports);
+ const resultsFolderSize = getTotalSizeBytes(results);
+ const dataFolderSize = reportsFolderSize + resultsFolderSize;
+
+ return {
+ dataFolderSizeinMB: bytesToString(dataFolderSize),
+ numOfResults: results.length,
+ resultsFolderSizeinMB: bytesToString(resultsFolderSize),
+ numOfReports: reports.length,
+ reportsFolderSizeinMB: bytesToString(reportsFolderSize),
+ };
+ }
+
+ public async getConfig() {
+ if (lifecycle.isInitialized() && configCache.initialized) {
+ const cached = configCache.config;
+
+ if (cached) {
+ console.log(`[service] using cached config`);
+
+ return cached;
+ }
+ }
+
+ const { result, error } = await storage.readConfigFile();
+
+ if (error) console.error(`[service] getConfig | error: ${error.message}`);
+
+ return { ...defaultConfig, ...(result ?? {}) };
+ }
+
+ public async updateConfig(config: Partial) {
+ console.log(`[service] updateConfig`, config);
+ const { result, error } = await storage.saveConfigFile(config);
+
+ if (error) {
+ throw error;
+ }
+
+ configCache.onChanged(result);
+
+ return result;
+ }
+
+ public async refreshCache() {
+ console.log(`[service] refreshCache`);
+
+ await reportDb.refresh();
+ await resultDb.refresh();
+ configCache.refresh();
+
+ return { message: 'cache refreshed successfully' };
+ }
+}
+
+export const service = Service.getInstance();
diff --git a/app/lib/service/jira.ts b/apps/backend/src/lib/service/jira.ts
similarity index 88%
rename from app/lib/service/jira.ts
rename to apps/backend/src/lib/service/jira.ts
index 28018b04..e4603932 100644
--- a/app/lib/service/jira.ts
+++ b/apps/backend/src/lib/service/jira.ts
@@ -1,5 +1,5 @@
-import { env } from '@/app/config/env';
-import { JiraConfig } from '@/app/types';
+import { env } from '../../config/env.js';
+import type { JiraConfig } from '@playwright-reports/shared';
export interface JiraIssueFields {
summary: string;
@@ -18,7 +18,7 @@ export interface JiraIssueFields {
};
issuetype: { name?: string; id?: string };
project: { key: string };
- [key: string]: any;
+ [key: string]: unknown;
}
export interface JiraCreateIssueRequest {
@@ -36,13 +36,16 @@ export interface JiraErrorResponse {
errors: Record;
}
+const initiatedJira = Symbol.for('playwright.reports.jira');
+const _instance = globalThis as typeof globalThis & {
+ [initiatedJira]?: JiraService;
+};
+
export class JiraService {
- private static instance: JiraService;
private baseUrl: string;
private auth: string;
private constructor(jiraConfig?: JiraConfig) {
- // Use config if provided, otherwise fall back to environment variables
const config = jiraConfig || {
baseUrl: env.JIRA_BASE_URL,
email: env.JIRA_EMAIL,
@@ -56,7 +59,7 @@ export class JiraService {
if (!this.baseUrl || !email || !apiToken) {
throw new Error(
- 'Jira configuration is incomplete. Please configure Jira settings in the admin panel or set JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.',
+ 'Jira configuration is incomplete. Please configure Jira settings in the admin panel or set JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.'
);
}
@@ -64,21 +67,19 @@ export class JiraService {
}
public static getInstance(jiraConfig?: JiraConfig): JiraService {
- if (!JiraService.instance) {
- JiraService.instance = new JiraService(jiraConfig);
- }
+ _instance[initiatedJira] ??= new JiraService(jiraConfig);
- return JiraService.instance;
+ return _instance[initiatedJira];
}
public static resetInstance(): void {
- JiraService.instance = undefined as any;
+ _instance[initiatedJira] = undefined;
}
private async makeRequest(
endpoint: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
- body?: any,
+ body?: any
): Promise {
const url = `${this.baseUrl}/rest/api/3${endpoint}`;
@@ -131,7 +132,7 @@ export class JiraService {
name: string;
path: string;
contentType: string;
- }>,
+ }>
): Promise {
const issueTypes = await this.getIssueTypes(projectKey);
@@ -144,14 +145,16 @@ export class JiraService {
availableIssueTypes = project.issueTypes;
}
} catch {
- console.warn(`Could not fetch project-specific issue types for ${projectKey}, using global issue types`);
+ console.warn(
+ `Could not fetch project-specific issue types for ${projectKey}, using global issue types`
+ );
}
const issueTypeObj = availableIssueTypes.find((it: any) => it.name === issueType);
if (!issueTypeObj) {
throw new Error(
- `Issue type '${issueType}' not found. Available issue types: ${availableIssueTypes.map((it: any) => it.name).join(', ')}`,
+ `Issue type '${issueType}' not found. Available issue types: ${availableIssueTypes.map((it: any) => it.name).join(', ')}`
);
}
@@ -201,7 +204,11 @@ export class JiraService {
console.log('Jira request body:', JSON.stringify(requestBody, null, 2));
- const issueResponse = await this.makeRequest('/issue', 'POST', requestBody);
+ const issueResponse = await this.makeRequest(
+ '/issue',
+ 'POST',
+ requestBody
+ );
if (attachments && attachments.length > 0) {
for (const attachment of attachments) {
@@ -226,10 +233,10 @@ export class JiraService {
name: string;
path: string;
contentType: string;
- },
+ }
): Promise {
try {
- const { storage } = await import('@/app/lib/storage');
+ const { storage } = await import('../storage/index.js');
let fileName = attachment.name;
@@ -243,7 +250,8 @@ export class JiraService {
const fileContent = await storage.readFile(attachment.path, attachment.contentType);
- const fileBuffer = typeof fileContent === 'string' ? Buffer.from(fileContent, 'utf-8') : fileContent;
+ const fileBuffer =
+ typeof fileContent === 'string' ? Buffer.from(fileContent, 'utf-8') : fileContent;
const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`;
@@ -271,7 +279,7 @@ export class JiraService {
const errorText = await response.text();
throw new Error(
- `Failed to attach file to JIRA issue: ${response.status} ${response.statusText} - ${errorText}`,
+ `Failed to attach file to JIRA issue: ${response.status} ${response.statusText} - ${errorText}`
);
}
diff --git a/apps/backend/src/lib/service/lifecycle.ts b/apps/backend/src/lib/service/lifecycle.ts
new file mode 100644
index 00000000..9404bf73
--- /dev/null
+++ b/apps/backend/src/lib/service/lifecycle.ts
@@ -0,0 +1,70 @@
+import { configCache } from './cache/config.js';
+import { cronService } from './cron.js';
+import { reportDb, resultDb } from './db/index.js';
+
+const createdLifecycle = Symbol.for('playwright.reports.lifecycle');
+const instance = globalThis as typeof globalThis & {
+ [createdLifecycle]?: Lifecycle;
+};
+
+export class Lifecycle {
+ private initialized = false;
+ private initPromise?: Promise;
+
+ public static getInstance(): Lifecycle {
+ instance[createdLifecycle] ??= new Lifecycle();
+
+ return instance[createdLifecycle];
+ }
+
+ public async initialize(): Promise {
+ if (this.initialized) return;
+
+ this.initPromise ??= this._performInitialization();
+
+ return this.initPromise;
+ }
+
+ private async _performInitialization(): Promise {
+ console.log('[lifecycle] Starting application initialization');
+
+ try {
+ await Promise.all([configCache.init(), reportDb.init(), resultDb.init()]);
+ console.log('[lifecycle] Databases initialized successfully');
+
+ if (!cronService.initialized) {
+ await cronService.init();
+ console.log('[lifecycle] Cron service initialized successfully');
+ }
+
+ this.initialized = true;
+ console.log('[lifecycle] Application initialization complete');
+ } catch (error) {
+ console.error('[lifecycle] Initialization failed:', error);
+ throw error;
+ }
+ }
+
+ public isInitialized(): boolean {
+ return this.initialized;
+ }
+
+ public async cleanup(): Promise {
+ if (!this.initialized) return;
+
+ console.log('[lifecycle] Starting application cleanup');
+
+ try {
+ if (cronService.initialized) {
+ await cronService.restart();
+ console.log('[lifecycle] Cron service stopped');
+ }
+
+ console.log('[lifecycle] Application cleanup complete');
+ } catch (error) {
+ console.error('[lifecycle] Cleanup failed:', error);
+ }
+ }
+}
+
+export const lifecycle = Lifecycle.getInstance();
diff --git a/app/lib/storage/batch.ts b/apps/backend/src/lib/storage/batch.ts
similarity index 90%
rename from app/lib/storage/batch.ts
rename to apps/backend/src/lib/storage/batch.ts
index 3bf409d2..447d8200 100644
--- a/app/lib/storage/batch.ts
+++ b/apps/backend/src/lib/storage/batch.ts
@@ -2,7 +2,7 @@ export async function processBatch(
ctx: unknown,
items: T[],
batchSize: number,
- asyncAction: (item: T) => Promise,
+ asyncAction: (item: T) => Promise
): Promise {
const results: R[] = [];
diff --git a/app/lib/storage/constants.ts b/apps/backend/src/lib/storage/constants.ts
similarity index 100%
rename from app/lib/storage/constants.ts
rename to apps/backend/src/lib/storage/constants.ts
diff --git a/app/lib/storage/file.ts b/apps/backend/src/lib/storage/file.ts
similarity index 89%
rename from app/lib/storage/file.ts
rename to apps/backend/src/lib/storage/file.ts
index f5ce904a..803b435a 100644
--- a/app/lib/storage/file.ts
+++ b/apps/backend/src/lib/storage/file.ts
@@ -1,4 +1,4 @@
-import { REPORTS_BUCKET } from './constants';
+import { REPORTS_BUCKET } from './constants.js';
export const isUUID = (uuid?: string): boolean => {
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(uuid ?? '');
diff --git a/app/lib/storage/folders.ts b/apps/backend/src/lib/storage/folders.ts
similarity index 100%
rename from app/lib/storage/folders.ts
rename to apps/backend/src/lib/storage/folders.ts
diff --git a/app/lib/storage/format.ts b/apps/backend/src/lib/storage/format.ts
similarity index 90%
rename from app/lib/storage/format.ts
rename to apps/backend/src/lib/storage/format.ts
index 92658d75..52762952 100644
--- a/app/lib/storage/format.ts
+++ b/apps/backend/src/lib/storage/format.ts
@@ -1,4 +1,4 @@
-import { Result, Report } from './types';
+import type { Report, Result } from './types.js';
export const bytesToString = (bytes: number): string => {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
diff --git a/app/lib/storage/fs.ts b/apps/backend/src/lib/storage/fs.ts
similarity index 59%
rename from app/lib/storage/fs.ts
rename to apps/backend/src/lib/storage/fs.ts
index 051f9c13..439eef4c 100644
--- a/app/lib/storage/fs.ts
+++ b/apps/backend/src/lib/storage/fs.ts
@@ -1,13 +1,17 @@
-import fs from 'node:fs/promises';
-import path from 'node:path';
import { randomUUID } from 'node:crypto';
import { createWriteStream, type Dirent, type Stats } from 'node:fs';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import type { PassThrough } from 'node:stream';
import { pipeline } from 'node:stream/promises';
-import { PassThrough } from 'node:stream';
-
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
import getFolderSize from 'get-folder-size';
-
-import { bytesToString } from './format';
+import { defaultConfig, isConfigValid, noConfigErr } from '../config.js';
+import { serveReportRoute } from '../constants.js';
+import { parse } from '../parser/index.js';
+import { generatePlaywrightReport } from '../pw.js';
+import { withError } from '../withError.js';
+import { processBatch } from './batch.js';
import {
APP_CONFIG,
DATA_FOLDER,
@@ -17,28 +21,19 @@ import {
REPORTS_PATH,
RESULTS_FOLDER,
TMP_FOLDER,
-} from './constants';
-import { processBatch } from './batch';
-import { handlePagination } from './pagination';
-import { createDirectory } from './folders';
-
-import { defaultConfig, isConfigValid, noConfigErr } from '@/app/lib/config';
-import { parse } from '@/app/lib/parser';
-import { generatePlaywrightReport } from '@/app/lib/pw';
-import { withError } from '@/app/lib/withError';
-import { serveReportRoute } from '@/app/lib/constants';
-import {
- type Storage,
- type Result,
- type ServerDataInfo,
- type ResultDetails,
+} from './constants.js';
+import { createDirectory } from './folders.js';
+import { bytesToString } from './format.js';
+import type {
ReadReportsOutput,
- ReadReportsInput,
- ReadResultsInput,
- ReportMetadata,
ReportHistory,
-} from '@/app/lib/storage';
-import { SiteWhiteLabelConfig } from '@/app/types';
+ ReportMetadata,
+ ReportPath,
+ Result,
+ ResultDetails,
+ ServerDataInfo,
+ Storage,
+} from './types.js';
async function createDirectoriesIfMissing() {
await createDirectory(RESULTS_FOLDER);
@@ -77,15 +72,19 @@ export async function readFile(targetPath: string, contentType: string | null) {
async function getResultsCount() {
const files = await fs.readdir(RESULTS_FOLDER);
+ const zipFilesCount = files.filter((file) => file.endsWith('.zip'));
- return Math.round(files.length / 2);
+ return zipFilesCount.length;
}
-export async function readResults(input?: ReadResultsInput) {
+export async function readResults() {
await createDirectoriesIfMissing();
const files = await fs.readdir(RESULTS_FOLDER);
- const stats = await processBatch(
+ const stats = await processBatch<
+ string,
+ Stats & { filePath: string; size: string; sizeBytes: number }
+ >(
{},
files.filter((file) => file.endsWith('.json')),
20,
@@ -99,64 +98,29 @@ export async function readResults(input?: ReadResultsInput) {
const size = bytesToString(sizeBytes);
return Object.assign(stat, { filePath, size, sizeBytes });
- },
- );
-
- const jsonFiles = stats.sort((a, b) => b.birthtimeMs - a.birthtimeMs);
-
- const fileContents: Result[] = await Promise.all(
- jsonFiles.map(async (entry) => {
- const content = await fs.readFile(entry.filePath, 'utf-8');
-
- return {
- size: entry.size,
- sizeBytes: entry.sizeBytes,
- ...JSON.parse(content),
- };
- }),
+ }
);
- let filteredResults = fileContents.filter((result) => (input?.project ? result.project === input.project : result));
-
- // Filter by tags if provided
- if (input?.tags && input.tags.length > 0) {
- const notMetadataKeys = ['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'];
-
- filteredResults = filteredResults.filter((result) => {
- const resultTags = Object.entries(result)
- .filter(([key]) => !notMetadataKeys.includes(key))
- .map(([key, value]) => `${key}: ${value}`);
-
- return input.tags!.some((selectedTag) => resultTags.includes(selectedTag));
- });
- }
-
- // Filter by search if provided
- if (input?.search?.trim()) {
- const searchTerm = input.search.toLowerCase().trim();
-
- filteredResults = filteredResults.filter((result) => {
- // Search in title, resultID, project, and all metadata fields
- const searchableFields = [
- result.title,
- result.resultID,
- result.project,
- ...Object.entries(result)
- .filter(([key]) => !['resultID', 'title', 'createdAt', 'size', 'sizeBytes', 'project'].includes(key))
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const paginatedResults = handlePagination(filteredResults, input?.pagination);
+ const results = await processBatch<
+ Stats & {
+ filePath: string;
+ size: string;
+ sizeBytes: number;
+ },
+ Result
+ >({}, stats, 10, async (entry) => {
+ const content = await fs.readFile(entry.filePath, 'utf-8');
+
+ return {
+ size: entry.size,
+ sizeBytes: entry.sizeBytes,
+ ...JSON.parse(content),
+ };
+ });
return {
- results: paginatedResults.map((result) => ({
- ...result,
- })),
- total: filteredResults.length,
+ results,
+ total: results.length,
};
}
@@ -166,7 +130,7 @@ function isMissingFileError(error?: Error | null) {
async function readOrParseReportMetadata(id: string, projectName: string): Promise {
const { result: metadataContent, error: metadataError } = await withError(
- readFile(path.join(projectName, id, REPORT_METADATA_FILE), 'utf-8'),
+ readFile(path.join(projectName, id, REPORT_METADATA_FILE), 'utf-8')
);
if (metadataError) console.error(`failed to read metadata for ${id}: ${metadataError.message}`);
@@ -196,41 +160,75 @@ async function readOrParseReportMetadata(id: string, projectName: string): Promi
return metadata;
}
-export async function readReports(input?: ReadReportsInput): Promise {
+export async function readReport(
+ reportID: string,
+ reportPath: string
+): Promise {
await createDirectoriesIfMissing();
- const entries = await fs.readdir(REPORTS_FOLDER, { withFileTypes: true, recursive: true });
- const reportEntries = entries
- .filter((entry) => !entry.isDirectory() && entry.name === 'index.html' && !(entry as any).path.endsWith('trace'))
- .filter((entry) => (input?.ids ? input.ids.some((id) => (entry as any).path.includes(id)) : entry))
- .filter((entry) => (input?.project ? (entry as any).path.includes(input.project) : entry));
+ console.log(`[fs] reading report ${reportID} metadata from path: ${reportPath}`);
+
+ // Convert reportPath to relative path from REPORTS_FOLDER
+ const relativePath = path.relative(REPORTS_FOLDER, reportPath);
+ console.log(`[fs] reading report ${reportID} relative path: ${relativePath}`);
+
+ const { result: metadataContent, error: metadataError } = await withError(
+ readFile(path.join(relativePath, REPORT_METADATA_FILE), 'utf-8')
+ );
+
+ if (metadataError) {
+ console.error(`[fs] failed to read metadata for ${reportID}: ${metadataError.message}`);
+
+ return null;
+ }
+
+ const metadata = metadataContent ? JSON.parse(metadataContent.toString()) : {};
+
+ return {
+ reportID,
+ project: metadata.project || '',
+ createdAt: new Date(metadata.createdAt),
+ size: metadata.size || '',
+ sizeBytes: metadata.sizeBytes || 0,
+ reportUrl: metadata.reportUrl || '',
+ ...metadata,
+ } as ReportHistory;
+}
+
+export async function readReports(): Promise {
+ await createDirectoriesIfMissing();
+ const entries = await fs.readdir(REPORTS_FOLDER, {
+ withFileTypes: true,
+ recursive: true,
+ });
+
+ const reportEntries = entries.filter(
+ (entry) =>
+ !entry.isDirectory() &&
+ entry.name === 'index.html' &&
+ !('parentPath' in entry && entry.parentPath?.endsWith('trace'))
+ );
const stats = await processBatch(
{},
reportEntries,
20,
async (file) => {
- const stat = await fs.stat((file as any).path);
+ const filePath =
+ 'parentPath' in file
+ ? file.parentPath
+ : path.join(REPORTS_FOLDER, (file as Dirent).name || '');
+ const stat = await fs.stat(filePath);
- return Object.assign(stat, { filePath: (file as any).path, createdAt: stat.birthtime });
- },
+ return Object.assign(stat, { filePath, createdAt: stat.birthtime });
+ }
);
- const reportFiles = stats.sort((a, b) => b.birthtimeMs - a.birthtimeMs);
-
- const reportsWithProject = reportFiles
- .map((file) => {
- const id = path.basename(file.filePath);
- const parentDir = path.basename(path.dirname(file.filePath));
-
- const projectName = parentDir === REPORTS_PATH ? '' : parentDir;
-
- return Object.assign(file, { id, project: projectName });
- })
- .filter((report) => (input?.project ? input.project === report.project : report));
-
- const allReports = await Promise.all(
- reportsWithProject.map(async (file) => {
+ const reports = await processBatch(
+ {},
+ stats,
+ 10,
+ async (file) => {
const id = path.basename(file.filePath);
const reportPath = path.dirname(file.filePath);
const parentDir = path.basename(reportPath);
@@ -249,37 +247,11 @@ export async function readReports(input?: ReadReportsInput): Promise {
- // Search in title, reportID, project, and all metadata fields
- const searchableFields = [
- report.title,
- report.reportID,
- report.project,
- ...Object.entries(report)
- .filter(
- ([key]) =>
- !['reportID', 'title', 'createdAt', 'size', 'sizeBytes', 'project', 'reportUrl', 'stats'].includes(key),
- )
- .map(([key, value]) => `${key}: ${value}`),
- ].filter(Boolean);
-
- return searchableFields.some((field) => field?.toLowerCase().includes(searchTerm));
- });
- }
-
- const paginatedReports = handlePagination(filteredReports, input?.pagination);
-
- return { reports: paginatedReports as ReportHistory[], total: filteredReports.length };
+ return { reports: reports, total: reports.length };
}
export async function deleteResults(resultsIds: string[]) {
@@ -292,15 +264,14 @@ export async function deleteResult(resultId: string) {
await Promise.allSettled([fs.unlink(`${resultPath}.json`), fs.unlink(`${resultPath}.zip`)]);
}
-export async function deleteReports(reportsIds: string[]) {
- const { reports } = await readReports({ ids: reportsIds });
-
- const paths = reportsIds
- .map((id) => reports.find((report) => report.reportID === id))
- .filter(Boolean)
- .map((report) => (report?.project ? `${report.project}/${report.reportID}` : report?.reportID));
+export async function deleteReports(reports: ReportPath[]) {
+ const paths = reports.map((report) =>
+ report.project ? `${report.project}/${report.reportID}` : report.reportID
+ );
- await Promise.allSettled(paths.map((path) => deleteReport(path!)));
+ await processBatch(undefined, paths, 10, async (path) => {
+ await deleteReport(path);
+ });
}
export async function deleteReport(reportId: string) {
@@ -325,7 +296,11 @@ export async function saveResult(filename: string, stream: PassThrough) {
}
}
-export async function saveResultDetails(resultID: string, resultDetails: ResultDetails, size: number): Promise {
+export async function saveResultDetails(
+ resultID: string,
+ resultDetails: ResultDetails,
+ size: number
+): Promise {
await createDirectoriesIfMissing();
const metaData = {
@@ -340,7 +315,7 @@ export async function saveResultDetails(resultID: string, resultDetails: ResultD
const { error: writeJsonError } = await withError(
fs.writeFile(path.join(RESULTS_FOLDER, `${resultID}.json`), JSON.stringify(metaData, null, 2), {
encoding: 'utf-8',
- }),
+ })
);
if (writeJsonError) {
@@ -367,7 +342,7 @@ export async function generateReport(resultsIds: string[], metadata?: ReportMeta
await saveReportMetadata(generated.reportPath, info);
- return reportId;
+ return { reportId, reportPath: generated.reportPath };
} finally {
await fs.rm(tempFolder, { recursive: true, force: true });
}
@@ -376,7 +351,7 @@ export async function generateReport(resultsIds: string[], metadata?: ReportMeta
async function parseReportMetadata(
reportID: string,
reportPath: string,
- metadata?: ReportMetadata,
+ metadata?: ReportMetadata
): Promise {
const html = await fs.readFile(path.join(reportPath, 'index.html'), 'utf-8');
const info = await parse(html as string);
@@ -387,7 +362,7 @@ async function parseReportMetadata(
reportID,
createdAt: new Date().toISOString(),
},
- metadata ?? {},
+ metadata ?? {}
);
return content;
@@ -419,7 +394,9 @@ async function readConfigFile() {
return isValid ? { result: parsed, error: null } : { error: new Error('invalid config') };
} catch (e) {
- return { error: new Error(`failed to parse config: ${e instanceof Error ? e.message : e}`) };
+ return {
+ error: new Error(`failed to parse config: ${e instanceof Error ? e.message : e}`),
+ };
}
}
@@ -435,7 +412,11 @@ async function saveConfigFile(config: Partial) {
const previousConfig = existingConfig ?? defaultConfig;
const uploadConfig = { ...previousConfig, ...config };
- const { error } = await withError(fs.writeFile(APP_CONFIG, JSON.stringify(uploadConfig, null, 2), { flag: 'w+' }));
+ const { error } = await withError(
+ fs.writeFile(APP_CONFIG, JSON.stringify(uploadConfig, null, 2), {
+ flag: 'w+',
+ })
+ );
return {
result: uploadConfig,
@@ -448,6 +429,7 @@ export const FS: Storage = {
readFile,
readResults,
readReports,
+ readReport,
deleteResults,
deleteReports,
saveResult,
diff --git a/apps/backend/src/lib/storage/index.ts b/apps/backend/src/lib/storage/index.ts
new file mode 100644
index 00000000..d1b5ef83
--- /dev/null
+++ b/apps/backend/src/lib/storage/index.ts
@@ -0,0 +1,7 @@
+export * from './types.js';
+
+import { env } from '../../config/env.js';
+import { FS } from './fs.js';
+import { S3 } from './s3.js';
+
+export const storage = env.DATA_STORAGE === 's3' ? S3.getInstance() : FS;
diff --git a/apps/backend/src/lib/storage/pagination.ts b/apps/backend/src/lib/storage/pagination.ts
new file mode 100644
index 00000000..24f65c14
--- /dev/null
+++ b/apps/backend/src/lib/storage/pagination.ts
@@ -0,0 +1,14 @@
+export interface Pagination {
+ limit: number;
+ offset: number;
+}
+
+export const parseFromRequest = (searchParams: URLSearchParams): Pagination => {
+ const limitQuery = searchParams.get('limit') ?? '';
+ const offsetQuery = searchParams.get('offset') ?? '';
+
+ const limit = limitQuery ? Number.parseInt(limitQuery, 10) : 20;
+ const offset = offsetQuery ? Number.parseInt(offsetQuery, 10) : 0;
+
+ return { limit, offset };
+};
diff --git a/apps/backend/src/lib/storage/s3.ts b/apps/backend/src/lib/storage/s3.ts
new file mode 100644
index 00000000..f6f66331
--- /dev/null
+++ b/apps/backend/src/lib/storage/s3.ts
@@ -0,0 +1,1229 @@
+import { randomUUID, type UUID } from 'node:crypto';
+import { createReadStream, createWriteStream } from 'node:fs';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+import type { PassThrough, Readable } from 'node:stream';
+
+import {
+ type _Object,
+ AbortMultipartUploadCommand,
+ CompleteMultipartUploadCommand,
+ CreateBucketCommand,
+ CreateMultipartUploadCommand,
+ DeleteObjectCommand,
+ GetObjectCommand,
+ HeadBucketCommand,
+ HeadObjectCommand,
+ ListObjectsV2Command,
+ PutObjectCommand,
+ S3Client,
+ UploadPartCommand,
+} from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import { env } from '../../config/env.js';
+import { withError } from '../../lib/withError.js';
+import { defaultConfig, isConfigValid } from '../config.js';
+import { serveReportRoute } from '../constants.js';
+import { parse } from '../parser/index.js';
+import { generatePlaywrightReport } from '../pw.js';
+import { processBatch } from './batch.js';
+import {
+ APP_CONFIG_S3,
+ DATA_FOLDER,
+ DATA_PATH,
+ REPORT_METADATA_FILE,
+ REPORTS_BUCKET,
+ REPORTS_FOLDER,
+ REPORTS_PATH,
+ RESULTS_BUCKET,
+ TMP_FOLDER,
+} from './constants.js';
+import { getFileReportID } from './file.js';
+import { bytesToString } from './format.js';
+import {
+ isReportHistory,
+ type ReadReportsOutput,
+ type ReadResultsOutput,
+ type Report,
+ type ReportHistory,
+ type ReportMetadata,
+ type ReportPath,
+ type Result,
+ type ResultDetails,
+ type ServerDataInfo,
+ type Storage,
+} from './types.js';
+
+const createClient = () => {
+ const endPoint = env.S3_ENDPOINT;
+ const accessKey = env.S3_ACCESS_KEY;
+ const secretKey = env.S3_SECRET_KEY;
+ const port = env.S3_PORT;
+ const region = env.S3_REGION;
+
+ if (!endPoint) {
+ throw new Error('S3_ENDPOINT is required');
+ }
+
+ if (!accessKey) {
+ throw new Error('S3_ACCESS_KEY is required');
+ }
+
+ if (!secretKey) {
+ throw new Error('S3_SECRET_KEY is required');
+ }
+
+ console.log('[s3] creating client');
+
+ const protocol = 'https://';
+ const endpointUrl = port ? `${protocol}${endPoint}:${port}` : `${protocol}${endPoint}`;
+
+ const client = new S3Client({
+ region: region || 'us-east-1',
+ endpoint: endpointUrl,
+ credentials: {
+ accessKeyId: accessKey,
+ secretAccessKey: secretKey,
+ },
+ forcePathStyle: true, // required for S3-compatible services like Minio
+ });
+
+ return client;
+};
+
+export class S3 implements Storage {
+ private static instance: S3;
+ private readonly client: S3Client;
+ private readonly bucket: string;
+ private readonly batchSize: number;
+
+ private constructor() {
+ this.client = createClient();
+ this.bucket = env.S3_BUCKET;
+ this.batchSize = env.S3_BATCH_SIZE;
+ }
+
+ public static getInstance() {
+ if (!S3.instance) {
+ S3.instance = new S3();
+ }
+
+ return S3.instance;
+ }
+
+ private async ensureBucketExist() {
+ const { error } = await withError(
+ this.client.send(new HeadBucketCommand({ Bucket: this.bucket }))
+ );
+
+ if (!error) {
+ return;
+ }
+
+ if (error.name === 'NotFound') {
+ console.log(`[s3] bucket ${this.bucket} does not exist, creating...`);
+
+ const { error } = await withError(
+ this.client.send(
+ new CreateBucketCommand({
+ Bucket: this.bucket,
+ })
+ )
+ );
+
+ if (error) {
+ console.error('[s3] failed to create bucket:', error);
+ }
+ }
+
+ console.error('[s3] failed to check that bucket exist:', error);
+ }
+
+ private async write(
+ dir: string,
+ files: {
+ name: string;
+ content: Readable | Buffer | string;
+ size?: number;
+ }[]
+ ) {
+ await this.ensureBucketExist();
+ for (const file of files) {
+ const filePath = path.join(dir, file.name);
+
+ console.log(`[s3] writing ${filePath}`);
+
+ const content = typeof file.content === 'string' ? Buffer.from(file.content) : file.content;
+
+ await this.client.send(
+ new PutObjectCommand({
+ Bucket: this.bucket,
+ Key: path.normalize(filePath),
+ Body: content,
+ })
+ );
+ }
+ }
+
+ private async read(targetPath: string, contentType?: string | null) {
+ await this.ensureBucketExist();
+ console.log(`[s3] read ${targetPath}`);
+
+ const remotePath = targetPath.includes(REPORTS_BUCKET)
+ ? targetPath
+ : `${REPORTS_BUCKET}/${targetPath}`;
+
+ console.log(`[s3] reading from remote path: ${remotePath}`);
+
+ const { result: response, error } = await withError(
+ this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ })
+ )
+ );
+
+ if (error ?? !response?.Body) {
+ return { result: null, error };
+ }
+
+ const stream = response.Body as Readable;
+
+ const readStream = new Promise((resolve, reject) => {
+ const chunks: Uint8Array[] = [];
+
+ stream.on('data', (chunk: Uint8Array) => {
+ chunks.push(chunk);
+ });
+
+ stream.on('end', () => {
+ const fullContent = Buffer.concat(chunks);
+
+ resolve(fullContent);
+ });
+
+ stream.on('error', (error) => {
+ console.error(`[s3] failed to read stream: ${error.message}`);
+ reject(error);
+ });
+ });
+
+ const { result, error: readError } = await withError(readStream);
+
+ return {
+ result: contentType === 'text/html' ? result?.toString('utf-8') : result,
+ error: error ?? readError ?? null,
+ };
+ }
+
+ async clear(...path: string[]) {
+ console.log(`[s3] clearing ${path}`);
+ // avoid using "removeObjects" as it is not supported by every S3-compatible provider
+ // for example, Google Cloud Storage.
+ await processBatch(this, path, this.batchSize, async (object) => {
+ await this.client.send(
+ new DeleteObjectCommand({
+ Bucket: this.bucket,
+ Key: object,
+ })
+ );
+ });
+ }
+
+ async getFolderSize(
+ folderPath: string
+ ): Promise<{ size: number; resultCount: number; indexCount: number }> {
+ let resultCount = 0;
+ let indexCount = 0;
+ let totalSize = 0;
+
+ let continuationToken: string | undefined;
+
+ do {
+ const response = await this.client.send(
+ new ListObjectsV2Command({
+ Bucket: this.bucket,
+ Prefix: folderPath,
+ ContinuationToken: continuationToken,
+ })
+ );
+
+ for (const obj of response.Contents ?? []) {
+ if (obj.Key?.endsWith('.zip')) {
+ resultCount += 1;
+ }
+
+ if (obj.Key?.endsWith('index.html') && !obj.Key.includes('/trace/index.html')) {
+ indexCount += 1;
+ }
+
+ totalSize += obj?.Size ?? 0;
+ }
+
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
+ } while (continuationToken);
+
+ return { size: totalSize, resultCount, indexCount };
+ }
+
+ async getServerDataInfo(): Promise {
+ await this.ensureBucketExist();
+ console.log('[s3] getting server data');
+
+ const [results, reports] = await Promise.all([
+ this.getFolderSize(RESULTS_BUCKET),
+ this.getFolderSize(REPORTS_BUCKET),
+ ]);
+
+ const dataSize = results.size + reports.size;
+
+ return {
+ dataFolderSizeinMB: bytesToString(dataSize),
+ numOfResults: results.resultCount,
+ resultsFolderSizeinMB: bytesToString(results.size),
+ numOfReports: reports.indexCount,
+ reportsFolderSizeinMB: bytesToString(reports.size),
+ };
+ }
+
+ async readFile(targetPath: string, contentType: string | null): Promise {
+ console.log(`[s3] reading ${targetPath} | ${contentType}`);
+ const { result, error } = await this.read(targetPath, contentType);
+
+ if (error) {
+ console.error(`[s3] failed to read file ${targetPath}: ${error.message}`);
+ throw new Error(`[s3] failed to read file: ${error.message}`);
+ }
+
+ return result!;
+ }
+
+ async readResults(): Promise {
+ await this.ensureBucketExist();
+
+ console.log('[s3] reading results');
+
+ const jsonFiles: _Object[] = [];
+ const resultSizes = new Map();
+
+ let continuationToken: string | undefined;
+
+ do {
+ const response = await this.client.send(
+ new ListObjectsV2Command({
+ Bucket: this.bucket,
+ Prefix: RESULTS_BUCKET,
+ ContinuationToken: continuationToken,
+ })
+ );
+
+ for (const file of response.Contents ?? []) {
+ if (!file?.Key) {
+ continue;
+ }
+
+ if (file.Key.endsWith('.zip')) {
+ const resultID = path.basename(file.Key, '.zip');
+
+ resultSizes.set(resultID, file.Size ?? 0);
+ }
+
+ if (file.Key.endsWith('.json')) {
+ jsonFiles.push(file);
+ }
+ }
+
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
+ } while (continuationToken);
+
+ console.log(`[s3] found ${jsonFiles.length} json files`);
+
+ if (!jsonFiles) {
+ return {
+ results: [],
+ total: 0,
+ };
+ }
+
+ const results = await processBatch<_Object, Result>(
+ this,
+ jsonFiles,
+ this.batchSize,
+ async (file) => {
+ console.log(`[s3.batch] reading result: ${JSON.stringify(file)}`);
+ const response = await this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: file.Key!,
+ })
+ );
+
+ const stream = response.Body as Readable;
+ let jsonString = '';
+
+ for await (const chunk of stream) {
+ jsonString += chunk.toString();
+ }
+
+ const parsed = JSON.parse(jsonString);
+
+ return parsed;
+ }
+ );
+
+ return {
+ results: results.map((result) => {
+ const sizeBytes = resultSizes.get(result.resultID) ?? 0;
+
+ return {
+ ...result,
+ sizeBytes,
+ size: result.size ?? bytesToString(sizeBytes),
+ };
+ }) as Result[],
+ total: results.length,
+ };
+ }
+
+ async readReport(reportID: string, reportPath: string): Promise {
+ await this.ensureBucketExist();
+
+ console.log(`[s3] reading report ${reportID} metadata`);
+
+ const relativePath = path.relative(reportPath, REPORTS_BUCKET);
+
+ const objectKey = path.join(REPORTS_BUCKET, relativePath, REPORT_METADATA_FILE);
+
+ console.log(`[s3] checking existence of result: ${objectKey}`);
+ const { error: headError } = await withError(
+ this.client.send(
+ new HeadObjectCommand({
+ Bucket: this.bucket,
+ Key: objectKey,
+ })
+ )
+ );
+
+ if (headError) {
+ throw new Error(`failed to check ${objectKey}: ${headError.message}`);
+ }
+
+ console.log(`[s3] downloading metadata file: ${objectKey}`);
+ const localFilePath = path.join(TMP_FOLDER, reportID, REPORT_METADATA_FILE);
+
+ const { error: downloadError } = await withError(
+ (async () => {
+ const response = await this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: objectKey,
+ })
+ );
+
+ const stream = response.Body as Readable;
+ const writeStream = createWriteStream(localFilePath);
+
+ return new Promise((resolve, reject) => {
+ stream.pipe(writeStream);
+ writeStream.on('finish', resolve);
+ writeStream.on('error', reject);
+ stream.on('error', reject);
+ });
+ })()
+ );
+
+ if (downloadError) {
+ console.error(`[s3] failed to download ${objectKey}: ${downloadError.message}`);
+
+ throw new Error(`failed to download ${objectKey}: ${downloadError.message}`);
+ }
+
+ try {
+ const content = await fs.readFile(localFilePath, 'utf-8');
+
+ const metadata = JSON.parse(content);
+
+ return isReportHistory(metadata) ? metadata : null;
+ } catch (e) {
+ console.error(`[s3] failed to read or parse metadata file: ${(e as Error).message}`);
+
+ return null;
+ }
+ }
+
+ async readReports(): Promise {
+ await this.ensureBucketExist();
+
+ console.log(`[s3] reading reports from external storage`);
+
+ const reports: Report[] = [];
+ const reportSizes = new Map();
+
+ let continuationToken: string | undefined;
+
+ do {
+ const response = await this.client.send(
+ new ListObjectsV2Command({
+ Bucket: this.bucket,
+ Prefix: REPORTS_BUCKET,
+ ContinuationToken: continuationToken,
+ })
+ );
+
+ for (const file of response.Contents ?? []) {
+ if (!file?.Key) {
+ continue;
+ }
+
+ const reportID = getFileReportID(file.Key);
+
+ const newSize = (reportSizes.get(reportID) ?? 0) + (file.Size ?? 0);
+
+ reportSizes.set(reportID, newSize);
+
+ if (!file.Key.endsWith('index.html') || file.Key.includes('trace')) {
+ continue;
+ }
+
+ const dir = path.dirname(file.Key);
+ const id = path.basename(dir);
+ const parentDir = path.basename(path.dirname(dir));
+
+ const projectName = parentDir === REPORTS_PATH ? '' : parentDir;
+
+ const report = {
+ reportID: id,
+ project: projectName,
+ createdAt: file.LastModified ?? new Date(),
+ reportUrl: `${serveReportRoute}/${projectName ? encodeURIComponent(projectName) : ''}/${id}/index.html`,
+ size: '',
+ sizeBytes: 0,
+ };
+
+ reports.push(report);
+ }
+
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
+ } while (continuationToken);
+
+ const withMetadata = await this.getReportsMetadata(reports as ReportHistory[]);
+
+ return {
+ reports: withMetadata.map((report) => {
+ const sizeBytes = reportSizes.get(report.reportID) ?? 0;
+
+ return {
+ ...report,
+ sizeBytes,
+ size: bytesToString(sizeBytes),
+ };
+ }),
+ total: withMetadata.length,
+ };
+ }
+
+ async getReportsMetadata(reports: ReportHistory[]): Promise {
+ return await processBatch(
+ this,
+ reports,
+ this.batchSize,
+ async (report) => {
+ console.log(`[s3.batch] reading report ${report.reportID} metadata`);
+
+ const { result: metadata, error: metadataError } = await withError(
+ this.readOrParseReportMetadata(report.reportID, report.project)
+ );
+
+ if (metadataError) {
+ console.error(
+ `[s3] failed to read or create metadata for ${report.reportID}: ${metadataError.message}`
+ );
+
+ return report;
+ }
+
+ if (!metadata) {
+ return report;
+ }
+
+ return Object.assign(metadata, report);
+ }
+ );
+ }
+
+ async readOrParseReportMetadata(id: string, projectName: string): Promise {
+ const { result: metadataContent, error: metadataError } = await withError(
+ this.readFile(path.join(REPORTS_BUCKET, projectName, id, REPORT_METADATA_FILE), 'utf-8')
+ );
+
+ if (metadataError)
+ console.error(`[s3] failed to read metadata for ${id}: ${metadataError.message}`);
+
+ const metadata =
+ metadataContent && !metadataError ? JSON.parse(metadataContent.toString()) : {};
+
+ if (isReportHistory(metadata)) {
+ console.log(`metadata found for report ${id}`);
+
+ return metadata;
+ }
+
+ console.log(`metadata file not found for ${id}, creating new metadata`);
+ try {
+ const { result: htmlContent, error: htmlError } = await withError(
+ this.readFile(path.join(REPORTS_BUCKET, projectName, id, 'index.html'), 'utf-8')
+ );
+
+ if (htmlError)
+ console.error(`[s3] failed to read index.html for ${id}: ${htmlError.message}`);
+
+ const created = await this.parseReportMetadata(
+ id,
+ path.join(REPORTS_FOLDER, projectName, id),
+ {
+ project: projectName,
+ reportID: id,
+ },
+ htmlContent?.toString()
+ );
+
+ console.log(`metadata object created for ${id}: ${JSON.stringify(created)}`);
+
+ await this.saveReportMetadata(id, path.join(REPORTS_FOLDER, projectName, id), created);
+
+ Object.assign(metadata, created);
+ } catch (e) {
+ console.error(`failed to create metadata for ${id}: ${(e as Error).message}`);
+ }
+
+ return metadata;
+ }
+
+ async deleteResults(resultIDs: string[]): Promise {
+ const objects = resultIDs.flatMap((id) => [
+ `${RESULTS_BUCKET}/${id}.json`,
+ `${RESULTS_BUCKET}/${id}.zip`,
+ ]);
+
+ await withError(this.clear(...objects));
+ }
+
+ private async getReportObjects(reportsIDs: string[]): Promise {
+ const files: string[] = [];
+
+ let continuationToken: string | undefined;
+
+ do {
+ const response = await this.client.send(
+ new ListObjectsV2Command({
+ Bucket: this.bucket,
+ Prefix: REPORTS_BUCKET,
+ ContinuationToken: continuationToken,
+ })
+ );
+
+ for (const file of response.Contents ?? []) {
+ if (!file?.Key) {
+ continue;
+ }
+
+ const reportID = path.basename(path.dirname(file.Key));
+
+ if (reportsIDs.includes(reportID)) {
+ files.push(file.Key);
+ }
+ }
+
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
+ } while (continuationToken);
+
+ return files;
+ }
+
+ async deleteReports(reports: ReportPath[]): Promise {
+ const ids = reports.map((r) => r.reportID);
+ const objects = await this.getReportObjects(ids);
+
+ await withError(this.clear(...objects));
+ }
+
+ async generatePresignedUploadUrl(fileName: string) {
+ await this.ensureBucketExist();
+ const objectKey = path.join(RESULTS_BUCKET, fileName);
+ const expiry = 30 * 60; // 30 minutes
+
+ const command = new PutObjectCommand({
+ Bucket: this.bucket,
+ Key: objectKey,
+ });
+
+ return await getSignedUrl(this.client, command, { expiresIn: expiry });
+ }
+
+ async saveResult(filename: string, stream: PassThrough) {
+ await this.ensureBucketExist();
+
+ const chunkSizeMB = env.S3_MULTIPART_CHUNK_SIZE_MB;
+ const chunkSize = chunkSizeMB * 1024 * 1024; // bytes
+
+ console.log(`[s3] starting multipart upload for ${filename} with chunk size ${chunkSizeMB}MB`);
+
+ const remotePath = path.join(RESULTS_BUCKET, filename);
+
+ const { UploadId: uploadID } = await this.client.send(
+ new CreateMultipartUploadCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ })
+ );
+
+ if (!uploadID) {
+ throw new Error('[s3] failed to initiate multipart upload: no UploadId received');
+ }
+
+ const uploadedParts: { PartNumber: number; ETag: string }[] = [];
+ let partNumber = 1;
+ const chunks: Buffer[] = [];
+ let currentSize = 0;
+
+ try {
+ for await (const chunk of stream) {
+ console.log(
+ `[s3] received chunk of size ${(chunk.length / (1024 * 1024)).toFixed(2)}MB for ${filename}`
+ );
+
+ chunks.push(chunk);
+ currentSize += chunk.length;
+
+ while (currentSize >= chunkSize) {
+ const partData = Buffer.allocUnsafe(chunkSize);
+ let copied = 0;
+
+ while (copied < chunkSize && chunks.length > 0) {
+ const currentChunk = chunks[0];
+ const needed = chunkSize - copied;
+ const available = currentChunk.length;
+
+ if (available <= needed) {
+ currentChunk.copy(partData, copied);
+ copied += available;
+ chunks.shift();
+ } else {
+ currentChunk.copy(partData, copied, 0, needed);
+ copied += needed;
+ chunks[0] = currentChunk.subarray(needed);
+ }
+ }
+
+ currentSize -= chunkSize;
+
+ console.log(
+ `[s3] uploading part ${partNumber} (${(partData.length / (1024 * 1024)).toFixed(2)}MB) for ${filename}`
+ );
+ console.log(
+ `[s3] buffer state: ${chunks.length} chunks, ${(currentSize / (1024 * 1024)).toFixed(2)}MB remaining`
+ );
+
+ stream.pause();
+
+ const uploadPartResult = await this.client.send(
+ new UploadPartCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ UploadId: uploadID,
+ PartNumber: partNumber,
+ Body: partData,
+ })
+ );
+
+ // explicitly clear the part data to help GC
+ partData.fill(0);
+
+ console.log(`[s3] uploaded part ${partNumber}, resume reading`);
+ stream.resume();
+
+ if (!uploadPartResult.ETag) {
+ throw new Error(`[s3] failed to upload part ${partNumber}: no ETag received`);
+ }
+
+ uploadedParts.push({
+ PartNumber: partNumber,
+ ETag: uploadPartResult.ETag,
+ });
+
+ partNumber++;
+ }
+ }
+
+ if (currentSize > 0) {
+ console.log(
+ `[s3] uploading final part ${partNumber} [${bytesToString(currentSize)}] for ${filename}`
+ );
+
+ const finalPart = Buffer.allocUnsafe(currentSize);
+ let offset = 0;
+
+ for (const chunk of chunks) {
+ chunk.copy(finalPart, offset);
+ offset += chunk.length;
+ }
+
+ const uploadPartResult = await this.client.send(
+ new UploadPartCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ UploadId: uploadID,
+ PartNumber: partNumber,
+ Body: finalPart,
+ })
+ );
+
+ // explicitly clear buffer references
+ chunks.length = 0;
+ finalPart.fill(0);
+
+ if (!uploadPartResult.ETag) {
+ throw new Error(`[s3] failed to upload final part ${partNumber}: no ETag received`);
+ }
+
+ uploadedParts.push({
+ PartNumber: partNumber,
+ ETag: uploadPartResult.ETag,
+ });
+ }
+
+ console.log(
+ `[s3] completing multipart upload for ${filename} with ${uploadedParts.length} parts`
+ );
+
+ await this.client.send(
+ new CompleteMultipartUploadCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ UploadId: uploadID,
+ MultipartUpload: {
+ Parts: uploadedParts,
+ },
+ })
+ );
+
+ console.log(`[s3] multipart upload completed successfully for ${filename}`);
+ } catch (error) {
+ console.error(`[s3] multipart upload failed, aborting: ${(error as Error).message}`);
+
+ await this.client.send(
+ new AbortMultipartUploadCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ UploadId: uploadID,
+ })
+ );
+
+ throw error;
+ }
+ }
+
+ async saveResultDetails(
+ resultID: string,
+ resultDetails: ResultDetails,
+ size: number
+ ): Promise {
+ const metaData = {
+ resultID,
+ createdAt: new Date().toISOString(),
+ project: resultDetails?.project ?? '',
+ ...resultDetails,
+ size: bytesToString(size),
+ sizeBytes: size,
+ };
+
+ await this.write(RESULTS_BUCKET, [
+ {
+ name: `${resultID}.json`,
+ content: JSON.stringify(metaData),
+ },
+ ]);
+
+ return metaData as Result;
+ }
+
+ private async uploadReport(reportId: string, reportPath: string, remotePath: string) {
+ console.log(`[s3] upload report: ${reportPath}`);
+
+ const files = await fs.readdir(reportPath, {
+ recursive: true,
+ withFileTypes: true,
+ });
+
+ await processBatch(this, files, this.batchSize, async (file) => {
+ if (!file.isFile()) {
+ return;
+ }
+
+ console.log(`[s3] uploading file: ${JSON.stringify(file)}`);
+
+ const nestedPath = (file as any).path.split(reportId).pop();
+ const s3Path = path.join(remotePath, nestedPath ?? '', file.name);
+
+ console.log(`[s3] uploading to ${s3Path}`);
+
+ const { error } = await withError(
+ this.uploadFileWithRetry(s3Path, path.join((file as any).path, file.name))
+ );
+
+ if (error) {
+ console.error(`[s3] failed to upload report: ${error.message}`);
+ throw new Error(`[s3] failed to upload report: ${error.message}`);
+ }
+ });
+ }
+
+ private async uploadFileWithRetry(
+ remotePath: string,
+ filePath: string,
+ attempt = 1
+ ): Promise {
+ if (attempt > 3) {
+ throw new Error(`[s3] failed to upload file after ${attempt} attempts: ${filePath}`);
+ }
+
+ const fileStream = createReadStream(filePath);
+
+ const { error } = await withError(
+ this.client.send(
+ new PutObjectCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ Body: fileStream,
+ })
+ )
+ );
+
+ if (error) {
+ console.error(`[s3] failed to upload file: ${error.message}`);
+ console.log(`[s3] will retry in 3s...`);
+
+ return await this.uploadFileWithRetry(remotePath, filePath, attempt + 1);
+ }
+ }
+
+ private async clearTempFolders(id?: string) {
+ const withReportPathMaybe = id ? ` for report ${id}` : '';
+
+ console.log(`[s3] clear temp folders${withReportPathMaybe}`);
+
+ await withError(fs.rm(path.join(TMP_FOLDER, id ?? ''), { recursive: true, force: true }));
+ await withError(fs.rm(REPORTS_FOLDER, { recursive: true, force: true }));
+ }
+
+ async generateReport(
+ resultsIds: string[],
+ metadata?: ReportMetadata
+ ): Promise<{ reportId: UUID; reportPath: string }> {
+ console.log(`[s3] generate report from results: ${JSON.stringify(resultsIds)}`);
+ console.log(`[s3] create temp folders`);
+ const { error: mkdirReportsError } = await withError(
+ fs.mkdir(REPORTS_FOLDER, { recursive: true })
+ );
+
+ if (mkdirReportsError) {
+ console.error(`[s3] failed to create reports folder: ${mkdirReportsError.message}`);
+ }
+
+ const reportId = randomUUID();
+ const tempFolder = path.join(TMP_FOLDER, reportId);
+
+ const { error: mkdirTempError } = await withError(fs.mkdir(tempFolder, { recursive: true }));
+
+ if (mkdirTempError) {
+ console.error(`[s3] failed to create temporary folder: ${mkdirTempError.message}`);
+ }
+
+ console.log(`[s3] start processing...`);
+
+ for (const resultId of resultsIds) {
+ const fileName = `${resultId}.zip`;
+ const objectKey = path.join(RESULTS_BUCKET, fileName);
+
+ console.log(`[s3] checking existence of result: ${objectKey}`);
+ const { error: headError } = await withError(
+ this.client.send(
+ new HeadObjectCommand({
+ Bucket: this.bucket,
+ Key: objectKey,
+ })
+ )
+ );
+
+ if (headError) {
+ console.error(`[s3] result ${resultId} not found, skipping: ${headError.message}`);
+ throw new Error(`failed to check ${objectKey}: ${headError.message}`);
+ }
+
+ console.log(`[s3] downloading result: ${objectKey}`);
+ const localFilePath = path.join(tempFolder, fileName);
+
+ const { error: downloadError } = await withError(
+ (async () => {
+ const response = await this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: objectKey,
+ })
+ );
+
+ const stream = response.Body as Readable;
+ const writeStream = createWriteStream(localFilePath);
+
+ return new Promise((resolve, reject) => {
+ stream.pipe(writeStream);
+ writeStream.on('finish', resolve);
+ writeStream.on('error', reject);
+ stream.on('error', reject);
+ });
+ })()
+ );
+
+ if (downloadError) {
+ console.error(`[s3] failed to download ${objectKey}: ${downloadError.message}`);
+
+ throw new Error(`failed to download ${objectKey}: ${downloadError.message}`);
+ }
+
+ console.log(`[s3] downloaded: ${objectKey} to ${localFilePath}`);
+ }
+
+ const { reportPath } = await generatePlaywrightReport(reportId, metadata!);
+
+ console.log(`[s3] report generated: ${reportId} | ${reportPath}`);
+
+ const { result: info, error: parseReportMetadataError } = await withError(
+ this.parseReportMetadata(reportId, reportPath, metadata)
+ );
+
+ if (parseReportMetadataError) console.error(parseReportMetadataError.message);
+
+ const remotePath = path.join(REPORTS_BUCKET, metadata?.project ?? '', reportId);
+
+ const { error: uploadError } = await withError(
+ this.uploadReport(reportId, reportPath, remotePath)
+ );
+
+ if (uploadError) {
+ console.error(`[s3] failed to upload report: ${uploadError.message}`);
+ } else {
+ const { error } = await withError(
+ this.saveReportMetadata(reportId, reportPath, info ?? metadata ?? {})
+ );
+
+ if (error) console.error(`[s3] failed to save report metadata: ${error.message}`);
+ }
+
+ await this.clearTempFolders(reportId);
+
+ return { reportId, reportPath };
+ }
+
+ private async saveReportMetadata(reportId: string, reportPath: string, metadata: ReportMetadata) {
+ console.log(`[s3] report uploaded: ${reportId}, uploading metadata to ${reportPath}`);
+ const { error: metadataError } = await withError(
+ this.write(path.join(REPORTS_BUCKET, metadata.project ?? '', reportId), [
+ {
+ name: REPORT_METADATA_FILE,
+ content: JSON.stringify(metadata),
+ },
+ ])
+ );
+
+ if (metadataError)
+ console.error(`[s3] failed to upload report metadata: ${metadataError.message}`);
+ }
+
+ private async parseReportMetadata(
+ reportId: string,
+ reportPath: string,
+ metadata?: Record,
+ htmlContent?: string // to pass file content if stored on s3
+ ): Promise {
+ console.log(`[s3] creating report metadata for ${reportId} and ${reportPath}`);
+ const html = htmlContent ?? (await fs.readFile(path.join(reportPath, 'index.html'), 'utf-8'));
+
+ const info = await parse(html as string);
+
+ const content = Object.assign(info, metadata, {
+ reportId,
+ createdAt: new Date().toISOString(),
+ });
+
+ return content;
+ }
+
+ async readConfigFile(): Promise<{
+ result?: SiteWhiteLabelConfig;
+ error: Error | null;
+ }> {
+ await this.ensureBucketExist();
+ console.log(`[s3] checking config file`);
+
+ const { result: response, error } = await withError(
+ this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: APP_CONFIG_S3,
+ })
+ )
+ );
+
+ if (error) {
+ console.error(`[s3] failed to read config file: ${error.message}`);
+
+ return { error };
+ }
+
+ const stream = response?.Body as Readable;
+ let existingConfig = '';
+
+ for await (const chunk of stream ?? []) {
+ existingConfig += chunk.toString();
+ }
+
+ try {
+ const parsed = JSON.parse(existingConfig);
+
+ const isValid = isConfigValid(parsed);
+
+ if (!isValid) {
+ return { error: new Error('invalid config') };
+ }
+
+ // ensure custom images available locally in data folder
+ for (const image of [
+ { path: parsed.faviconPath, default: defaultConfig.faviconPath },
+ { path: parsed.logoPath, default: defaultConfig.logoPath },
+ ]) {
+ if (!image) continue;
+ if (image.path === image.default) continue;
+
+ const localPath = path.join(DATA_FOLDER, image.path);
+ const { error: accessError } = await withError(fs.access(localPath));
+
+ if (accessError) {
+ const remotePath = path.join(DATA_PATH, image.path);
+
+ console.log(`[s3] downloading config image: ${remotePath} to ${localPath}`);
+
+ const response = await this.client.send(
+ new GetObjectCommand({
+ Bucket: this.bucket,
+ Key: remotePath,
+ })
+ );
+
+ const stream = response.Body as Readable;
+ const writeStream = createWriteStream(localPath);
+
+ await new Promise((resolve, reject) => {
+ stream.pipe(writeStream);
+ writeStream.on('finish', resolve);
+ writeStream.on('error', reject);
+ stream.on('error', reject);
+ });
+ }
+ }
+
+ return { result: parsed, error: null };
+ } catch (e) {
+ return {
+ error: new Error(`failed to parse config: ${e instanceof Error ? e.message : e}`),
+ };
+ }
+ }
+
+ async saveConfigFile(config: Partial) {
+ console.log(`[s3] writing config file`);
+
+ const { result: existingConfig, error: readExistingConfigError } = await this.readConfigFile();
+
+ if (readExistingConfigError) {
+ console.error(`[s3] failed to read existing config file: ${readExistingConfigError.message}`);
+ }
+
+ const { error: clearExistingConfigError } = await withError(this.clear(APP_CONFIG_S3));
+
+ if (clearExistingConfigError) {
+ console.error(
+ `[s3] failed to clear existing config file: ${clearExistingConfigError.message}`
+ );
+ }
+
+ const uploadConfig = {
+ ...(existingConfig ?? {}),
+ ...config,
+ } as SiteWhiteLabelConfig;
+
+ const isDefaultImage = (key: keyof SiteWhiteLabelConfig) =>
+ config[key] && config[key] === defaultConfig[key];
+
+ const shouldBeUploaded = async (key: keyof SiteWhiteLabelConfig) => {
+ if (!config[key]) return false;
+ if (isDefaultImage(key)) return false;
+
+ const imagePath = key === 'logoPath' ? uploadConfig.logoPath : uploadConfig.faviconPath;
+
+ const { result } = await withError(
+ this.client.send(
+ new HeadObjectCommand({
+ Bucket: this.bucket,
+ Key: path.join(DATA_PATH, imagePath),
+ })
+ )
+ );
+
+ if (!result) {
+ return true;
+ }
+
+ return false;
+ };
+
+ if (await shouldBeUploaded('logoPath')) {
+ await this.uploadConfigImage(uploadConfig.logoPath);
+ }
+
+ if (await shouldBeUploaded('faviconPath')) {
+ await this.uploadConfigImage(uploadConfig.faviconPath);
+ }
+
+ const { error } = await withError(
+ this.write(DATA_PATH, [
+ {
+ name: 'config.json',
+ content: JSON.stringify(uploadConfig, null, 2),
+ },
+ ])
+ );
+
+ if (error) console.error(`[s3] failed to write config file: ${error.message}`);
+
+ return { result: uploadConfig, error };
+ }
+
+ private async uploadConfigImage(imagePath: string): Promise {
+ console.log(`[s3] uploading config image: ${imagePath}`);
+
+ const localPath = path.join(DATA_FOLDER, imagePath);
+ const remotePath = path.join(DATA_PATH, imagePath);
+
+ const { error } = await withError(this.uploadFileWithRetry(remotePath, localPath));
+
+ if (error) {
+ console.error(`[s3] failed to upload config image: ${error.message}`);
+
+ return error;
+ }
+
+ return null;
+ }
+}
diff --git a/app/lib/storage/types.ts b/apps/backend/src/lib/storage/types.ts
similarity index 56%
rename from app/lib/storage/types.ts
rename to apps/backend/src/lib/storage/types.ts
index b0e8079a..772f9e81 100644
--- a/app/lib/storage/types.ts
+++ b/apps/backend/src/lib/storage/types.ts
@@ -1,26 +1,40 @@
-import { PassThrough } from 'node:stream';
-
-import { type Pagination } from './pagination';
-
-import { type SiteWhiteLabelConfig, type UUID } from '@/app/types';
-import { type ReportInfo, type ReportTest } from '@/app/lib/parser/types';
+import type { PassThrough } from 'node:stream';
+import type { SiteWhiteLabelConfig, UUID } from '@playwright-reports/shared';
+import type { ReportInfo, ReportTest } from '../parser/types.js';
+import type { Pagination } from './pagination.js';
export interface Storage {
getServerDataInfo: () => Promise;
readFile: (targetPath: string, contentType: string | null) => Promise;
- readResults: (input?: ReadResultsInput) => Promise;
- readReports: (input?: ReadReportsInput) => Promise;
+ readResults: () => Promise;
+ readReports: () => Promise;
+ readReport: (reportID: string, reportPath: string) => Promise;
deleteResults: (resultIDs: string[]) => Promise;
- deleteReports: (reportIDs: string[]) => Promise;
+ deleteReports: (reports: ReportPath[]) => Promise;
saveResult: (filename: string, stream: PassThrough) => Promise;
- saveResultDetails: (resultID: string, resultDetails: ResultDetails, size: number) => Promise;
- generateReport: (resultsIds: string[], metadata?: ReportMetadata) => Promise;
- readConfigFile: () => Promise<{ result?: SiteWhiteLabelConfig; error: Error | null }>;
+ saveResultDetails: (
+ resultID: string,
+ resultDetails: ResultDetails,
+ size: number
+ ) => Promise;
+ generateReport: (
+ resultsIds: string[],
+ metadata?: ReportMetadata
+ ) => Promise<{ reportId: UUID; reportPath: string }>;
+ readConfigFile: () => Promise<{
+ result?: SiteWhiteLabelConfig;
+ error: Error | null;
+ }>;
saveConfigFile: (
- config: Partial,
+ config: Partial
) => Promise<{ result: SiteWhiteLabelConfig; error: Error | null }>;
}
+export interface ReportPath {
+ reportID: string;
+ project: string;
+}
+
export interface ReadResultsInput {
pagination?: Pagination;
project?: string;
@@ -77,12 +91,17 @@ export type Report = {
export type ReportHistory = Report & ReportInfo;
-export const isReportHistory = (report: Report | ReportHistory | undefined): report is ReportHistory =>
- !!report && typeof report === 'object' && 'stats' in report;
+export const isReportHistory = (
+ report: Report | ReportHistory | undefined
+): report is ReportHistory => !!report && typeof report === 'object' && 'stats' in report;
export type TestHistory = Report & ReportTest;
-export type ReportMetadata = Partial<{ title: string; project: string; playwrightVersion?: string }> &
+export type ReportMetadata = Partial<{
+ title: string;
+ project: string;
+ playwrightVersion?: string;
+}> &
Record;
export interface ServerDataInfo {
diff --git a/app/lib/time.ts b/apps/backend/src/lib/time.ts
similarity index 73%
rename from app/lib/time.ts
rename to apps/backend/src/lib/time.ts
index 175e9be8..fb5c5c68 100644
--- a/app/lib/time.ts
+++ b/apps/backend/src/lib/time.ts
@@ -18,3 +18,10 @@ export const parseMilliseconds = (ms: number) => {
return `${leftMs}ms`;
};
+
+export const getTimestamp = (date?: Date | string) => {
+ if (!date) return 0;
+ if (typeof date === 'string') return new Date(date).getTime();
+
+ return date.getTime();
+};
diff --git a/app/lib/transformers.ts b/apps/backend/src/lib/transformers.ts
similarity index 81%
rename from app/lib/transformers.ts
rename to apps/backend/src/lib/transformers.ts
index fb3b4770..59b48d1b 100644
--- a/app/lib/transformers.ts
+++ b/apps/backend/src/lib/transformers.ts
@@ -1,5 +1,5 @@
-import { ReportFile, ReportTest, ReportTestFilters } from '@/app/lib/parser';
-import { ReportHistory } from '@/app/lib/storage/types';
+import type { ReportFile, ReportTest, ReportTestFilters } from './parser/index.js';
+import type { ReportHistory } from './storage/types.js';
const isTestMatchingFilters = (test: ReportTest, filters?: ReportTestFilters): boolean => {
if (!filters) {
@@ -13,7 +13,7 @@ const isTestMatchingFilters = (test: ReportTest, filters?: ReportTestFilters): b
export const filterReportHistory = (
report: ReportHistory,
- filters?: ReportTestFilters,
+ filters?: ReportTestFilters
): ReportHistory & { testCount: number; totalTestCount: number } => {
const filtered = structuredClone(report);
const counter = {
@@ -47,7 +47,12 @@ export const filterReportHistory = (
};
};
-export const pluralize = (count: number, singular: string, plural: string, locale: string = 'en-US'): string => {
+export const pluralize = (
+ count: number,
+ singular: string,
+ plural: string,
+ locale: string = 'en-US'
+): string => {
const pluralRules = new Intl.PluralRules(locale);
const rule = pluralRules.select(count);
diff --git a/app/lib/url.ts b/apps/backend/src/lib/url.ts
similarity index 81%
rename from app/lib/url.ts
rename to apps/backend/src/lib/url.ts
index d35d7dbd..d8b00e88 100644
--- a/app/lib/url.ts
+++ b/apps/backend/src/lib/url.ts
@@ -1,4 +1,4 @@
-import { env } from '../config/env';
+import { env } from '../config/env.js';
export const withBase = (p = '') => {
const base = (env.API_BASE_PATH || '').replace(/\/+$/, '');
diff --git a/apps/backend/src/lib/validation/index.ts b/apps/backend/src/lib/validation/index.ts
new file mode 100644
index 00000000..d4e40b55
--- /dev/null
+++ b/apps/backend/src/lib/validation/index.ts
@@ -0,0 +1,82 @@
+import type { FastifyReply, FastifyRequest } from 'fastify';
+import type { z } from 'zod';
+
+export class ValidationError extends Error {
+ constructor(
+ message: string,
+ public details?: any
+ ) {
+ super(message);
+ this.name = 'ValidationError';
+ }
+}
+
+export function validateSchema(schema: T, data: unknown): z.infer {
+ const result = schema.safeParse(data);
+ if (!result.success) {
+ console.log('Validation error data:', data);
+ console.log('Validation error:', result.error);
+ const errorMessages = result.error.issues.map((err: any) => ({
+ field: err.path.join('.'),
+ message: err.message,
+ }));
+ throw new ValidationError('Validation failed', errorMessages);
+ }
+ return result.data;
+}
+
+export function validateQuery(schema: T) {
+ return async (request: FastifyRequest, reply: FastifyReply) => {
+ try {
+ request.query = validateSchema(schema, request.query);
+ } catch (error) {
+ if (error instanceof ValidationError) {
+ return reply.status(400).send({
+ error: 'Invalid query parameters',
+ details: error.details,
+ });
+ }
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ };
+}
+
+export function validateBody(schema: T) {
+ return async (request: FastifyRequest, reply: FastifyReply) => {
+ try {
+ request.body = validateSchema(schema, request.body);
+ } catch (error) {
+ if (error instanceof ValidationError) {
+ return reply.status(400).send({
+ error: 'Invalid request body',
+ details: error.details,
+ });
+ }
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ };
+}
+
+export function validateParams(schema: T) {
+ return async (request: FastifyRequest, reply: FastifyReply) => {
+ try {
+ request.params = validateSchema(schema, request.params);
+ } catch (error) {
+ if (error instanceof ValidationError) {
+ return reply.status(400).send({
+ error: 'Invalid URL parameters',
+ details: error.details,
+ });
+ }
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ };
+}
+
+export function createJsonSchema(zodSchema: z.ZodType) {
+ return zodTypeToJsonSchema(zodSchema);
+}
+
+function zodTypeToJsonSchema(_zodType: any): any {
+ return { type: 'any' };
+}
diff --git a/app/lib/withError.ts b/apps/backend/src/lib/withError.ts
similarity index 65%
rename from app/lib/withError.ts
rename to apps/backend/src/lib/withError.ts
index c9b37597..58e16a77 100644
--- a/app/lib/withError.ts
+++ b/apps/backend/src/lib/withError.ts
@@ -1,4 +1,6 @@
-export async function withError(promise: Promise): Promise<{ result: T | null; error: Error | null }> {
+export async function withError(
+ promise: Promise
+): Promise<{ result: T | null; error: Error | null }> {
try {
const result = await promise;
diff --git a/apps/backend/src/routes/auth.ts b/apps/backend/src/routes/auth.ts
new file mode 100644
index 00000000..fe0ee29e
--- /dev/null
+++ b/apps/backend/src/routes/auth.ts
@@ -0,0 +1,236 @@
+import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
+import jwt from 'jsonwebtoken';
+import { env } from '../config/env.js';
+
+const useAuth = !!env.API_TOKEN;
+
+// strictly recommended to specify via env var
+// Use a stable default secret when AUTH_SECRET is not set to avoid JWT decryption errors
+// This is only acceptable when auth is disabled (no API_TOKEN)
+const secret = env.AUTH_SECRET ?? 'default-secret-for-non-auth-mode';
+
+const expirationHours = env.UI_AUTH_EXPIRE_HOURS
+ ? Number.parseInt(env.UI_AUTH_EXPIRE_HOURS, 10)
+ : 2;
+const expirationSeconds = expirationHours * 60 * 60;
+
+interface AuthUser {
+ apiToken: string;
+ jwtToken: string;
+}
+
+interface AuthRequest extends Omit {
+ user?: AuthUser;
+}
+
+const createAuthTokens = (apiToken: string): AuthUser => {
+ const jwtToken = jwt.sign({ authorized: true }, secret);
+ return { apiToken, jwtToken };
+};
+
+const createNoAuthTokens = (): AuthUser => {
+ const token = jwt.sign({ authorized: true }, secret);
+ return { apiToken: token, jwtToken: token };
+};
+
+const verifyToken = (token: string): { authorized: boolean } | null => {
+ try {
+ return jwt.verify(token, secret) as { authorized: boolean };
+ } catch (_error) {
+ return null;
+ }
+};
+
+export const authenticate = async (request: AuthRequest, reply: FastifyReply) => {
+ if (!useAuth) {
+ request.user = createNoAuthTokens();
+ return;
+ }
+
+ const authHeader = request.headers.authorization;
+ const cookieToken = request.cookies.token;
+
+ let token = null;
+
+ if (authHeader?.startsWith('Bearer ')) {
+ token = authHeader.substring(7);
+ } else if (cookieToken) {
+ token = cookieToken;
+ }
+
+ if (!token) {
+ return reply.status(401).send({ error: 'Unauthorized: No token provided' });
+ }
+
+ const decoded = verifyToken(token);
+ if (!decoded) {
+ return reply.status(401).send({ error: 'Unauthorized: Invalid token' });
+ }
+
+ request.user = {
+ apiToken: '', // should not store the original API token in JWT
+ jwtToken: token,
+ };
+};
+
+export const getSession = async (request: AuthRequest, reply: FastifyReply) => {
+ if (!useAuth) {
+ const noAuthUser = createNoAuthTokens();
+ return {
+ user: {
+ apiToken: noAuthUser.apiToken,
+ jwtToken: noAuthUser.jwtToken,
+ },
+ expires: new Date(Date.now() + expirationSeconds * 1000).toISOString(),
+ };
+ }
+
+ try {
+ await authenticate(request, reply);
+
+ if (request.user) {
+ return {
+ user: {
+ apiToken: request.user.apiToken,
+ jwtToken: request.user.jwtToken,
+ },
+ expires: new Date(Date.now() + expirationSeconds * 1000).toISOString(),
+ };
+ }
+ } catch (error) {
+ return reply.status(401).send({ error: 'Unauthorized' });
+ }
+};
+
+export async function registerAuthRoutes(fastify: FastifyInstance) {
+ fastify.post('/api/auth/signin', async (request, reply) => {
+ try {
+ const { apiToken } = request.body as { apiToken?: string };
+
+ if (!useAuth) {
+ const noAuthUser = createNoAuthTokens();
+
+ // Set token in cookie
+ reply.setCookie('token', noAuthUser.jwtToken, {
+ path: '/',
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: 'lax',
+ maxAge: expirationSeconds,
+ });
+
+ return {
+ user: {
+ apiToken: noAuthUser.apiToken,
+ jwtToken: noAuthUser.jwtToken,
+ },
+ success: true,
+ };
+ }
+
+ if (!apiToken || apiToken !== env.API_TOKEN) {
+ return reply.status(401).send({
+ error: 'Invalid API token',
+ success: false,
+ });
+ }
+
+ const authUser = createAuthTokens(apiToken);
+
+ reply.setCookie('token', authUser.jwtToken, {
+ path: '/',
+ httpOnly: true,
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: 'lax',
+ maxAge: expirationSeconds,
+ });
+
+ return {
+ user: {
+ apiToken: authUser.apiToken,
+ jwtToken: authUser.jwtToken,
+ },
+ success: true,
+ };
+ } catch (error) {
+ fastify.log.error({ error }, 'Sign in error');
+ return reply.status(500).send({
+ error: 'Internal server error',
+ success: false,
+ });
+ }
+ });
+
+ fastify.post('/api/auth/signout', (_, reply) => {
+ try {
+ reply.clearCookie('token', { path: '/' });
+
+ return {
+ success: true,
+ message: 'Signed out successfully',
+ };
+ } catch (error) {
+ fastify.log.error({ error }, 'Sign out error');
+ return reply.status(500).send({
+ error: 'Internal server error',
+ success: false,
+ });
+ }
+ });
+
+ fastify.get('/api/auth/session', async (request, reply) => {
+ try {
+ const sessionData = await getSession(request as AuthRequest, reply);
+ return sessionData;
+ } catch (error) {
+ return reply.status(401).send({ error: 'Unauthorized' });
+ }
+ });
+
+ fastify.get('/api/auth/csrf', async (_request, _reply) => {
+ const csrfToken = Buffer.from(Date.now().toString()).toString('base64');
+
+ return {
+ csrfToken,
+ };
+ });
+
+ fastify.get('/api/auth/providers', async (_request, _reply) => {
+ return {
+ credentials: {
+ id: 'credentials',
+ name: 'API Token',
+ type: 'credentials',
+ signinUrl: '/api/auth/signin/credentials',
+ callbackUrl: '/api/auth/callback/credentials',
+ },
+ };
+ });
+
+ fastify.all('/api/auth/*', async (request, reply) => {
+ // Handle various next-auth endpoints for compatibility
+ const path = (request.params as { '*': string })['*'] || '';
+
+ switch (path) {
+ case 'signin':
+ return reply.send({
+ providers: [
+ {
+ id: 'credentials',
+ name: 'API Token',
+ type: 'credentials',
+ },
+ ],
+ });
+
+ case 'session':
+ return await getSession(request as AuthRequest, reply);
+
+ default:
+ return reply.status(404).send({ error: 'Not found' });
+ }
+ });
+}
+
+export type { AuthRequest, AuthUser };
+export { useAuth, secret, expirationSeconds };
diff --git a/apps/backend/src/routes/config.ts b/apps/backend/src/routes/config.ts
new file mode 100644
index 00000000..3175f0f4
--- /dev/null
+++ b/apps/backend/src/routes/config.ts
@@ -0,0 +1,253 @@
+import { writeFile } from 'node:fs/promises';
+import { join } from 'node:path';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
+import { env } from '../config/env.js';
+import { cronService } from '../lib/service/cron.js';
+import { getDatabaseStats } from '../lib/service/db/index.js';
+import { service } from '../lib/service/index.js';
+import { JiraService } from '../lib/service/jira.js';
+import { DATA_FOLDER } from '../lib/storage/constants.js';
+import { withError } from '../lib/withError.js';
+import { type AuthRequest, authenticate } from './auth.js';
+
+interface MultipartFile {
+ fieldname: string;
+ filename?: string;
+ toBuffer(): Promise;
+}
+
+interface ConfigFormData {
+ title?: string;
+ logoPath?: string;
+ faviconPath?: string;
+ reporterPaths?: string;
+ headerLinks?: string;
+ jiraBaseUrl?: string;
+ jiraEmail?: string;
+ jiraApiToken?: string;
+ jiraProjectKey?: string;
+ resultExpireDays?: string;
+ resultExpireCronSchedule?: string;
+ reportExpireDays?: string;
+ reportExpireCronSchedule?: string;
+}
+
+export async function registerConfigRoutes(fastify: FastifyInstance) {
+ fastify.get('/api/config', async (_request, reply) => {
+ const { result: config, error } = await withError(service.getConfig());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ const envInfo = {
+ authRequired: !!env.API_TOKEN,
+ database: getDatabaseStats(),
+ dataStorage: env.DATA_STORAGE,
+ s3Endpoint: env.S3_ENDPOINT,
+ s3Bucket: env.S3_BUCKET,
+ };
+
+ return { ...config, ...envInfo };
+ });
+
+ fastify.patch('/api/config', async (request: FastifyRequest, reply: FastifyReply) => {
+ try {
+ const authResult = await authenticate(request as AuthRequest, reply);
+ if (authResult) return authResult;
+
+ const data = await request.file({
+ limits: { files: 2 },
+ });
+
+ if (!data) {
+ return reply.status(400).send({ error: 'No data received' });
+ }
+
+ let logoFile: MultipartFile | null = null;
+ let faviconFile: MultipartFile | null = null;
+ const formData: ConfigFormData = {};
+
+ for await (const part of data.file) {
+ if (part.type === 'file') {
+ const filePart = part as MultipartFile;
+ if (part.fieldname === 'logo') {
+ logoFile = filePart;
+ } else if (part.fieldname === 'favicon') {
+ faviconFile = filePart;
+ }
+ } else if (part.type === 'field') {
+ const fieldName = part.fieldname as keyof ConfigFormData;
+ formData[fieldName] = part.value as string;
+ }
+ }
+
+ const config = await service.getConfig();
+
+ if (!config) {
+ return reply.status(500).send({ error: 'failed to get config' });
+ }
+
+ if (logoFile) {
+ const { error: logoError } = await withError(
+ writeFile(join(DATA_FOLDER, logoFile.filename!), Buffer.from(await logoFile.toBuffer()))
+ );
+
+ if (logoError) {
+ return reply.status(500).send({ error: `failed to save logo: ${logoError?.message}` });
+ }
+ config.logoPath = `/${logoFile.filename}`;
+ }
+
+ if (faviconFile) {
+ const { error: faviconError } = await withError(
+ writeFile(
+ join(DATA_FOLDER, faviconFile.filename!),
+ Buffer.from(await faviconFile.toBuffer())
+ )
+ );
+
+ if (faviconError) {
+ return reply.status(500).send({
+ error: `failed to save favicon: ${faviconError?.message}`,
+ });
+ }
+ config.faviconPath = `/${faviconFile.filename}`;
+ }
+
+ if (formData.title !== undefined) {
+ config.title = formData.title;
+ }
+
+ if (formData.logoPath !== undefined && !logoFile) {
+ config.logoPath = formData.logoPath;
+ }
+
+ if (formData.faviconPath !== undefined && !faviconFile) {
+ config.faviconPath = formData.faviconPath;
+ }
+
+ if (formData.reporterPaths !== undefined) {
+ try {
+ config.reporterPaths = JSON.parse(formData.reporterPaths);
+ } catch {
+ config.reporterPaths = [formData.reporterPaths];
+ }
+ }
+
+ if (formData.headerLinks !== undefined) {
+ try {
+ const parsedHeaderLinks = JSON.parse(formData.headerLinks);
+ if (parsedHeaderLinks) config.headerLinks = parsedHeaderLinks;
+ } catch (error) {
+ return reply.status(400).send({
+ error: `failed to parse header links: ${error instanceof Error ? error.message : 'Invalid JSON'}`,
+ });
+ }
+ }
+
+ if (!config.jira) {
+ config.jira = {};
+ }
+
+ if (formData.jiraBaseUrl !== undefined) config.jira.baseUrl = formData.jiraBaseUrl;
+ if (formData.jiraEmail !== undefined) config.jira.email = formData.jiraEmail;
+ if (formData.jiraApiToken !== undefined) config.jira.apiToken = formData.jiraApiToken;
+ if (formData.jiraProjectKey !== undefined) config.jira.projectKey = formData.jiraProjectKey;
+
+ if (
+ formData.jiraBaseUrl ||
+ formData.jiraEmail ||
+ formData.jiraApiToken ||
+ formData.jiraProjectKey
+ ) {
+ JiraService.resetInstance();
+ }
+
+ if (!config.cron) {
+ config.cron = {};
+ }
+
+ if (formData.resultExpireDays !== undefined) {
+ config.cron.resultExpireDays = Number.parseInt(formData.resultExpireDays, 10);
+ }
+ if (formData.resultExpireCronSchedule !== undefined) {
+ config.cron.resultExpireCronSchedule = formData.resultExpireCronSchedule;
+ }
+ if (formData.reportExpireDays !== undefined) {
+ config.cron.reportExpireDays = Number.parseInt(formData.reportExpireDays, 10);
+ }
+ if (formData.reportExpireCronSchedule !== undefined) {
+ config.cron.reportExpireCronSchedule = formData.reportExpireCronSchedule;
+ }
+
+ const { error: saveConfigError } = await withError(service.updateConfig(config));
+
+ if (saveConfigError) {
+ return reply.status(500).send({
+ error: `failed to save config: ${saveConfigError.message}`,
+ });
+ }
+
+ if (
+ config.cron?.resultExpireDays ||
+ config.cron?.resultExpireCronSchedule ||
+ config.cron?.reportExpireDays ||
+ config.cron?.reportExpireCronSchedule
+ ) {
+ await cronService.restart();
+ }
+
+ return reply.send({ message: 'config saved' });
+ } catch (error) {
+ fastify.log.error({ error }, 'Config update error');
+ return reply.status(400).send({
+ error: `config update failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ });
+ }
+ });
+
+ fastify.post('/api/config', async (request, reply) => {
+ const { result, error } = await withError(
+ service.updateConfig(request.body as Partial)
+ );
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return result;
+ });
+
+ fastify.get('/api/info', async (_request, reply) => {
+ const { result: info, error } = await withError(service.getServerInfo());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return info;
+ });
+
+ fastify.post(
+ '/api/cache/refresh',
+ {
+ schema: {
+ response: {
+ 200: { type: 'object' },
+ 400: { type: 'object', properties: { error: { type: 'string' } } },
+ },
+ },
+ },
+ async (_request, reply) => {
+ const { result, error } = await withError(service.refreshCache());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return result;
+ }
+ );
+}
diff --git a/apps/backend/src/routes/index.ts b/apps/backend/src/routes/index.ts
new file mode 100644
index 00000000..52d4f00f
--- /dev/null
+++ b/apps/backend/src/routes/index.ts
@@ -0,0 +1,16 @@
+import type { FastifyInstance } from 'fastify';
+import { registerAuthRoutes } from './auth.js';
+import { registerConfigRoutes } from './config.js';
+import { registerJiraRoutes } from './jira.js';
+import { registerReportRoutes } from './reports.js';
+import { registerResultRoutes } from './results.js';
+import { registerServeRoutes } from './serve.js';
+
+export async function registerApiRoutes(fastify: FastifyInstance) {
+ await registerAuthRoutes(fastify);
+ await registerReportRoutes(fastify);
+ await registerResultRoutes(fastify);
+ await registerConfigRoutes(fastify);
+ await registerServeRoutes(fastify);
+ await registerJiraRoutes(fastify);
+}
diff --git a/apps/backend/src/routes/jira.ts b/apps/backend/src/routes/jira.ts
new file mode 100644
index 00000000..1dc810a7
--- /dev/null
+++ b/apps/backend/src/routes/jira.ts
@@ -0,0 +1,160 @@
+import type { FastifyInstance } from 'fastify';
+import { service } from '../lib/service/index.js';
+import { JiraService } from '../lib/service/jira.js';
+import { withError } from '../lib/withError.js';
+import { type AuthRequest, authenticate } from './auth.js';
+
+interface CreateTicketRequest {
+ summary: string;
+ description: string;
+ issueType: string;
+ projectKey: string;
+ testId: string;
+ testTitle: string;
+ testOutcome: string;
+ testLocation: {
+ file: string;
+ line: number;
+ column: number;
+ };
+ reportId: string;
+ testAttachments?: Array<{
+ name: string;
+ path: string;
+ contentType: string;
+ }>;
+}
+
+export async function registerJiraRoutes(fastify: FastifyInstance) {
+ fastify.get('/api/jira/config', async (_request, reply) => {
+ try {
+ const config = await service.getConfig();
+ const jiraConfig = config.jira;
+
+ const isConfigured = !!(jiraConfig?.baseUrl && jiraConfig?.email && jiraConfig?.apiToken);
+
+ if (!isConfigured) {
+ return reply.send({
+ configured: false,
+ message: 'Jira is not configured. Please configure Jira settings in the admin panel.',
+ config: jiraConfig || {},
+ });
+ }
+
+ const jiraService = JiraService.getInstance(jiraConfig);
+
+ let issueTypes = [];
+
+ if (jiraConfig?.projectKey) {
+ try {
+ const project = await jiraService.getProject(jiraConfig.projectKey);
+
+ issueTypes = project.issueTypes || [];
+ } catch (error) {
+ fastify.log.warn(
+ { error },
+ `Could not fetch project-specific issue types for ${jiraConfig.projectKey}`
+ );
+ }
+ }
+
+ return reply.send({
+ configured: true,
+ baseUrl: jiraConfig.baseUrl,
+ defaultProjectKey: jiraConfig.projectKey,
+ issueTypes: issueTypes.map((type: any) => ({
+ id: type.id,
+ name: type.name,
+ description: type.description,
+ })),
+ });
+ } catch (error) {
+ return reply.status(500).send({
+ configured: false,
+ error: `Failed to connect to Jira: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ });
+ }
+ });
+
+ fastify.post('/api/jira/create-ticket', async (request, reply) => {
+ const authResult = await authenticate(request as AuthRequest, reply);
+ if (authResult) return authResult;
+
+ const { result: data, error: parseError } = await withError(Promise.resolve(request.body));
+
+ if (parseError) {
+ return reply.status(400).send({ error: parseError.message });
+ }
+
+ if (!data) {
+ return reply.status(400).send({ error: 'Request data is missing' });
+ }
+
+ const ticketData = data as CreateTicketRequest;
+
+ try {
+ const report = await service.getReport(ticketData.reportId);
+ const projectPath = report.project ? `${report.project}/` : '';
+
+ ticketData.testAttachments = ticketData.testAttachments?.map((att) => ({
+ ...att,
+ path: `${projectPath}${ticketData.reportId}/${att.path}`,
+ }));
+ } catch (error) {
+ fastify.log.error({ error }, `Failed to get report ${ticketData.reportId}`);
+ }
+
+ try {
+ if (!ticketData.summary || !ticketData.projectKey) {
+ return reply.status(400).send({
+ error: 'Summary and project key are required',
+ });
+ }
+
+ const config = await service.getConfig();
+ const jiraService = JiraService.getInstance(config.jira);
+
+ const jiraResponse = await jiraService.createIssue(
+ ticketData.summary,
+ ticketData.description,
+ ticketData.issueType,
+ ticketData.projectKey,
+ {
+ testId: ticketData.testId,
+ testTitle: ticketData.testTitle,
+ testOutcome: ticketData.testOutcome,
+ testLocation: ticketData.testLocation,
+ },
+ ticketData.testAttachments
+ );
+
+ return reply.status(201).send({
+ success: true,
+ issueKey: jiraResponse.key,
+ issueId: jiraResponse.id,
+ issueUrl: jiraResponse.self,
+ message: 'Jira ticket created successfully',
+ data: {
+ ...ticketData,
+ issueKey: jiraResponse.key,
+ issueId: jiraResponse.id,
+ issueUrl: jiraResponse.self,
+ created: new Date().toISOString(),
+ },
+ });
+ } catch (error) {
+ fastify.log.error({ error }, 'Failed to create Jira ticket');
+
+ if (error instanceof Error && error.message.includes('Jira configuration is incomplete')) {
+ return reply.status(500).send({
+ error:
+ 'Jira is not configured. Please set up JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.',
+ });
+ }
+
+ return reply.status(500).send({
+ error: `Failed to create Jira ticket: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ });
+ }
+ });
+}
diff --git a/apps/backend/src/routes/reports.ts b/apps/backend/src/routes/reports.ts
new file mode 100644
index 00000000..0758fbbd
--- /dev/null
+++ b/apps/backend/src/routes/reports.ts
@@ -0,0 +1,163 @@
+import type { FastifyInstance } from 'fastify';
+import {
+ DeleteReportsRequestSchema,
+ GenerateReportRequestSchema,
+ GetReportParamsSchema,
+ ListReportsQuerySchema,
+} from '../lib/schemas/index.js';
+import { service } from '../lib/service/index.js';
+import { parseFromRequest } from '../lib/storage/pagination.js';
+import { validateSchema } from '../lib/validation/index.js';
+import { withError } from '../lib/withError.js';
+
+export async function registerReportRoutes(fastify: FastifyInstance) {
+ fastify.get('/api/report/list', async (request, reply) => {
+ try {
+ const query = validateSchema(ListReportsQuerySchema, request.query);
+ const params = new URLSearchParams();
+ if (query.limit !== undefined) {
+ params.append('limit', query.limit.toString());
+ }
+ if (query.offset !== undefined) {
+ params.append('offset', query.offset.toString());
+ }
+ const pagination = parseFromRequest(params);
+
+ const { result: reports, error } = await withError(
+ service.getReports({
+ pagination,
+ project: query.project,
+ search: query.search,
+ })
+ );
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return reports;
+ } catch (error) {
+ console.error('[routes] list reports error:', error);
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ });
+
+ fastify.get('/api/report/:id', async (request, reply) => {
+ try {
+ const params = validateSchema(GetReportParamsSchema, request.params);
+ const { result: report, error } = await withError(service.getReport(params.id));
+
+ if (error) {
+ return reply.status(404).send({ error: error.message });
+ }
+
+ return report;
+ } catch (error) {
+ console.error('[routes] get report error:', error);
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ });
+
+ fastify.get('/api/report/projects', async (_request, reply) => {
+ try {
+ const { result: projects, error } = await withError(service.getReportsProjects());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return projects;
+ } catch (error) {
+ console.error('[routes] get projects error:', error);
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ });
+
+ fastify.post('/api/report/generate', async (request, reply) => {
+ try {
+ const body = (request.body as { resultsIds?: unknown; [key: string]: unknown }) || {};
+
+ if (!body.resultsIds || !Array.isArray(body.resultsIds)) {
+ return reply.status(400).send({ error: 'resultsIds array is required' });
+ }
+
+ if (body.resultsIds.length === 0) {
+ return reply.status(400).send({ error: 'At least one result ID must be provided' });
+ }
+
+ const validatedBody = validateSchema(GenerateReportRequestSchema, body);
+ console.log(`[routes] generate report request:`, validatedBody);
+
+ const metadata: Record = {
+ ...(validatedBody.project && { project: validatedBody.project }),
+ ...(validatedBody.playwrightVersion && {
+ playwrightVersion: validatedBody.playwrightVersion,
+ }),
+ ...(validatedBody.title && { title: validatedBody.title }),
+ ...Object.fromEntries(
+ Object.entries(validatedBody)
+ .filter(
+ ([key]) =>
+ !['resultsIds', 'project', 'playwrightVersion', 'title'].includes(key) &&
+ typeof validatedBody[key as keyof typeof validatedBody] === 'string'
+ )
+ .map(([key, value]) => [key, String(value)])
+ ),
+ };
+
+ const { result, error } = await withError(
+ service.generateReport(validatedBody.resultsIds, metadata)
+ );
+
+ if (error) {
+ console.error(`[routes] generate report error:`, error.message);
+
+ if (error instanceof Error && error.message.includes('ENOENT: no such file or directory')) {
+ return reply.status(404).send({
+ error: `ResultID not found: ${error.message}`,
+ });
+ }
+
+ return reply.status(400).send({ error: error.message });
+ }
+
+ console.log(`[routes] generate report success:`, result);
+ return result;
+ } catch (error) {
+ console.error('[routes] generate report validation error:', error);
+ return reply.status(400).send({ error: 'Invalid request format' });
+ }
+ });
+
+ fastify.delete('/api/report/delete', async (request, reply) => {
+ try {
+ const body = (request.body as { reportsIds?: unknown }) || { reportsIds: [] };
+
+ if (!body.reportsIds || !Array.isArray(body.reportsIds)) {
+ return reply.status(400).send({ error: 'reportsIds array is required' });
+ }
+
+ if (body.reportsIds.length === 0) {
+ return reply.status(400).send({ error: 'At least one report ID must be provided' });
+ }
+
+ const validatedBody = validateSchema(DeleteReportsRequestSchema, body);
+ console.log(`[routes] delete reports:`, validatedBody.reportsIds);
+
+ const { error } = await withError(service.deleteReports(validatedBody.reportsIds));
+
+ if (error) {
+ console.error(`[routes] delete reports error:`, error);
+ return reply.status(404).send({ error: error.message });
+ }
+
+ return reply.status(200).send({
+ message: 'Reports deleted successfully',
+ reportsIds: validatedBody.reportsIds,
+ });
+ } catch (error) {
+ console.error('[routes] delete reports validation error:', error);
+ return reply.status(400).send({ error: 'Invalid request format' });
+ }
+ });
+}
diff --git a/apps/backend/src/routes/results.ts b/apps/backend/src/routes/results.ts
new file mode 100644
index 00000000..db86af59
--- /dev/null
+++ b/apps/backend/src/routes/results.ts
@@ -0,0 +1,291 @@
+import { randomUUID } from 'node:crypto';
+import { PassThrough } from 'node:stream';
+import type { FastifyInstance } from 'fastify';
+import { DeleteResultsRequestSchema, ListResultsQuerySchema } from '../lib/schemas/index.js';
+import { service } from '../lib/service/index.js';
+import { DEFAULT_STREAM_CHUNK_SIZE } from '../lib/storage/constants.js';
+import { parseFromRequest } from '../lib/storage/pagination.js';
+import { validateSchema } from '../lib/validation/index.js';
+import { withError } from '../lib/withError.js';
+import { type AuthRequest, authenticate } from './auth.js';
+
+export async function registerResultRoutes(fastify: FastifyInstance) {
+ fastify.get('/api/result/list', async (request, reply) => {
+ try {
+ const query = validateSchema(ListResultsQuerySchema, request.query);
+ const params = new URLSearchParams();
+ if (query.limit !== undefined) {
+ params.append('limit', query.limit.toString());
+ }
+ if (query.offset !== undefined) {
+ params.append('offset', query.offset.toString());
+ }
+ const pagination = parseFromRequest(params);
+ const tags = query.tags ? query.tags.split(',').filter(Boolean) : [];
+
+ const { result, error } = await withError(
+ service.getResults({
+ pagination,
+ project: query.project,
+ tags,
+ search: query.search,
+ })
+ );
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return result;
+ } catch (error) {
+ console.error('[routes] list results error:', error);
+ return reply.status(500).send({ error: 'Internal server error' });
+ }
+ });
+
+ fastify.get('/api/result/projects', async (_, reply) => {
+ const { result: projects, error } = await withError(service.getResultsProjects());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return projects;
+ });
+
+ fastify.get('/api/result/tags', async (_, reply) => {
+ const { result: tags, error } = await withError(service.getResultsTags());
+
+ if (error) {
+ return reply.status(400).send({ error: error.message });
+ }
+
+ return tags;
+ });
+
+ fastify.delete(
+ '/api/result/delete',
+ {
+ config: {
+ rawBody: true,
+ },
+ },
+ async (request, reply) => {
+ try {
+ const body = (request.body as { resultsIds?: unknown }) || { resultsIds: [] };
+
+ if (!body.resultsIds || !Array.isArray(body.resultsIds)) {
+ return reply.status(400).send({ error: 'resultsIds array is required' });
+ }
+
+ if (body.resultsIds.length === 0) {
+ return reply.status(400).send({ error: 'At least one result ID must be provided' });
+ }
+
+ const validatedBody = validateSchema(DeleteResultsRequestSchema, body);
+ console.log(`[routes] delete results:`, validatedBody.resultsIds);
+
+ const { error } = await withError(service.deleteResults(validatedBody.resultsIds));
+
+ if (error) {
+ console.error(`[routes] delete results error:`, error);
+ return reply.status(404).send({ error: error.message });
+ }
+
+ console.log(`[routes] delete results - deletion successful`);
+ return reply.status(200).send({
+ message: 'Results files deleted successfully',
+ resultsIds: validatedBody.resultsIds,
+ });
+ } catch (error) {
+ console.error('[routes] delete results validation error:', error);
+ return reply.status(400).send({ error: 'Invalid request format' });
+ }
+ }
+ );
+
+ fastify.put('/api/result/upload', async (request, reply) => {
+ const authResult = await authenticate(request as AuthRequest, reply);
+ if (authResult) return authResult;
+
+ const resultID = randomUUID();
+ const fileName = `${resultID}.zip`;
+
+ const query = request.query as Record;
+ const contentLength = query['fileContentLength'] || '';
+
+ if (contentLength || Number.parseInt(contentLength, 10)) {
+ console.log(
+ `[upload] fileContentLength query parameter is provided for result ${resultID}, using presigned URL flow`
+ );
+ }
+
+ // if there is fileContentLength query parameter we can use presigned URL for direct upload
+ const presignedUrl = contentLength ? await service.getPresignedUrl(fileName) : '';
+
+ const resultDetails: Record = {};
+ let fileSize = 0;
+
+ const filePassThrough = new PassThrough({
+ highWaterMark: DEFAULT_STREAM_CHUNK_SIZE,
+ });
+
+ try {
+ const data = await request.file({
+ limits: { files: 1, fileSize: 100 * 1024 * 1024 },
+ });
+
+ if (!data) {
+ return reply.status(400).send({ error: 'upload result failed: No file received' });
+ }
+
+ for (const [key, prop] of Object.entries(data.fields)) {
+ if (key === 'file') continue;
+
+ if (prop && typeof prop === 'object' && 'value' in prop) {
+ resultDetails[key] = String(prop.value);
+ } else {
+ resultDetails[key] = typeof prop === 'string' ? prop : String(prop);
+ }
+ }
+
+ if (data.file && !Array.isArray(data.file)) {
+ const saveResultPromise = service.saveResult(
+ fileName,
+ filePassThrough,
+ presignedUrl,
+ contentLength
+ );
+
+ let isPaused = false;
+
+ data.file.on('data', (chunk: Buffer) => {
+ fileSize += chunk.length;
+
+ const canContinue = filePassThrough.write(chunk);
+
+ if (!canContinue && !isPaused) {
+ isPaused = true;
+ data.file?.pause();
+ }
+ });
+
+ filePassThrough.on('drain', () => {
+ if (isPaused) {
+ isPaused = false;
+ data.file?.resume();
+ }
+ });
+
+ data.file.on('end', () => {
+ console.log('[upload] file ended');
+ filePassThrough.end();
+ });
+
+ data.file.on('error', (error) => {
+ console.error('[upload] file error:', error);
+ filePassThrough.destroy();
+ throw error;
+ });
+
+ data.file.pipe(filePassThrough);
+ await saveResultPromise;
+
+ if (contentLength) {
+ const expected = Number.parseInt(contentLength, 10);
+ if (Number.isFinite(expected) && expected > 0 && fileSize !== expected) {
+ return reply.status(400).send({
+ error: `Size mismatch: received ${fileSize} bytes, expected ${expected} bytes`,
+ });
+ }
+ }
+ }
+
+ const { result: uploadResult, error: uploadResultDetailsError } = await withError(
+ service.saveResultDetails(resultID, resultDetails, fileSize)
+ );
+
+ if (uploadResultDetailsError) {
+ return reply.status(400).send({
+ error: `upload result details failed: ${uploadResultDetailsError.message}`,
+ });
+ }
+
+ let generatedReport = null;
+
+ if (
+ resultDetails.shardCurrent &&
+ resultDetails.shardTotal &&
+ resultDetails.triggerReportGeneration === 'true'
+ ) {
+ const { result: results, error: resultsError } = await withError(
+ service.getResults({
+ testRun: resultDetails.testRun,
+ })
+ );
+
+ if (resultsError) {
+ return reply.status(500).send({
+ error: `failed to generate report: ${resultsError.message}`,
+ });
+ }
+
+ const testRunResults = results?.results.filter(
+ (result) =>
+ result.testRun === resultDetails.testRun &&
+ (resultDetails.project ? result.project === resultDetails.project : true)
+ );
+
+ console.log(
+ `found ${testRunResults?.length} results for the test run ${resultDetails.testRun}`
+ );
+
+ if (testRunResults?.length === Number.parseInt(resultDetails.shardTotal)) {
+ const ids = testRunResults.map((result) => result.resultID);
+
+ console.log('triggerReportGeneration for', resultDetails.testRun, ids);
+ const { result, error } = await withError(
+ service.generateReport(ids, {
+ project: resultDetails.project,
+ testRun: resultDetails.testRun,
+ playwrightVersion: resultDetails.playwrightVersion,
+ })
+ );
+
+ if (error) {
+ return reply.status(500).send({ error: `failed to generate report: ${error.message}` });
+ }
+
+ generatedReport = result;
+ }
+ }
+
+ return reply.status(200).send({
+ message: 'Success',
+ data: {
+ ...uploadResult,
+ generatedReport,
+ },
+ });
+ } catch (error) {
+ console.error('[upload] error:', error);
+
+ if (!filePassThrough.destroyed) {
+ filePassThrough.destroy();
+ }
+
+ const { error: deleteError } = await withError(service.deleteResults([resultID]));
+ if (deleteError) {
+ console.error(`[upload] cleanup failed for result ${resultID}:`, deleteError);
+ reply.status(400).send({
+ error: `upload result failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ });
+ return;
+ }
+
+ return reply.status(400).send({
+ error: `upload result failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
+ });
+ }
+ });
+}
diff --git a/apps/backend/src/routes/serve.ts b/apps/backend/src/routes/serve.ts
new file mode 100644
index 00000000..33f7059a
--- /dev/null
+++ b/apps/backend/src/routes/serve.ts
@@ -0,0 +1,83 @@
+import { access, readFile } from 'node:fs/promises';
+import { join } from 'node:path';
+import type { FastifyInstance } from 'fastify';
+import mime from 'mime';
+import { env } from '../config/env.js';
+import { DATA_FOLDER } from '../lib/storage/constants.js';
+import { storage } from '../lib/storage/index.js';
+import { withError } from '../lib/withError.js';
+import { type AuthRequest, authenticate } from './auth.js';
+
+export async function registerServeRoutes(fastify: FastifyInstance) {
+ fastify.get('/api/serve/*', async (request, reply) => {
+ try {
+ const filePath = (request.params as { '*': string })['*'] || '';
+ const targetPath = decodeURI(filePath);
+
+ const authRequired = !!env.API_TOKEN;
+
+ if (authRequired) {
+ const authResult = await authenticate(request as AuthRequest, reply);
+ if (authResult) return authResult;
+ }
+
+ const contentType = mime.getType(targetPath.split('/').pop() || '');
+
+ if (!contentType && !targetPath.includes('.')) {
+ return reply.code(404).send({ error: 'Not Found' });
+ }
+
+ const { result: content, error } = await withError(
+ storage.readFile(targetPath, contentType || null)
+ );
+
+ if (error || !content) {
+ return reply.code(404).send({
+ error: `Could not read file: ${error?.message || 'File not found'}`,
+ });
+ }
+
+ const headers: Record = {
+ 'Content-Type': contentType ?? 'application/octet-stream',
+ };
+
+ if ((request as AuthRequest).user?.apiToken) {
+ headers['X-API-Token'] = (request as AuthRequest).user!.apiToken;
+ }
+
+ return reply.code(200).headers(headers).send(content);
+ } catch (error) {
+ fastify.log.error({ error }, 'File serving error');
+ return reply.code(500).send({ error: 'Internal server error' });
+ }
+ });
+
+ fastify.get('/api/static/*', async (request, reply) => {
+ try {
+ const filePath = (request.params as { '*': string })['*'] || '';
+ const targetPath = decodeURI(filePath);
+
+ const contentType = mime.getType(targetPath.split('/').pop() || '');
+
+ if (!contentType && !targetPath.includes('.')) {
+ return reply.code(404).send({ error: 'Not Found' });
+ }
+
+ const imageDataPath = join(DATA_FOLDER, targetPath);
+ const imagePublicPath = join(process.cwd(), 'public', targetPath);
+
+ const { error: dataAccessError } = await withError(access(imageDataPath));
+ const imagePath = dataAccessError ? imagePublicPath : imageDataPath;
+
+ const imageBuffer = await readFile(imagePath);
+
+ return reply
+ .code(200)
+ .header('Content-Type', contentType || 'image/*')
+ .send(imageBuffer);
+ } catch (error) {
+ fastify.log.error({ error }, 'Static file serving error');
+ return reply.code(404).send({ error: 'File not found' });
+ }
+ });
+}
diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json
new file mode 100644
index 00000000..5c2d3e32
--- /dev/null
+++ b/apps/backend/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "ESNext",
+ "lib": ["ES2022"],
+ "moduleResolution": "bundler",
+ "rootDir": "./src",
+ "outDir": "./dist",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "paths": {
+ "@/*": ["./src/*"],
+ "@playwright-reports/shared": ["./node_modules/@playwright-reports/shared/src"]
+ }
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/apps/backend/tsconfig.node.json b/apps/backend/tsconfig.node.json
new file mode 100644
index 00000000..eca66688
--- /dev/null
+++ b/apps/backend/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example
new file mode 100644
index 00000000..25c035c7
--- /dev/null
+++ b/apps/frontend/.env.example
@@ -0,0 +1,8 @@
+# Frontend environment variables for Vite
+# All variables must be prefixed with VITE_ to be available in the browser
+
+# API base path - useful when frontend is served from a subdirectory
+VITE_API_BASE_PATH=
+
+# API URL - fallback for production when backend is on different origin
+VITE_API_URL=http://localhost:3001
\ No newline at end of file
diff --git a/apps/frontend/index.html b/apps/frontend/index.html
new file mode 100644
index 00000000..b673d13e
--- /dev/null
+++ b/apps/frontend/index.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Playwright Reports Server
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/frontend/package-lock.json b/apps/frontend/package-lock.json
new file mode 100644
index 00000000..29c00bfb
--- /dev/null
+++ b/apps/frontend/package-lock.json
@@ -0,0 +1,8151 @@
+{
+ "name": "playwright-reports-server-frontend",
+ "version": "5.7.4",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "playwright-reports-server-frontend",
+ "version": "5.7.4",
+ "dependencies": {
+ "@heroui/link": "2.2.12",
+ "@heroui/navbar": "2.2.13",
+ "@heroui/react": "2.7.4",
+ "@heroui/switch": "2.2.13",
+ "@heroui/system": "2.4.11",
+ "@heroui/theme": "2.4.11",
+ "@react-aria/selection": "^3.12.1",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.15.0",
+ "@react-aria/visually-hidden": "^3.8.28",
+ "@tanstack/react-query": "^5.59.15",
+ "@tanstack/react-query-devtools": "^5.59.15",
+ "clsx": "^2.1.1",
+ "framer-motion": "^11.11.8",
+ "next-themes": "^0.4.6",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^7.0.2",
+ "recharts": "^2.13.0",
+ "sonner": "^1.5.0",
+ "tailwind-merge": "^3.0.2"
+ },
+ "devDependencies": {
+ "@types/react": "18.3.11",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "autoprefixer": "10.4.20",
+ "postcss": "8.4.47",
+ "tailwind-variants": "0.2.1",
+ "tailwindcss": "3.4.13",
+ "typescript": "5.2.2",
+ "vite": "^6.0.3"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
+ "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
+ "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
+ "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/fast-memoize": "2.2.7",
+ "@formatjs/intl-localematcher": "0.6.2",
+ "decimal.js": "^10.4.3",
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@formatjs/fast-memoize": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
+ "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@formatjs/icu-messageformat-parser": {
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz",
+ "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.3.6",
+ "@formatjs/icu-skeleton-parser": "1.8.16",
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@formatjs/icu-skeleton-parser": {
+ "version": "1.8.16",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz",
+ "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.3.6",
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
+ "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@heroui/accordion": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/accordion/-/accordion-2.2.12.tgz",
+ "integrity": "sha512-ged/4UkDgro/6yL8vN7tdgMYcClq1ttA3dpF8xfqhoThS3P8yA9I/rX4Bf6WB1V/QOPnDpat6N9nJj5mKTwUGA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/divider": "2.2.10",
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-accordion": "2.2.7",
+ "@react-aria/button": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/tree": "3.8.7",
+ "@react-types/accordion": "3.0.0-alpha.26",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/accordion/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/alert": {
+ "version": "2.2.15",
+ "resolved": "https://registry.npmjs.org/@heroui/alert/-/alert-2.2.15.tgz",
+ "integrity": "sha512-Zvwilzf5iYiL6aKz5FV2yzjImBi3jE0NYXLjLR7h1zczUSewKaUG0aqYCo8mw3U9SWX7Nj2DP1mboLyNCD0mRA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/button": "2.2.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/utils": "3.10.5"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/alert/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/aria-utils": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/aria-utils/-/aria-utils-2.2.12.tgz",
+ "integrity": "sha512-vTcIo0NE6htNY6vSltmmwgQH5Lye+6/dBWcFG18qmBptiwFjJ6EPxoHzBNtUkRY2wJFYNNKOfPViQBe9qQepwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-rsc-utils": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system": "2.4.11",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/collections": "3.12.1",
+ "@react-stately/overlays": "3.6.13",
+ "@react-types/overlays": "3.8.12",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/aria-utils/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/autocomplete": {
+ "version": "2.3.16",
+ "resolved": "https://registry.npmjs.org/@heroui/autocomplete/-/autocomplete-2.3.16.tgz",
+ "integrity": "sha512-brOUl5cuIaWgftOffFGi1IG4LijKRCvMGgWL8MNylI1xrsBfl1VmeRuSn8CpJDKllZICseePOTerLKITl/50Kg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/button": "2.2.15",
+ "@heroui/form": "2.1.14",
+ "@heroui/input": "2.4.15",
+ "@heroui/listbox": "2.3.14",
+ "@heroui/popover": "2.3.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/scroll-shadow": "2.3.10",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/spinner": "2.2.12",
+ "@heroui/use-aria-button": "2.2.9",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/combobox": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/combobox": "3.10.2",
+ "@react-types/combobox": "3.13.2",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/autocomplete/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/autocomplete/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/avatar": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/avatar/-/avatar-2.2.11.tgz",
+ "integrity": "sha512-exXfXsma3X33YoU3C7n96zctGRtOn8yIsIF97quTekSThWn4bcCCo2Otu2hLbGZVR5QikyPWlG6MnXTohzqXxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-image": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/avatar/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/badge": {
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/@heroui/badge/-/badge-2.2.10.tgz",
+ "integrity": "sha512-ZY+7zvgHUW7Ye4Epdd4GnbmJgf59pGjxGML6Jm+R+GvjdqUSpneRW+QUNzwL8paSwXbGTP48nnQh9fMs+PKyRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/breadcrumbs": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/breadcrumbs/-/breadcrumbs-2.2.11.tgz",
+ "integrity": "sha512-DfrJhUT+HyiFh/F5j6CI7yxVuwd+F2sfJOC3EJrrbsrUS1q5ZXapkX1e4hkzAJ4SG31cb+fM3Np+8ga/Vdq+sg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/breadcrumbs": "3.5.20",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/breadcrumbs": "3.7.10",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/breadcrumbs/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/button": {
+ "version": "2.2.15",
+ "resolved": "https://registry.npmjs.org/@heroui/button/-/button-2.2.15.tgz",
+ "integrity": "sha512-ixKxTiwHBMz0vVur4vKGBplOwJwZx0gg1fb6Rzs79CivXu/pgZQkGj9SyC3smkJ+PNRMHxKnMg5haMTZCiINpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/ripple": "2.2.12",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/spinner": "2.2.12",
+ "@heroui/use-aria-button": "2.2.9",
+ "@react-aria/button": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/button": "3.10.2",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/button/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/calendar": {
+ "version": "2.2.15",
+ "resolved": "https://registry.npmjs.org/@heroui/calendar/-/calendar-2.2.15.tgz",
+ "integrity": "sha512-B8qfMwp6ijc7rY6SrN4XxBHGYCJodmAs6wF51R8H7Cmn+2YW4nCbjl+thf8RdE8oA/f130rvew43cdJPjHcy7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/button": "2.2.15",
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-button": "2.2.9",
+ "@internationalized/date": "3.7.0",
+ "@react-aria/calendar": "3.7.0",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/calendar": "3.7.0",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/button": "3.10.2",
+ "@react-types/calendar": "3.6.0",
+ "@react-types/shared": "3.27.0",
+ "@types/lodash.debounce": "^4.0.7",
+ "scroll-into-view-if-needed": "3.0.10"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/calendar/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/calendar/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/card": {
+ "version": "2.2.14",
+ "resolved": "https://registry.npmjs.org/@heroui/card/-/card-2.2.14.tgz",
+ "integrity": "sha512-s8ZaCCSChkGehvLGYX3VEDX70PzOwQAG6ikXaEeHD3poO7wMZzdxQw73RtCGf/XAlJCpWgJ18xTdHGfm3aQESQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/ripple": "2.2.12",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-button": "2.2.9",
+ "@react-aria/button": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/card/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/checkbox": {
+ "version": "2.3.14",
+ "resolved": "https://registry.npmjs.org/@heroui/checkbox/-/checkbox-2.3.14.tgz",
+ "integrity": "sha512-Yn1h6IhYlhxEsR235BUgHzh6yAR6JPTzdr97PSKf/xJox0D4HK6uyKj0YCmPwIgYCxGfd1NaqXRTm3FM4RRkFg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-callback-ref": "2.1.6",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/checkbox": "3.15.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/checkbox": "3.6.11",
+ "@react-stately/toggle": "3.8.1",
+ "@react-types/checkbox": "3.9.1",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.3",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/checkbox/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/checkbox/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/chip": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/chip/-/chip-2.2.11.tgz",
+ "integrity": "sha512-FFSK+bwnbZFXA2zk/Y49VKvwZlgMCEk6L0du/4knyFc1vtoI2+vr+W8uRM82xXAXptCkrZlXKm6vlDKaKTDH2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/checkbox": "3.9.1"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/chip/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/code": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/code/-/code-2.2.11.tgz",
+ "integrity": "sha512-nNz8wxpm5epPWptVca1sWKpqYvCEGXVWzuuTLcXLX4lmmZyE/Dj0G3rlf1Sq8skxzQpdfPy52mvUog+/ONrJfQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system-rsc": "2.3.10"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/date-input": {
+ "version": "2.3.14",
+ "resolved": "https://registry.npmjs.org/@heroui/date-input/-/date-input-2.3.14.tgz",
+ "integrity": "sha512-p7qgD5BAMuJGQcXMUbRdMG0tXRPZtrLiF/sMysVHUYsxLTAdi99j+7a1l905GhLHSsHuqdWoGr/YfAxVzytftg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@internationalized/date": "3.7.0",
+ "@react-aria/datepicker": "3.13.0",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/datepicker": "3.12.0",
+ "@react-types/datepicker": "3.10.0",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/date-input/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/date-picker": {
+ "version": "2.3.15",
+ "resolved": "https://registry.npmjs.org/@heroui/date-picker/-/date-picker-2.3.15.tgz",
+ "integrity": "sha512-MLzeMB1//WClwKF6IAuEcHChiobd4SVa0jr5Y7ScYzj4Gw9qvDAd/cfIZUrhQ0nXHZUvwmfcLEi2YdVqMuFWZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/button": "2.2.15",
+ "@heroui/calendar": "2.2.15",
+ "@heroui/date-input": "2.3.14",
+ "@heroui/form": "2.1.14",
+ "@heroui/popover": "2.3.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@internationalized/date": "3.7.0",
+ "@react-aria/datepicker": "3.13.0",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/datepicker": "3.12.0",
+ "@react-stately/overlays": "3.6.13",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/datepicker": "3.10.0",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/date-picker/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/divider": {
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/@heroui/divider/-/divider-2.2.10.tgz",
+ "integrity": "sha512-L3aEQ+ClUafzFJCmgCPjWL3ZDVHqSPRjLRE0XHpqC2xAP5dU0Hq44lhzCYHHKHHk1ZP/f1WDeas65C8lcHLZpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-rsc-utils": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system-rsc": "2.3.10",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/dom-animation": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/dom-animation/-/dom-animation-2.1.6.tgz",
+ "integrity": "sha512-l4xh+y02lmoJVdLR0cjpsa7LjLIvVQCX+w+S2KW6tOoPKmHlyW/8r7h6SqPB4Ua1NZGmRHtlYmw+mw47yqyTjw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1"
+ }
+ },
+ "node_modules/@heroui/drawer": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/drawer/-/drawer-2.2.12.tgz",
+ "integrity": "sha512-27OEnfn82S2b143C372yKx8DBUct5htbk6KFHlH/enZeMsppOLnSCXsX7QpZas8c6B5k03otvUyL+YQ29/JdTw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/modal": "2.2.12",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/dropdown": {
+ "version": "2.3.15",
+ "resolved": "https://registry.npmjs.org/@heroui/dropdown/-/dropdown-2.3.15.tgz",
+ "integrity": "sha512-MoOFQJy8MBL5Qiclirt5LouEYi9+4H4wlrAdCYdsGGrdoivS2UT/jllc2oEwRLYyRLgnkKyqh5UqnxHWNrnX6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/menu": "2.2.14",
+ "@heroui/popover": "2.3.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/menu": "3.17.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/menu": "3.9.1",
+ "@react-types/menu": "3.9.14"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/dropdown/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/form": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/@heroui/form/-/form-2.1.14.tgz",
+ "integrity": "sha512-4OZ8dqekSkXca1hbhU1gUBEdrVm6QHafrzbyTpDHaohwE+ea+OuWKn5ttlS/S5wamcnxTErmG3uyvsZGsDspBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system": "2.4.11",
+ "@heroui/theme": "2.4.11",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/form": "3.1.1",
+ "@react-types/form": "3.7.9",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/@heroui/form/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/framer-utils": {
+ "version": "2.1.11",
+ "resolved": "https://registry.npmjs.org/@heroui/framer-utils/-/framer-utils-2.1.11.tgz",
+ "integrity": "sha512-E8MLdLKvIVlAEVVeivORivt0HzN9y9LunmcOok3BhrBhSI0yBVBMrgd4XZJzFsNipgu2L687nxHbWeUs8jK/5g==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system": "2.4.11",
+ "@heroui/use-measure": "2.1.6"
+ },
+ "peerDependencies": {
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/image": {
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/@heroui/image/-/image-2.2.10.tgz",
+ "integrity": "sha512-02v0bJShCwaoXAjfMLGV900HsJ4J5YtW3OHJD/TIGWQzHNYxv7Mls4u2PyfUpk6IDimlZ+fIiEjfV0zR/HY3MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-image": "2.1.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/input": {
+ "version": "2.4.15",
+ "resolved": "https://registry.npmjs.org/@heroui/input/-/input-2.4.15.tgz",
+ "integrity": "sha512-ImnbIQ6h6gHgYTho1l+t+rzEehl86HJsshycoAfCi0Pn5U9KAe0Xv/ePZ6PeVhCC25QUcv1Ey5G2AkgobF49tg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/textfield": "3.16.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/shared": "3.27.0",
+ "@react-types/textfield": "3.11.0",
+ "react-textarea-autosize": "^8.5.3"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/input-otp": {
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/@heroui/input-otp/-/input-otp-2.1.14.tgz",
+ "integrity": "sha512-3Nd3+6zRxxIX6rSwdAoCm1eIqsyLqGjnFQhVG55FCyeWllfl3OQEwe3QZuQvpeBUVudeqBFCu7m2+Wbkv+r83g==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/form": "3.0.12",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/form": "3.1.1",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/textfield": "3.11.0",
+ "input-otp": "1.4.1"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/@heroui/input-otp/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/input/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/kbd": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/kbd/-/kbd-2.2.11.tgz",
+ "integrity": "sha512-yM1B4lxW2tgptKDt9rdw7T/HDEHGZLoSvB9V6X+0vJ/AjQMFZcqAIrYEUkM8BupX+Mokrur7dc036Mo3pYk5ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system-rsc": "2.3.10",
+ "@react-aria/utils": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/kbd/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/link": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/link/-/link-2.2.12.tgz",
+ "integrity": "sha512-LQTD9xSwEosgMHjZJfc0kN3AmBJfRv8W7LRuevHanSwusYvyreHGRoESIjPnTJ1+KNC01SuJlHt3K0y+JCkSfg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-link": "2.2.10",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/link": "3.7.8",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/link": "3.5.10"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/link/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/listbox": {
+ "version": "2.3.14",
+ "resolved": "https://registry.npmjs.org/@heroui/listbox/-/listbox-2.3.14.tgz",
+ "integrity": "sha512-PANgexYzI7L9em6F/va0TzFrnPOLMgSWKgUTcmdAGrOefWnD7CwxyFeduiNNNDZ1m/WmLzWPwjPLDTxahHIfpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/divider": "2.2.10",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-is-mobile": "2.2.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/listbox": "3.14.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/list": "3.11.2",
+ "@react-types/menu": "3.9.14",
+ "@react-types/shared": "3.27.0",
+ "@tanstack/react-virtual": "3.11.3"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/listbox/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/menu": {
+ "version": "2.2.14",
+ "resolved": "https://registry.npmjs.org/@heroui/menu/-/menu-2.2.14.tgz",
+ "integrity": "sha512-0CqyB2tsL/6ftxMShndyFHZySIUuqJHy4i3v7CP6NbnYv7oL6HVRB3ysg6qVaiXILF5S8HTTJNU1/kYvVgjfUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/divider": "2.2.10",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-is-mobile": "2.2.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/menu": "3.17.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/menu": "3.9.1",
+ "@react-stately/tree": "3.8.7",
+ "@react-types/menu": "3.9.14",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/menu/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/modal": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/modal/-/modal-2.2.12.tgz",
+ "integrity": "sha512-kWoghgH/oihaBv0PzkfLbKDXWEZRVUHUEaxHCTI5Ut/5rAwxR/tYW9Wy+UW6m5nshefsq+RYh5cD9iPvDv4T1A==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-button": "2.2.9",
+ "@heroui/use-aria-modal-overlay": "2.2.8",
+ "@heroui/use-disclosure": "2.2.7",
+ "@heroui/use-draggable": "2.1.7",
+ "@react-aria/dialog": "3.5.21",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/overlays": "3.6.13",
+ "@react-types/overlays": "3.8.12"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/modal/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/navbar": {
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/@heroui/navbar/-/navbar-2.2.13.tgz",
+ "integrity": "sha512-gXg0j0rDxKG2SGZF/8x62O6K+yKcMXgVYx1Bs7yCvOO0Nc3PtOfkShxZqCMxY9d5qYHEI2hq3QvxjcDVXIoypg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-scroll-position": "2.1.6",
+ "@react-aria/button": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/toggle": "3.8.1",
+ "@react-stately/utils": "3.10.5"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/navbar/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/number-input": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@heroui/number-input/-/number-input-2.0.5.tgz",
+ "integrity": "sha512-GgvF3jPYzYG2fNpVYDC2wFjKRdJbM987enHzMheK2PaRo0Kf8nNaxVQZX/ZFJY1KVJeAHvQ/QqSDEHBmDp+5rQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/button": "2.2.15",
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/numberfield": "3.11.10",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/numberfield": "3.9.9",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/button": "3.10.2",
+ "@react-types/numberfield": "3.8.8",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/number-input/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/pagination": {
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/@heroui/pagination/-/pagination-2.2.13.tgz",
+ "integrity": "sha512-TJankzcEb9unzvVKL6nWbvxD4U1RPhDZlkO26LylIRW7/bLHbeehJeY0a2yHxIIVjtiaNTyQK2MV9PUXdtKfrg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-intersection-observer": "2.2.7",
+ "@heroui/use-pagination": "2.2.8",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "scroll-into-view-if-needed": "3.0.10"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/pagination/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/popover": {
+ "version": "2.3.15",
+ "resolved": "https://registry.npmjs.org/@heroui/popover/-/popover-2.3.15.tgz",
+ "integrity": "sha512-KPLRB5L5a185pyCjyFDanGfVb8x/jni4wNpZB0OaPmOHj1/uIDACkmtavIqxIHu8IzrRGnAO1c0nt6iNg2qjfA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/button": "2.2.15",
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-aria-button": "2.2.9",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/dialog": "3.5.21",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/overlays": "3.6.13",
+ "@react-types/button": "3.10.2",
+ "@react-types/overlays": "3.8.12"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/popover/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/progress": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/progress/-/progress-2.2.11.tgz",
+ "integrity": "sha512-y7GtCxqkCyZr4tifDOS4/XAxgBH6+PqKOmuPC9pHCRL7b8VFeKM9KFkxjS6n6VM/zdKqnmlysdHDFtIi3ppVBw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-is-mounted": "2.1.6",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/progress": "3.4.19",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/progress": "3.5.9"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/progress/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/radio": {
+ "version": "2.3.14",
+ "resolved": "https://registry.npmjs.org/@heroui/radio/-/radio-2.3.14.tgz",
+ "integrity": "sha512-k0KSH8Q7D8pacpu9pZxobH3MJ/zg8MGvDWj0J0TY9ZpUnetSTG+YBuwTtlRGDKz8IBmu7kZ3rlym8NU6wbir2g==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/form": "2.1.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/radio": "3.10.11",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/radio": "3.10.10",
+ "@react-types/radio": "3.8.6",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.3",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/radio/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/radio/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/react": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/@heroui/react/-/react-2.7.4.tgz",
+ "integrity": "sha512-w5RZxxx0DHxciLRsJ2v8oPeHIc/FH1sVuWUGM9Vjg5/0TZoZNYryDtsubMJLKAe3Yu010kxY9qOdR8GA3f25eg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/accordion": "2.2.12",
+ "@heroui/alert": "2.2.15",
+ "@heroui/autocomplete": "2.3.16",
+ "@heroui/avatar": "2.2.11",
+ "@heroui/badge": "2.2.10",
+ "@heroui/breadcrumbs": "2.2.11",
+ "@heroui/button": "2.2.15",
+ "@heroui/calendar": "2.2.15",
+ "@heroui/card": "2.2.14",
+ "@heroui/checkbox": "2.3.14",
+ "@heroui/chip": "2.2.11",
+ "@heroui/code": "2.2.11",
+ "@heroui/date-input": "2.3.14",
+ "@heroui/date-picker": "2.3.15",
+ "@heroui/divider": "2.2.10",
+ "@heroui/drawer": "2.2.12",
+ "@heroui/dropdown": "2.3.15",
+ "@heroui/form": "2.1.14",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/image": "2.2.10",
+ "@heroui/input": "2.4.15",
+ "@heroui/input-otp": "2.1.14",
+ "@heroui/kbd": "2.2.11",
+ "@heroui/link": "2.2.12",
+ "@heroui/listbox": "2.3.14",
+ "@heroui/menu": "2.2.14",
+ "@heroui/modal": "2.2.12",
+ "@heroui/navbar": "2.2.13",
+ "@heroui/number-input": "2.0.5",
+ "@heroui/pagination": "2.2.13",
+ "@heroui/popover": "2.3.15",
+ "@heroui/progress": "2.2.11",
+ "@heroui/radio": "2.3.14",
+ "@heroui/ripple": "2.2.12",
+ "@heroui/scroll-shadow": "2.3.10",
+ "@heroui/select": "2.4.15",
+ "@heroui/skeleton": "2.2.10",
+ "@heroui/slider": "2.4.12",
+ "@heroui/snippet": "2.2.16",
+ "@heroui/spacer": "2.2.11",
+ "@heroui/spinner": "2.2.12",
+ "@heroui/switch": "2.2.13",
+ "@heroui/system": "2.4.11",
+ "@heroui/table": "2.2.14",
+ "@heroui/tabs": "2.2.12",
+ "@heroui/theme": "2.4.11",
+ "@heroui/toast": "2.0.5",
+ "@heroui/tooltip": "2.2.12",
+ "@heroui/user": "2.2.11",
+ "@react-aria/visually-hidden": "3.8.19"
+ },
+ "peerDependencies": {
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/react-rsc-utils": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/react-rsc-utils/-/react-rsc-utils-2.1.6.tgz",
+ "integrity": "sha512-slBWi9g3HdnSNRhoedDhXFybaab5MveAeECzQoj4oJrIlmiezyeZWRKbWR8li2tiZtvBoEr0Xpu/A8hdni15dQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/react-utils": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/@heroui/react-utils/-/react-utils-2.1.8.tgz",
+ "integrity": "sha512-ET8sQaqfAWEviuZfatSYXBzyD0PpzuIK2YQkijla0TmF0sHJ3Yl4YQ6DYleWAaIJEWW1u0HgUPrdIjVGjWyKVg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-rsc-utils": "2.1.6",
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/react/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/ripple": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/ripple/-/ripple-2.2.12.tgz",
+ "integrity": "sha512-5hKlJfl05rtp/ABhmsJ/qqQjh9TgzyvBdeuvWf0K3PJVIMSp+LJly86mwlEzHEbbBwAJvdq9jxd3+R54ZMaQRw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/scroll-shadow": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@heroui/scroll-shadow/-/scroll-shadow-2.3.10.tgz",
+ "integrity": "sha512-l10qKwQLWxW0l94SNxh+z8UnzgWlhTmvNRezrjXZZFhv4EKgv8u1f/E0HsLTy/g8KgPU0ebGWQmbhdqfMyiqOg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-data-scroll-overflow": "2.2.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/select": {
+ "version": "2.4.15",
+ "resolved": "https://registry.npmjs.org/@heroui/select/-/select-2.4.15.tgz",
+ "integrity": "sha512-FSbZd+M9RR/CY1hLJJ9bMQ6FbKQQS+f4jC6GGv2NAqx5kJ1wB1XjGpRRTPyZm1eKKLHc347DszOZRlYAMTxRxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/form": "2.1.14",
+ "@heroui/listbox": "2.3.14",
+ "@heroui/popover": "2.3.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/scroll-shadow": "2.3.10",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/spinner": "2.2.12",
+ "@heroui/use-aria-button": "2.2.9",
+ "@heroui/use-aria-multiselect": "2.4.8",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/form": "3.0.12",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-types/shared": "3.27.0",
+ "@tanstack/react-virtual": "3.11.3"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/select/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/select/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/shared-icons": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/shared-icons/-/shared-icons-2.1.6.tgz",
+ "integrity": "sha512-4Gey+FJF4XBlMw5p9D2geOEAED8xCxuksurWKUz7eAoAivRRsZJf9wwUsKvNfrmboBUoytdxpUDbVgnckx/G8A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/shared-utils": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@heroui/shared-utils/-/shared-utils-2.1.7.tgz",
+ "integrity": "sha512-1nx7y41P+Bsca7nDC+QFajAoFhSRGvjKhdFeopMQNTvU95L42PD7B0ThjcOretvQD0Ye2TsAEQInwsSgZ6kK/g==",
+ "hasInstallScript": true,
+ "license": "MIT"
+ },
+ "node_modules/@heroui/skeleton": {
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/@heroui/skeleton/-/skeleton-2.2.10.tgz",
+ "integrity": "sha512-6nv+Efzi3DBrVCVTY1CC8InaiYdmztPjmw/ytjGEm1rJNpJCK9HOgKSUVuz6dncLsIsB77toMfE+2s53Yrq9Yg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/slider": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/@heroui/slider/-/slider-2.4.12.tgz",
+ "integrity": "sha512-ArIQ73SDKX3OL0Q8DX2aeGnPSnVaALtoYzlFthI3BzN1eQeWAPbf43Np29yMadBnuPhnBKBuSZkgGS0ziRtSsg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/tooltip": "2.2.12",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/slider": "3.7.15",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/slider": "3.6.1"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/slider/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/slider/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/snippet": {
+ "version": "2.2.16",
+ "resolved": "https://registry.npmjs.org/@heroui/snippet/-/snippet-2.2.16.tgz",
+ "integrity": "sha512-QbWkGto4sP8L0LX3ZEqi5+vxWBFnRvjt1/zELY/NEFZLgMxC2BsM9uHc3ivxNNWyaV6S50Ddnl3RaL11+ZUd1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/button": "2.2.15",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/tooltip": "2.2.12",
+ "@heroui/use-clipboard": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/utils": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/snippet/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/spacer": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/spacer/-/spacer-2.2.11.tgz",
+ "integrity": "sha512-NZp0pbZ8LJcR2XaslHPHFYJSKxStN+gO8eymNIQjumSRBU0cHpNcrxbNy/N3gBtCqOrrainbmSIXxpxifWeOyA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system-rsc": "2.3.10"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/spinner": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/spinner/-/spinner-2.2.12.tgz",
+ "integrity": "sha512-P7tbxZg3E2vd2+5cpvNme1b6ytAy+BWEh7Q3JHjiIMjR70+140HulChp0nRkxIizoKF+vY5qGwEz+rAvteJ9dg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/system": "2.4.11",
+ "@heroui/system-rsc": "2.3.10"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/switch": {
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/@heroui/switch/-/switch-2.2.13.tgz",
+ "integrity": "sha512-lPxGTZewHtSe2WfsMBoQajbr0NMfDSw8bHZWqpoe+ks+d8RCDNkULknkziFb5xzRQNfUoa6cnQ5P0vGqcG9YIw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/switch": "3.6.11",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/toggle": "3.8.1",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.3",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/switch/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/switch/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/system": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/@heroui/system/-/system-2.4.11.tgz",
+ "integrity": "sha512-6OI7bgRLaH8i8kATeB6WOOLkHoxxSbfRaJr21bb2w+tnHkeqqHU73pURUR+t8jlheP9iS/j1IqGrcjVB9HR6sg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/system-rsc": "2.3.10",
+ "@internationalized/date": "3.7.0",
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/utils": "3.10.5",
+ "@react-types/datepicker": "3.10.0"
+ },
+ "peerDependencies": {
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/system-rsc": {
+ "version": "2.3.10",
+ "resolved": "https://registry.npmjs.org/@heroui/system-rsc/-/system-rsc-2.3.10.tgz",
+ "integrity": "sha512-/feLXGslWl27fGfot3ePaku53+HMqMeKwuwvJ5z6xGl3W9Npxmy+rTIS31wd7lnVWfH1ARxyODJWcfEvp/5lgg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-types/shared": "3.27.0",
+ "clsx": "^1.2.1"
+ },
+ "peerDependencies": {
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/system-rsc/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@heroui/system/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/table": {
+ "version": "2.2.14",
+ "resolved": "https://registry.npmjs.org/@heroui/table/-/table-2.2.14.tgz",
+ "integrity": "sha512-679UslGs8/UXp8V6ONnZIIr278jR6oKSv1XVM7+70MX2wpAieXiBcFdTvycXGRcEKQIsy+6AV4EqwTh5qizoMg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/checkbox": "2.3.14",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/spacer": "2.2.11",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/table": "3.16.1",
+ "@react-aria/utils": "3.27.0",
+ "@react-aria/visually-hidden": "3.8.19",
+ "@react-stately/table": "3.13.1",
+ "@react-stately/virtualizer": "4.2.1",
+ "@react-types/grid": "3.2.11",
+ "@react-types/table": "3.10.4",
+ "@tanstack/react-virtual": "3.11.3"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/table/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/table/node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.19.tgz",
+ "integrity": "sha512-MZgCCyQ3sdG94J5iJz7I7Ai3IxoN0U5d/+EaUnA1mfK7jf2fSYQBqi6Eyp8sWUYzBTLw4giXB5h0RGAnWzk9hA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/tabs": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/tabs/-/tabs-2.2.12.tgz",
+ "integrity": "sha512-yMQCumzqbCS7YGfn/nmarBEWuNAqfGy6fz+bn3uUyHSkKc4CsiRUprzF5jJoHfOFGxQI+7XIBSOtQ262Pj5o1w==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-is-mounted": "2.1.6",
+ "@heroui/use-update-effect": "2.1.6",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/tabs": "3.9.9",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/tabs": "3.7.1",
+ "@react-types/shared": "3.27.0",
+ "@react-types/tabs": "3.3.12",
+ "scroll-into-view-if-needed": "3.0.10"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/tabs/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/theme": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.11.tgz",
+ "integrity": "sha512-sy17DlHCxi5qIILkVhzsez7ZsibWo5gBvZlBETp3yHtxrRwCEHqJ00x2frPAvDf2s/xiN79rtwMf5tlA4w3Ubw==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7",
+ "clsx": "^1.2.1",
+ "color": "^4.2.3",
+ "color2k": "^2.0.3",
+ "deepmerge": "4.3.1",
+ "flat": "^5.0.2",
+ "tailwind-merge": "2.5.4",
+ "tailwind-variants": "0.3.0"
+ },
+ "peerDependencies": {
+ "tailwindcss": ">=3.4.0"
+ }
+ },
+ "node_modules/@heroui/theme/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@heroui/theme/node_modules/tailwind-merge": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz",
+ "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/@heroui/theme/node_modules/tailwind-variants": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.0.tgz",
+ "integrity": "sha512-ho2k5kn+LB1fT5XdNS3Clb96zieWxbStE9wNLK7D0AV64kdZMaYzAKo0fWl6fXLPY99ffF9oBJnIj5escEl/8A==",
+ "license": "MIT",
+ "dependencies": {
+ "tailwind-merge": "^2.5.4"
+ },
+ "engines": {
+ "node": ">=16.x",
+ "pnpm": ">=7.x"
+ },
+ "peerDependencies": {
+ "tailwindcss": "*"
+ }
+ },
+ "node_modules/@heroui/toast": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@heroui/toast/-/toast-2.0.5.tgz",
+ "integrity": "sha512-EFSJ8F1K79q2Y9ssMQhuWWeNcDAT8HMdwsZxL3hV3MZl/ZsEFobmmCeH38gCTidm63CUiIL6zaT1/FI7F+QiKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-icons": "2.1.6",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/spinner": "2.2.12",
+ "@heroui/use-is-mobile": "2.2.7",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/toast": "3.0.0-beta.19",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/toast": "3.0.0-beta.7",
+ "@react-stately/utils": "3.10.5"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.10",
+ "@heroui/theme": ">=2.4.9",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/toast/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/tooltip": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/@heroui/tooltip/-/tooltip-2.2.12.tgz",
+ "integrity": "sha512-S9Hpc+DvK+E1lKEahBDGrIImzC99cVLjtB3bkv3u0zAhkEFfTs9JM5ti4wNWANG7i7G80MZemNRXHA0LQQmCtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/aria-utils": "2.2.12",
+ "@heroui/dom-animation": "2.1.6",
+ "@heroui/framer-utils": "2.1.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@heroui/use-safe-layout-effect": "2.1.6",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/tooltip": "3.7.11",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/tooltip": "3.5.1",
+ "@react-types/overlays": "3.8.12",
+ "@react-types/tooltip": "3.4.14"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/tooltip/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-accordion": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-aria-accordion/-/use-aria-accordion-2.2.7.tgz",
+ "integrity": "sha512-UPQoroLcvylGN3rSo3r350UPCl49Ava1HRjBsozqYHvTlG3PzW3ArnzDHwYjl3eh2dCtVyxDct+H4kokR2f0Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/button": "3.11.1",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/selection": "3.22.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/tree": "3.8.7",
+ "@react-types/accordion": "3.0.0-alpha.26",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-aria-accordion/node_modules/@react-aria/selection": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.22.0.tgz",
+ "integrity": "sha512-XFOrK525HX2eeWeLZcZscUAs5qsuC1ZxsInDXMjvLeAaUPtQNEhUKHj3psDAl6XDU4VV1IJo0qCmFTVqTTMZSg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/selection": "^3.19.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-accordion/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-button": {
+ "version": "2.2.9",
+ "resolved": "https://registry.npmjs.org/@heroui/use-aria-button/-/use-aria-button-2.2.9.tgz",
+ "integrity": "sha512-hKYT8M98XDtCygGvMdCut4LpQgIRl8tKjTp7rZklWLPu3NgX2BBw0Lu2c4cjjf0XBWXMA/bTIIX9fJsd2FD6OA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/button": "3.10.2",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-aria-button/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-link": {
+ "version": "2.2.10",
+ "resolved": "https://registry.npmjs.org/@heroui/use-aria-link/-/use-aria-link-2.2.10.tgz",
+ "integrity": "sha512-W8iNzUKMAWuZ7FrNxYS6HCGZLCZy91sgcMisE3GRrdXideqwb5F2OUfEY3FOYPoWDb2+AfhuKMsVfAdECS2nXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/link": "3.5.10",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-aria-link/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-modal-overlay": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/@heroui/use-aria-modal-overlay/-/use-aria-modal-overlay-2.2.8.tgz",
+ "integrity": "sha512-QYYF1zM+hZjUPnx40yDbhynH/d+2hEoYlduP6yhBXFiAk44mwm9hkJlLobQNZeiA+sobbN7EvguCYSoyYsOGYg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/overlays": "3.25.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/overlays": "3.6.13",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-aria-modal-overlay/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-multiselect": {
+ "version": "2.4.8",
+ "resolved": "https://registry.npmjs.org/@heroui/use-aria-multiselect/-/use-aria-multiselect-2.4.8.tgz",
+ "integrity": "sha512-pvwCLpmP8SL/38EJme794TCP9wR5iQPeIctZbgncXZxT9qmDONn/bO27XFWTbWJlSf3hJ38Nm0HmRgUcat6sUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/i18n": "3.12.5",
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/label": "3.7.14",
+ "@react-aria/listbox": "3.14.0",
+ "@react-aria/menu": "3.17.0",
+ "@react-aria/selection": "3.22.0",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/form": "3.1.1",
+ "@react-stately/list": "3.11.2",
+ "@react-stately/menu": "3.9.1",
+ "@react-types/button": "3.10.2",
+ "@react-types/overlays": "3.8.12",
+ "@react-types/select": "3.9.9",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-aria-multiselect/node_modules/@react-aria/selection": {
+ "version": "3.22.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.22.0.tgz",
+ "integrity": "sha512-XFOrK525HX2eeWeLZcZscUAs5qsuC1ZxsInDXMjvLeAaUPtQNEhUKHj3psDAl6XDU4VV1IJo0qCmFTVqTTMZSg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/selection": "^3.19.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-aria-multiselect/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-callback-ref": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-callback-ref/-/use-callback-ref-2.1.6.tgz",
+ "integrity": "sha512-icFp4WBWTZhypBcyu+5kir7nZLtvtQq7DDvGwkTtxsGnFHgGDc6sXXcOU6AcCdoGefmsiVp5c3D3lZ2pMlGHmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/use-safe-layout-effect": "2.1.6"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-clipboard": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-clipboard/-/use-clipboard-2.1.7.tgz",
+ "integrity": "sha512-Nt/ILhHovvYpoRjhqbbyz9sPI5xquvsSU/UuZ4qE8xFrsI8ukJo9znI1mW5eeNUlY9EOjz6HWdYU1B6QyLR3hg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-data-scroll-overflow": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-data-scroll-overflow/-/use-data-scroll-overflow-2.2.7.tgz",
+ "integrity": "sha512-+XPWShncxvPt+wSz5wXIP1GRws6mZs5QoHHG9n0agPL3eYiE0dHeEVYmfLQCopYhnnTA3HRcTkRKQ6pNR4oVQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-disclosure": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-disclosure/-/use-disclosure-2.2.7.tgz",
+ "integrity": "sha512-nSv2MgoWS/7MKpZGikIT1CID0VgomSs2XTCz2M011VD89v0A9k27qW68Qmf03oWyrl6UghC1XdgJe6IIsfq72A==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/use-callback-ref": "2.1.6",
+ "@react-aria/utils": "3.27.0",
+ "@react-stately/utils": "3.10.5"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-disclosure/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-draggable": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-draggable/-/use-draggable-2.1.7.tgz",
+ "integrity": "sha512-TMeUiDT1yw2sSIPardaB3/JIFD12VO1pV3N/Jf0WyiYQO82v2VoturQHbRTvjI+69SjqapucZSYM0X7henlOwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/interactions": "3.23.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-image": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-image/-/use-image-2.1.7.tgz",
+ "integrity": "sha512-Cno8oXNo/3YDCRnCwSuJYgdsZ7mujjVWSwlYaoYbi+rM5o9TjZYRPYHZacHMABlbY+Hew31ddYpOmyw4SrkIwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/use-safe-layout-effect": "2.1.6"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-intersection-observer": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-intersection-observer/-/use-intersection-observer-2.2.7.tgz",
+ "integrity": "sha512-efEAGWlfH9+DoShOSC/3dBmxtGDsVCwIQuV+ZBAROjbal/QOCOBKYOGpMcIBruOsAs8r4I9R432QhMxkDgOTWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/interactions": "3.23.0",
+ "@react-aria/ssr": "3.9.7",
+ "@react-aria/utils": "3.27.0",
+ "@react-types/shared": "3.27.0"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-intersection-observer/node_modules/@react-aria/ssr": {
+ "version": "3.9.7",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz",
+ "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-intersection-observer/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-is-mobile": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@heroui/use-is-mobile/-/use-is-mobile-2.2.7.tgz",
+ "integrity": "sha512-aaQjvATBb09c4UzkcCaeZLqv5Sz0gtA1n07LxW+LJd2ENEYEuxNOWyO7dIAHaaYb3znX1ZxGC1h4cYLcN59nPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-aria/ssr": "3.9.7"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-is-mobile/node_modules/@react-aria/ssr": {
+ "version": "3.9.7",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz",
+ "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@heroui/use-is-mounted": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-is-mounted/-/use-is-mounted-2.1.6.tgz",
+ "integrity": "sha512-dnTX1PUWGhIQJxszTScHgM9XxvYIx9j8vnSJuVGaptJonZWlt50yI/WAi+oWXJ289rw7XBDJ8o38qmU5Pmq+WA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-measure": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-measure/-/use-measure-2.1.6.tgz",
+ "integrity": "sha512-FiN3Za6hExqU1B0d2drCm9JUFneQ1W5gyNoX0owf3aIWG98QR+LR1MOL3WBAGWtDsp4K6q8rqUKXatNxGJd/sA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-pagination": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/@heroui/use-pagination/-/use-pagination-2.2.8.tgz",
+ "integrity": "sha512-SQNJli3A5uoTl9f5xKsgkXGG8YuNUtCaAfpb+f1xQ5/v0QEBuhttRH6njT76+wXIlijSK42b2bSzU3EKY2uGjg==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/i18n": "3.12.5"
+ },
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-safe-layout-effect": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-safe-layout-effect/-/use-safe-layout-effect-2.1.6.tgz",
+ "integrity": "sha512-yLT6zrlcZGJX4KKenzvR6lPS42Lf/Q0Q8ErpufLSkTdX4uk/ThGB/CRwdXfP+TPFLIfjXdsgCHgZr2ZAQJaG5Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-scroll-position": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-scroll-position/-/use-scroll-position-2.1.6.tgz",
+ "integrity": "sha512-9ap2AIuPjJCGLt7ZZAQqSE7s9Md1lUqnmxXf6UhKH0CJowhVHIl76gtV2rMeQZ+vsjbG3d4tsX2Vw13h+HLpuA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/use-update-effect": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@heroui/use-update-effect/-/use-update-effect-2.1.6.tgz",
+ "integrity": "sha512-nGSaIngKPuutmQcfZgnMHGYXJDqo6sPjdIIFjb5vutEnc827Xyh5f4q8hXfo7huYYYzA1CqLaThNVFCf3qIwHg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/user": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/@heroui/user/-/user-2.2.11.tgz",
+ "integrity": "sha512-1ZpVIr7yjdJEeFPWtvHmx0Q8WJSkKYnUd5iz0m9z8pTrrQtnfWavdwsGJ5Xw68Sjsr/ig5SEjro5Yn6l4COn8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@heroui/avatar": "2.2.11",
+ "@heroui/react-utils": "2.1.8",
+ "@heroui/shared-utils": "2.1.7",
+ "@react-aria/focus": "3.19.1",
+ "@react-aria/utils": "3.27.0"
+ },
+ "peerDependencies": {
+ "@heroui/system": ">=2.4.7",
+ "@heroui/theme": ">=2.4.6",
+ "react": ">=18 || >=19.0.0-rc.0",
+ "react-dom": ">=18 || >=19.0.0-rc.0"
+ }
+ },
+ "node_modules/@heroui/user/node_modules/@react-aria/utils": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.27.0.tgz",
+ "integrity": "sha512-p681OtApnKOdbeN8ITfnnYqfdHS0z7GE+4l8EXlfLnr70Rp/9xicBO6d2rU+V/B3JujDw2gPWxYKEnEeh0CGCw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@internationalized/date": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.7.0.tgz",
+ "integrity": "sha512-VJ5WS3fcVx0bejE/YHfbDKR/yawZgKqn/if+oEeLqNwBtPzVB06olkfcnojTmEMX+gTpH+FlQ69SHNitJ8/erQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@internationalized/message": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.8.tgz",
+ "integrity": "sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0",
+ "intl-messageformat": "^10.1.0"
+ }
+ },
+ "node_modules/@internationalized/number": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz",
+ "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@internationalized/string": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.7.tgz",
+ "integrity": "sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@react-aria/breadcrumbs": {
+ "version": "3.5.20",
+ "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.20.tgz",
+ "integrity": "sha512-xqVSSDPpQuUFpJyIXMQv8L7zumk5CeGX7qTzo4XRvqm5T9qnNAX4XpYEMdktnLrQRY/OemCBScbx7SEwr0B3Kg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/link": "^3.7.8",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/breadcrumbs": "^3.7.10",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/button": {
+ "version": "3.11.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.11.1.tgz",
+ "integrity": "sha512-NSs2HxHSSPSuYy5bN+PMJzsCNDVsbm1fZ/nrWM2WWWHTBrx9OqyrEXZVV9ebzQCN9q0nzhwpf6D42zHIivWtJA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/toolbar": "3.0.0-beta.12",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/toggle": "^3.8.1",
+ "@react-types/button": "^3.10.2",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/calendar": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.7.0.tgz",
+ "integrity": "sha512-9YUbgcox7cQgvZfQtL2BLLRsIuX4mJeclk9HkFoOsAu3RGO5HNsteah8FV54W8BMjm/bNRXIPUxtjTTP+1L6jg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/live-announcer": "^3.4.1",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/calendar": "^3.7.0",
+ "@react-types/button": "^3.10.2",
+ "@react-types/calendar": "^3.6.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/checkbox": {
+ "version": "3.15.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.15.1.tgz",
+ "integrity": "sha512-ETgsMDZ0IZzRXy/OVlGkazm8T+PcMHoTvsxp0c+U82c8iqdITA+VJ615eBPOQh6OkkYIIn4cRn/e+69RmGzXng==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/form": "^3.0.12",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/toggle": "^3.10.11",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/checkbox": "^3.6.11",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/toggle": "^3.8.1",
+ "@react-types/checkbox": "^3.9.1",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/combobox": {
+ "version": "3.11.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.11.1.tgz",
+ "integrity": "sha512-TTNbGhUuqxzPcJzd6hufOxuHzX0UARkw+0bl+TuCwNPQnqrcPf20EoOZvd3MHZwGq6GCP4QV+qo0uGx83RpUvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/listbox": "^3.14.0",
+ "@react-aria/live-announcer": "^3.4.1",
+ "@react-aria/menu": "^3.17.0",
+ "@react-aria/overlays": "^3.25.0",
+ "@react-aria/selection": "^3.22.0",
+ "@react-aria/textfield": "^3.16.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/combobox": "^3.10.2",
+ "@react-stately/form": "^3.1.1",
+ "@react-types/button": "^3.10.2",
+ "@react-types/combobox": "^3.13.2",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/datepicker": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.13.0.tgz",
+ "integrity": "sha512-TmJan65P3Vk7VDBNW5rH9Z25cAn0vk8TEtaP3boCs8wJFE+HbEuB8EqLxBFu47khtuKTEqDP3dTlUh2Vt/f7Xw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@internationalized/number": "^3.6.0",
+ "@internationalized/string": "^3.2.5",
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/form": "^3.0.12",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/spinbutton": "^3.6.11",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/datepicker": "^3.12.0",
+ "@react-stately/form": "^3.1.1",
+ "@react-types/button": "^3.10.2",
+ "@react-types/calendar": "^3.6.0",
+ "@react-types/datepicker": "^3.10.0",
+ "@react-types/dialog": "^3.5.15",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/dialog": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.21.tgz",
+ "integrity": "sha512-tBsn9swBhcptJ9QIm0+ur0PVR799N6qmGguva3rUdd+gfitknFScyT08d7AoMr9AbXYdJ+2R9XNSZ3H3uIWQMw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/overlays": "^3.25.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/dialog": "^3.5.15",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/focus": {
+ "version": "3.19.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.19.1.tgz",
+ "integrity": "sha512-bix9Bu1Ue7RPcYmjwcjhB14BMu2qzfJ3tMQLqDc9pweJA66nOw8DThy3IfVr8Z7j2PHktOLf9kcbiZpydKHqzg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/form": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.12.tgz",
+ "integrity": "sha512-8uvPYEd3GDyGt5NRJIzdWW1Ry5HLZq37vzRZKUW8alZ2upFMH3KJJG55L9GP59KiF6zBrYBebvI/YK1Ye1PE1g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/form": "^3.1.1",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid": {
+ "version": "3.14.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.14.5.tgz",
+ "integrity": "sha512-XHw6rgjlTqc85e3zjsWo3U0EVwjN5MOYtrolCKc/lc2ItNdcY3OlMhpsU9+6jHwg/U3VCSWkGvwAz9hg7krd8Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.21.2",
+ "@react-aria/i18n": "^3.12.13",
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/live-announcer": "^3.4.4",
+ "@react-aria/selection": "^3.26.0",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/grid": "^3.11.6",
+ "@react-stately/selection": "^3.20.6",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/grid": "^3.3.6",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@internationalized/date": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz",
+ "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-aria/focus": {
+ "version": "3.21.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz",
+ "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-aria/i18n": {
+ "version": "3.12.13",
+ "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.13.tgz",
+ "integrity": "sha512-YTM2BPg0v1RvmP8keHenJBmlx8FXUKsdYIEX7x6QWRd1hKlcDwphfjzvt0InX9wiLiPHsT5EoBTpuUk8SXc0Mg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.10.0",
+ "@internationalized/message": "^3.1.8",
+ "@internationalized/number": "^3.6.5",
+ "@internationalized/string": "^3.2.7",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-aria/interactions": {
+ "version": "3.25.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz",
+ "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-stately/collections": {
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-types/checkbox": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz",
+ "integrity": "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-types/grid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.6.tgz",
+ "integrity": "sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/grid/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/i18n": {
+ "version": "3.12.5",
+ "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.5.tgz",
+ "integrity": "sha512-ooeop2pTG94PuaHoN2OTk2hpkqVuoqgEYxRvnc1t7DVAtsskfhS/gVOTqyWGsxvwAvRi7m/CnDu6FYdeQ/bK5w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@internationalized/message": "^3.1.6",
+ "@internationalized/number": "^3.6.0",
+ "@internationalized/string": "^3.2.5",
+ "@react-aria/ssr": "^3.9.7",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/interactions": {
+ "version": "3.23.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.23.0.tgz",
+ "integrity": "sha512-0qR1atBIWrb7FzQ+Tmr3s8uH5mQdyRH78n0krYaG8tng9+u1JlSi8DGRSaC9ezKyNB84m7vHT207xnHXGeJ3Fg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.7",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/label": {
+ "version": "3.7.14",
+ "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.7.14.tgz",
+ "integrity": "sha512-EN1Md2YvcC4sMqBoggsGYUEGlTNqUfJZWzduSt29fbQp1rKU2KlybTe+TWxKq/r2fFd+4JsRXxMeJiwB3w2AQA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/landmark": {
+ "version": "3.0.0-beta.18",
+ "resolved": "https://registry.npmjs.org/@react-aria/landmark/-/landmark-3.0.0-beta.18.tgz",
+ "integrity": "sha512-jFtWL7TYZrKucWNDx6ppUkGSqS2itkjhyLo9MIFqEg2mi4Lc2EoUjI/Gw9xMT+IJgebTcdQeXJpPskspl3Pojg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0",
+ "use-sync-external-store": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/link": {
+ "version": "3.7.8",
+ "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.7.8.tgz",
+ "integrity": "sha512-oiXUPQLZmf9Q9Xehb/sG1QRxfo28NFKdh9w+unD12sHI6NdLMETl5MA4CYyTgI0dfMtTjtfrF68GCnWfc7JvXQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/link": "^3.5.10",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/listbox": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.14.0.tgz",
+ "integrity": "sha512-pyVbKavh8N8iyiwOx6I3JIcICvAzFXkKSFni1yarfgngJsJV3KSyOkzLomOfN9UhbjcV4sX61/fccwJuvlurlA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/selection": "^3.22.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/list": "^3.11.2",
+ "@react-types/listbox": "^3.5.4",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/live-announcer": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.4.tgz",
+ "integrity": "sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-aria/menu": {
+ "version": "3.17.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.17.0.tgz",
+ "integrity": "sha512-aiFvSv3G1YvPC0klJQ/9quB05xIDZzJ5Lt6/CykP0UwGK5i8GCqm6/cyFLwEXsS5ooUPxS3bqmdOsgdADSSgqg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/overlays": "^3.25.0",
+ "@react-aria/selection": "^3.22.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/menu": "^3.9.1",
+ "@react-stately/selection": "^3.19.0",
+ "@react-stately/tree": "^3.8.7",
+ "@react-types/button": "^3.10.2",
+ "@react-types/menu": "^3.9.14",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/numberfield": {
+ "version": "3.11.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.11.10.tgz",
+ "integrity": "sha512-bYbTfO9NbAKMFOfEGGs+lvlxk0I9L0lU3WD2PFQZWdaoBz9TCkL+vK0fJk1zsuKaVjeGsmHP9VesBPRmaP0MiA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/spinbutton": "^3.6.11",
+ "@react-aria/textfield": "^3.16.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/numberfield": "^3.9.9",
+ "@react-types/button": "^3.10.2",
+ "@react-types/numberfield": "^3.8.8",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/overlays": {
+ "version": "3.25.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.25.0.tgz",
+ "integrity": "sha512-UEqJJ4duowrD1JvwXpPZreBuK79pbyNjNxFUVpFSskpGEJe3oCWwsSDKz7P1O7xbx5OYp+rDiY8fk/sE5rkaKw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/ssr": "^3.9.7",
+ "@react-aria/utils": "^3.27.0",
+ "@react-aria/visually-hidden": "^3.8.19",
+ "@react-stately/overlays": "^3.6.13",
+ "@react-types/button": "^3.10.2",
+ "@react-types/overlays": "^3.8.12",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/progress": {
+ "version": "3.4.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.19.tgz",
+ "integrity": "sha512-5HHnBJHqEUuY+dYsjIZDYsENeKr49VCuxeaDZ0OSahbOlloIOB1baCo/6jLBv1O1rwrAzZ2gCCPcVGed/cjrcw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/progress": "^3.5.9",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/radio": {
+ "version": "3.10.11",
+ "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.10.11.tgz",
+ "integrity": "sha512-R150HsBFPr1jLMShI4aBM8heCa1k6h0KEvnFRfTAOBu+B9hMSZOPB+d6GQOwGPysNlbset90Kej8G15FGHjqiA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/form": "^3.0.12",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/radio": "^3.10.10",
+ "@react-types/radio": "^3.8.6",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/selection": {
+ "version": "3.26.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.26.0.tgz",
+ "integrity": "sha512-ZBH3EfWZ+RfhTj01dH8L17uT7iNbXWS8u77/fUpHgtrm0pwNVhx0TYVnLU1YpazQ/3WVpvWhmBB8sWwD1FlD/g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.21.2",
+ "@react-aria/i18n": "^3.12.13",
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/selection": "^3.20.6",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/selection/node_modules/@internationalized/date": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz",
+ "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-aria/selection/node_modules/@react-aria/focus": {
+ "version": "3.21.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz",
+ "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/selection/node_modules/@react-aria/i18n": {
+ "version": "3.12.13",
+ "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.13.tgz",
+ "integrity": "sha512-YTM2BPg0v1RvmP8keHenJBmlx8FXUKsdYIEX7x6QWRd1hKlcDwphfjzvt0InX9wiLiPHsT5EoBTpuUk8SXc0Mg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.10.0",
+ "@internationalized/message": "^3.1.8",
+ "@internationalized/number": "^3.6.5",
+ "@internationalized/string": "^3.2.7",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/selection/node_modules/@react-aria/interactions": {
+ "version": "3.25.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz",
+ "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/selection/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/slider": {
+ "version": "3.7.15",
+ "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.7.15.tgz",
+ "integrity": "sha512-v9tujsuvJYRX0vE/vMYBzTT9FXbzrLsjkOrouNq+UdBIr7wRjIWTHHM0j+khb2swyCWNTbdv6Ce316Zqx2qWFg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/slider": "^3.6.1",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/slider": "^3.7.8",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/spinbutton": {
+ "version": "3.6.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.19.tgz",
+ "integrity": "sha512-xOIXegDpts9t3RSHdIN0iYQpdts0FZ3LbpYJIYVvdEHo9OpDS+ElnDzCGtwZLguvZlwc5s1LAKuKopDUsAEMkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.13",
+ "@react-aria/live-announcer": "^3.4.4",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/button": "^3.14.1",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/spinbutton/node_modules/@internationalized/date": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.0.tgz",
+ "integrity": "sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-aria/spinbutton/node_modules/@react-aria/i18n": {
+ "version": "3.12.13",
+ "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.13.tgz",
+ "integrity": "sha512-YTM2BPg0v1RvmP8keHenJBmlx8FXUKsdYIEX7x6QWRd1hKlcDwphfjzvt0InX9wiLiPHsT5EoBTpuUk8SXc0Mg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.10.0",
+ "@internationalized/message": "^3.1.8",
+ "@internationalized/number": "^3.6.5",
+ "@internationalized/string": "^3.2.7",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/spinbutton/node_modules/@react-types/button": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.14.1.tgz",
+ "integrity": "sha512-D8C4IEwKB7zEtiWYVJ3WE/5HDcWlze9mLWQ5hfsBfpePyWCgO3bT/+wjb/7pJvcAocrkXo90QrMm85LcpBtrpg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/spinbutton/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/ssr": {
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/switch": {
+ "version": "3.6.11",
+ "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.11.tgz",
+ "integrity": "sha512-paYCpH+oeL+8rgQK+cBJ+IaZ1sXSh3+50WPlg2LvLBta0QVfQhPR4juPvfXRpfHHhCjFBgF4/RGbV8q5zpl3vA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/toggle": "^3.10.11",
+ "@react-stately/toggle": "^3.8.1",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/switch": "^3.5.8",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/table": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.16.1.tgz",
+ "integrity": "sha512-T28TIGnKnPBunyErDBmm5jUX7AyzT7NVWBo9pDSt9wUuEnz0rVNd7p9sjmP2+u7I645feGG9klcdpCvFeqrk8A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/grid": "^3.11.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/live-announcer": "^3.4.1",
+ "@react-aria/utils": "^3.27.0",
+ "@react-aria/visually-hidden": "^3.8.19",
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/flags": "^3.0.5",
+ "@react-stately/table": "^3.13.1",
+ "@react-types/checkbox": "^3.9.1",
+ "@react-types/grid": "^3.2.11",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/table": "^3.10.4",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/tabs": {
+ "version": "3.9.9",
+ "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.9.9.tgz",
+ "integrity": "sha512-oXPtANs16xu6MdMGLHjGV/2Zupvyp9CJEt7ORPLv5xAzSY5hSjuQHJLZ0te3Lh/KSG5/0o3RW/W5yEqo7pBQQQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/selection": "^3.22.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/tabs": "^3.7.1",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/tabs": "^3.3.12",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/textfield": {
+ "version": "3.16.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.16.0.tgz",
+ "integrity": "sha512-53RVpMeMDN/QoabqnYZ1lxTh1xTQ3IBYQARuayq5EGGMafyxoFHzttxUdSqkZGK/+zdSF2GfmjOYJVm2nDKuDQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/form": "^3.0.12",
+ "@react-aria/label": "^3.7.14",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/textfield": "^3.11.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toast": {
+ "version": "3.0.0-beta.19",
+ "resolved": "https://registry.npmjs.org/@react-aria/toast/-/toast-3.0.0-beta.19.tgz",
+ "integrity": "sha512-LCMTcmSmum5CzBk+DIec66q6pJGEl+InQPJdsby7QG/row0ka6wHPvul78HVseN7dzg6G3xVjvHtVPOixkuegA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/landmark": "3.0.0-beta.18",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/toast": "3.0.0-beta.7",
+ "@react-types/button": "^3.10.2",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle": {
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.12.2.tgz",
+ "integrity": "sha512-g25XLYqJuJpt0/YoYz2Rab8ax+hBfbssllcEFh0v0jiwfk2gwTWfRU9KAZUvxIqbV8Nm8EBmrYychDpDcvW1kw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/toggle": "^3.9.2",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle/node_modules/@react-aria/interactions": {
+ "version": "3.25.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz",
+ "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle/node_modules/@react-stately/toggle": {
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.9.2.tgz",
+ "integrity": "sha512-dOxs9wrVXHUmA7lc8l+N9NbTJMAaXcYsnNGsMwfXIXQ3rdq+IjWGNYJ52UmNQyRYFcg0jrzRrU16TyGbNjOdNQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle/node_modules/@react-stately/utils": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
+ "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle/node_modules/@react-types/checkbox": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz",
+ "integrity": "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toggle/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/toolbar": {
+ "version": "3.0.0-beta.12",
+ "resolved": "https://registry.npmjs.org/@react-aria/toolbar/-/toolbar-3.0.0-beta.12.tgz",
+ "integrity": "sha512-a+Be27BtM2lzEdTzm19FikPbitfW65g/JZln3kyAvgpswhU6Ljl8lztaVw4ixjG4H0nqnKvVggMy4AlWwDUaVQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/i18n": "^3.12.5",
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/tooltip": {
+ "version": "3.7.11",
+ "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.7.11.tgz",
+ "integrity": "sha512-mhZgAWUj7bUWipDeJXaVPZdqnzoBCd/uaEbdafnvgETmov1udVqPTh9w4ZKX2Oh1wa2+OdLFrBOk+8vC6QbWag==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.19.1",
+ "@react-aria/interactions": "^3.23.0",
+ "@react-aria/utils": "^3.27.0",
+ "@react-stately/tooltip": "^3.5.1",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/tooltip": "^3.4.14",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/utils": {
+ "version": "3.31.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.31.0.tgz",
+ "integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-stately/flags": "^3.1.2",
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0",
+ "clsx": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/utils/node_modules/@react-stately/utils": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
+ "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/utils/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/visually-hidden": {
+ "version": "3.8.28",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.28.tgz",
+ "integrity": "sha512-KRRjbVVob2CeBidF24dzufMxBveEUtUu7IM+hpdZKB+gxVROoh4XRLPv9SFmaH89Z7D9To3QoykVZoWD0lan6Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/interactions": "^3.25.6",
+ "@react-aria/utils": "^3.31.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/visually-hidden/node_modules/@react-aria/interactions": {
+ "version": "3.25.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz",
+ "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.31.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-aria/visually-hidden/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/calendar": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.7.0.tgz",
+ "integrity": "sha512-N15zKubP2S7eWfPSJjKVlmJA7YpWzrIGx52BFhwLSQAZcV+OPcMgvOs71WtB7PLwl6DUYQGsgc0B3tcHzzvdvQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/calendar": "^3.6.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/checkbox": {
+ "version": "3.6.11",
+ "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.6.11.tgz",
+ "integrity": "sha512-jApdBis+Q1sXLivg+f7krcVaP/AMMMiQcVqcz5gwxlweQN+dRZ/NpL0BYaDOuGc26Mp0lcuVaET3jIZeHwtyxA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/checkbox": "^3.9.1",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/collections": {
+ "version": "3.12.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.1.tgz",
+ "integrity": "sha512-8QmFBL7f+P64dEP4o35pYH61/lP0T/ziSdZAvNMrCqaM+fXcMfUp2yu1E63kADVX7WRDsFJWE3CVMeqirPH6Xg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/combobox": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.10.2.tgz",
+ "integrity": "sha512-uT642Dool4tQBh+8UQjlJnTisrJVtg3LqmiP/HqLQ4O3pW0O+ImbG+2r6c9dUzlAnH4kEfmEwCp9dxkBkmFWsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/list": "^3.11.2",
+ "@react-stately/overlays": "^3.6.13",
+ "@react-stately/select": "^3.6.10",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/combobox": "^3.13.2",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/datepicker": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.12.0.tgz",
+ "integrity": "sha512-AfJEP36d+QgQ30GfacXtYdGsJvqY2yuCJ+JrjHct+m1nYuTkMvMMnhwNBFasgDJPLCDyHzyANlWkl2kQGfsBFw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@internationalized/string": "^3.2.5",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/overlays": "^3.6.13",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/datepicker": "^3.10.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/flags": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
+ "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ }
+ },
+ "node_modules/@react-stately/form": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.1.1.tgz",
+ "integrity": "sha512-qavrz5X5Mdf/Q1v/QJRxc0F8UTNEyRCNSM1we/nnF7GV64+aYSDLOtaRGmzq+09RSwo1c8ZYnIkK5CnwsPhTsQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/grid": {
+ "version": "3.11.6",
+ "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.6.tgz",
+ "integrity": "sha512-vWPAkzpeTIsrurHfMubzMuqEw7vKzFhIJeEK5sEcLunyr1rlADwTzeWrHNbPMl66NAIAi70Dr1yNq+kahQyvMA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/selection": "^3.20.6",
+ "@react-types/grid": "^3.3.6",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/grid/node_modules/@react-stately/collections": {
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/grid/node_modules/@react-types/grid": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.6.tgz",
+ "integrity": "sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/grid/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/list": {
+ "version": "3.11.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.11.2.tgz",
+ "integrity": "sha512-eU2tY3aWj0SEeC7lH9AQoeAB4LL9mwS54FvTgHHoOgc1ZIwRJUaZoiuETyWQe98AL8KMgR1nrnDJ1I+CcT1Y7g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/selection": "^3.19.0",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/menu": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.9.1.tgz",
+ "integrity": "sha512-WRjGGImhQlQaer/hhahGytwd1BDq3fjpTkY/04wv3cQJPJR6lkVI5nSvGFMHfCaErsA1bNyB8/T9Y5F5u4u9ng==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/overlays": "^3.6.13",
+ "@react-types/menu": "^3.9.14",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/numberfield": {
+ "version": "3.9.9",
+ "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.9.9.tgz",
+ "integrity": "sha512-hZsLiGGHTHmffjFymbH1qVmA633rU2GNjMFQTuSsN4lqqaP8fgxngd5pPCoTCUFEkUgWjdHenw+ZFByw8lIE+g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/number": "^3.6.0",
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/numberfield": "^3.8.8",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/overlays": {
+ "version": "3.6.13",
+ "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.13.tgz",
+ "integrity": "sha512-WsU85Gf/b+HbWsnnYw7P/Ila3wD+C37Uk/WbU4/fHgJ26IEOWsPE6wlul8j54NZ1PnLNhV9Fn+Kffi+PaJMQXQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/overlays": "^3.8.12",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/radio": {
+ "version": "3.10.10",
+ "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.10.10.tgz",
+ "integrity": "sha512-9x3bpq87uV8iYA4NaioTTWjriQSlSdp+Huqlxll0T3W3okpyraTTejE91PbIoRTUmL5qByIh2WzxYmr4QdBgAA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/form": "^3.1.1",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/radio": "^3.8.6",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.8.0.tgz",
+ "integrity": "sha512-A721nlt0DSCDit0wKvhcrXFTG5Vv1qkEVkeKvobmETZy6piKvwh0aaN8iQno5AFuZaj1iOZeNjZ/20TsDJR/4A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/form": "^3.2.2",
+ "@react-stately/list": "^3.13.1",
+ "@react-stately/overlays": "^3.6.20",
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/select": "^3.11.0",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-stately/collections": {
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-stately/form": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.2.2.tgz",
+ "integrity": "sha512-soAheOd7oaTO6eNs6LXnfn0tTqvOoe3zN9FvtIhhrErKz9XPc5sUmh3QWwR45+zKbitOi1HOjfA/gifKhZcfWw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-stately/list": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.13.1.tgz",
+ "integrity": "sha512-eHaoauh21twbcl0kkwULhVJ+CzYcy1jUjMikNVMHOQdhr4WIBdExf7PmSgKHKqsSPhpGg6IpTCY2dUX3RycjDg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/selection": "^3.20.6",
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-stately/overlays": {
+ "version": "3.6.20",
+ "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.20.tgz",
+ "integrity": "sha512-YAIe+uI8GUXX8F/0Pzr53YeC5c/bjqbzDFlV8NKfdlCPa6+Jp4B/IlYVjIooBj9+94QvbQdjylegvYWK/iPwlg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/overlays": "^3.9.2",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-stately/utils": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
+ "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-types/overlays": {
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.9.2.tgz",
+ "integrity": "sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-types/select": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.11.0.tgz",
+ "integrity": "sha512-SzIsMFVPCbXE1Z1TLfpdfiwJ1xnIkcL1/CjGilmUKkNk5uT7rYX1xCJqWCjXI0vAU1xM4Qn+T3n8de4fw6HRBg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/select/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/selection": {
+ "version": "3.20.6",
+ "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.6.tgz",
+ "integrity": "sha512-a0bjuP2pJYPKEiedz2Us1W1aSz0iHRuyeQEdBOyL6Z6VUa6hIMq9H60kvseir2T85cOa4QggizuRV7mcO6bU5w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/utils": "^3.10.8",
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/selection/node_modules/@react-stately/collections": {
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/selection/node_modules/@react-stately/utils": {
+ "version": "3.10.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz",
+ "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/selection/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/slider": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.6.1.tgz",
+ "integrity": "sha512-8kij5O82Xe233vZZ6qNGqPXidnlNQiSnyF1q613c7ktFmzAyGjkIWVUapHi23T1fqm7H2Rs3RWlmwE9bo2KecA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/slider": "^3.7.8",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/table": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.13.1.tgz",
+ "integrity": "sha512-Im8W+F8o9EhglY5kqRa3xcMGXl8zBi6W5phGpAjXb+UGDL1tBIlAcYj733bw8g/ITCnaSz9ubsmON0HekPd6Jg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/flags": "^3.0.5",
+ "@react-stately/grid": "^3.10.1",
+ "@react-stately/selection": "^3.19.0",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/grid": "^3.2.11",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/table": "^3.10.4",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/tabs": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.7.1.tgz",
+ "integrity": "sha512-gr9ACyuWrYuc727h7WaHdmNw8yxVlUyQlguziR94MdeRtFGQnf3V6fNQG3kxyB77Ljko69tgDF7Nf6kfPUPAQQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/list": "^3.11.2",
+ "@react-types/shared": "^3.27.0",
+ "@react-types/tabs": "^3.3.12",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/toast": {
+ "version": "3.0.0-beta.7",
+ "resolved": "https://registry.npmjs.org/@react-stately/toast/-/toast-3.0.0-beta.7.tgz",
+ "integrity": "sha512-+KDkaOS5Y4ApOfiP0HHij4ySwAd1VM9/pI4rVTyHrzkp0R2Q0eBxZ74MpWMpVfJa2lSjb/qEm60tqJ3A+4R/cQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0",
+ "use-sync-external-store": "^1.2.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/toggle": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.1.tgz",
+ "integrity": "sha512-MVpe79ghVQiwLmVzIPhF/O/UJAUc9B+ZSylVTyJiEPi0cwhbkKGQv9thOF0ebkkRkace5lojASqUAYtSTZHQJA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/checkbox": "^3.9.1",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/tooltip": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.5.1.tgz",
+ "integrity": "sha512-0aI3U5kB7Cop9OCW9/Bag04zkivFSdUcQgy/TWL4JtpXidVWmOha8txI1WySawFSjZhH83KIyPc+wKm1msfLMQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/overlays": "^3.6.13",
+ "@react-types/tooltip": "^3.4.14",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/tree": {
+ "version": "3.8.7",
+ "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.8.7.tgz",
+ "integrity": "sha512-hpc3pyuXWeQV5ufQ02AeNQg/MYhnzZ4NOznlY5OOUoPzpLYiI3ZJubiY3Dot4jw5N/LR7CqvDLHmrHaJPmZlHg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-stately/collections": "^3.12.1",
+ "@react-stately/selection": "^3.19.0",
+ "@react-stately/utils": "^3.10.5",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/utils": {
+ "version": "3.10.5",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz",
+ "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-stately/virtualizer": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-4.2.1.tgz",
+ "integrity": "sha512-GHGEXV0ZRhq34U/P3LzkByCBfy2IDynYlV1SE4njkUWWGE/0AH56UegM6w2l3GeiNpXsXCgXl7jpAKeIGMEnrQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/utils": "^3.27.0",
+ "@react-types/shared": "^3.27.0",
+ "@swc/helpers": "^0.5.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
+ "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/accordion": {
+ "version": "3.0.0-alpha.26",
+ "resolved": "https://registry.npmjs.org/@react-types/accordion/-/accordion-3.0.0-alpha.26.tgz",
+ "integrity": "sha512-OXf/kXcD2vFlEnkcZy/GG+a/1xO9BN7Uh3/5/Ceuj9z2E/WwD55YwU3GFM5zzkZ4+DMkdowHnZX37XnmbyD3Mg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/breadcrumbs": {
+ "version": "3.7.10",
+ "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.7.10.tgz",
+ "integrity": "sha512-5HhRxkKHfAQBoyOYzyf4HT+24HgPE/C/QerxJLNNId303LXO03yeYrbvRqhYZSlD1ACLJW9OmpPpREcw5iSqgw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/link": "^3.5.10",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/button": {
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.10.2.tgz",
+ "integrity": "sha512-h8SB/BLoCgoBulCpyzaoZ+miKXrolK9XC48+n1dKJXT8g4gImrficurDW6+PRTQWaRai0Q0A6bu8UibZOU4syg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/calendar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.6.0.tgz",
+ "integrity": "sha512-BtFh4BFwvsYlsaSqUOVxlqXZSlJ6u4aozgO3PwHykhpemwidlzNwm9qDZhcMWPioNF/w2cU/6EqhvEKUHDnFZg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/checkbox": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.1.tgz",
+ "integrity": "sha512-0x/KQcipfNM9Nvy6UMwYG25roRLvsiqf0J3woTYylNNWzF+72XT0iI5FdJkE3w2wfa0obmSoeq4WcbFREQrH/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/combobox": {
+ "version": "3.13.2",
+ "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.13.2.tgz",
+ "integrity": "sha512-yl2yMcM5/v3lJiNZWjpAhQ9vRW6dD55CD4rYmO2K7XvzYJaFVT4WYI/AymPYD8RqomMp7coBmBHfHW0oupk8gg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/datepicker": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.10.0.tgz",
+ "integrity": "sha512-Att7y4NedNH1CogMDIX9URXgMLxGbZgnFCZ8oxgFAVndWzbh3TBcc4s7uoJDPvgRMAalq+z+SrlFFeoBeJmvvg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.7.0",
+ "@react-types/calendar": "^3.6.0",
+ "@react-types/overlays": "^3.8.12",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/dialog": {
+ "version": "3.5.22",
+ "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.22.tgz",
+ "integrity": "sha512-smSvzOcqKE196rWk0oqJDnz+ox5JM5+OT0PmmJXiUD4q7P5g32O6W5Bg7hMIFUI9clBtngo8kLaX2iMg+GqAzg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/overlays": "^3.9.2",
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/dialog/node_modules/@react-types/overlays": {
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.9.2.tgz",
+ "integrity": "sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/dialog/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/form": {
+ "version": "3.7.9",
+ "resolved": "https://registry.npmjs.org/@react-types/form/-/form-3.7.9.tgz",
+ "integrity": "sha512-+qGDrQFdIh8umU82zmnYJ0V2rLoGSQ3yApFT02URz//NWeTA7qo0Oab2veKvXUkcBb47oSvytZYmkExPikxIEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/grid": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.2.11.tgz",
+ "integrity": "sha512-Mww9nrasppvPbsBi+uUqFnf7ya8fXN0cTVzDNG+SveD8mhW+sbtuy+gPtEpnFD2Oyi8qLuObefzt4gdekJX2Yw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/link": {
+ "version": "3.5.10",
+ "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.5.10.tgz",
+ "integrity": "sha512-IM2mbSpB0qP44Jh1Iqpevo7bQdZAr0iDyDi13OhsiUYJeWgPMHzGEnQqdBMkrfQeOTXLtZtUyOYLXE2v39bhzQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/listbox": {
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.7.4.tgz",
+ "integrity": "sha512-p4YEpTl/VQGrqVE8GIfqTS5LkT5jtjDTbVeZgrkPnX/fiPhsfbTPiZ6g0FNap4+aOGJFGEEZUv2q4vx+rCORww==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/listbox/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/menu": {
+ "version": "3.9.14",
+ "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.9.14.tgz",
+ "integrity": "sha512-RJW/S8IPwbRuohJ/A9HJ7W8QaAY816tm7Nv6+H/TLXG76zu2AS5vEgq+0TcCAWvJJwUdLDpJWJMlo0iIoIBtcg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/overlays": "^3.8.12",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/numberfield": {
+ "version": "3.8.8",
+ "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.8.8.tgz",
+ "integrity": "sha512-825JPppxDaWh0Zxb0Q+wSslgRQYOtQPCAuhszPuWEy6d2F/M+hLR+qQqvQm9+LfMbdwiTg6QK5wxdWFCp2t7jw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/overlays": {
+ "version": "3.8.12",
+ "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.12.tgz",
+ "integrity": "sha512-ZvR1t0YV7/6j+6OD8VozKYjvsXT92+C/2LOIKozy7YUNS5KI4MkXbRZzJvkuRECVZOmx8JXKTUzhghWJM/3QuQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/progress": {
+ "version": "3.5.9",
+ "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.9.tgz",
+ "integrity": "sha512-zFxOzx3G8XUmHgpm037Hcayls5bqzXVa182E3iM7YWTmrjxJPKZ58XL0WWBgpTd+mJD7fTpnFdAZqSmFbtDOdA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/radio": {
+ "version": "3.8.6",
+ "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.8.6.tgz",
+ "integrity": "sha512-woTQYdRFjPzuml4qcIf+2zmycRuM5w3fDS5vk6CQmComVUjOFPtD28zX3Z9kc9lSNzaBQz9ONZfFqkZ1gqfICA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/select": {
+ "version": "3.9.9",
+ "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.9.tgz",
+ "integrity": "sha512-/hCd0o+ztn29FKCmVec+v7t4JpOzz56o+KrG7NDq2pcRWqUR9kNwCjrPhSbJIIEDm4ubtrfPu41ysIuDvRd2Bg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/shared": {
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.27.0.tgz",
+ "integrity": "sha512-gvznmLhi6JPEf0bsq7SwRYTHAKKq/wcmKqFez9sRdbED+SPMUmK5omfZ6w3EwUFQHbYUa4zPBYedQ7Knv70RMw==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/slider": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.8.2.tgz",
+ "integrity": "sha512-MQYZP76OEOYe7/yA2To+Dl0LNb0cKKnvh5JtvNvDnAvEprn1RuLiay8Oi/rTtXmc2KmBa4VdTcsXsmkbbkeN2Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/slider/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/switch": {
+ "version": "3.5.15",
+ "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.15.tgz",
+ "integrity": "sha512-r/ouGWQmIeHyYSP1e5luET+oiR7N7cLrAlWsrAfYRWHxqXOSNQloQnZJ3PLHrKFT02fsrQhx2rHaK2LfKeyN3A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.32.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/switch/node_modules/@react-types/shared": {
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/table": {
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.10.4.tgz",
+ "integrity": "sha512-d0tLz/whxVteqr1rophtuuxqyknHHfTKeXrCgDjt8pAyd9U8GPDbfcFSfYPUhWdELRt7aLVyQw6VblZHioVEgQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/grid": "^3.2.11",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/tabs": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.12.tgz",
+ "integrity": "sha512-E9O9G+wf9kaQ8UbDEDliW/oxYlJnh7oDCW1zaMOySwnG4yeCh7Wu02EOCvlQW4xvgn/i+lbEWgirf7L+yj5nRg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/textfield": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.11.0.tgz",
+ "integrity": "sha512-YORBgr6wlu2xfvr4MqjKFHGpj+z8LBzk14FbWDbYnnhGnv0I10pj+m2KeOHgDNFHrfkDdDOQmMIKn1UCqeUuEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@react-types/tooltip": {
+ "version": "3.4.14",
+ "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.14.tgz",
+ "integrity": "sha512-J7CeYL2yPeKIasx1rPaEefyCHGEx2DOCx+7bM3XcKGmCxvNdVQLjimNJOt8IHlUA0nFJQOjmSW/mz9P0f2/kUw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-types/overlays": "^3.8.12",
+ "@react-types/shared": "^3.27.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+ "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+ "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+ "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+ "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+ "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+ "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+ "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+ "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+ "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+ "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+ "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+ "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+ "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+ "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+ "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+ "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+ "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+ "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+ "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+ "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+ "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.17",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
+ "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.11",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.11.tgz",
+ "integrity": "sha512-f9z/nXhCgWDF4lHqgIE30jxLe4sYv15QodfdPDKYAk7nAEjNcndy4dHz3ezhdUaR23BpWa4I2EH4/DZ0//Uf8A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/query-devtools": {
+ "version": "5.91.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.91.1.tgz",
+ "integrity": "sha512-l8bxjk6BMsCaVQH6NzQEE/bEgFy1hAs5qbgXl0xhzezlaQbPk6Mgz9BqEg2vTLPOHD8N4k+w/gdgCbEzecGyNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.90.11",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.11.tgz",
+ "integrity": "sha512-3uyzz01D1fkTLXuxF3JfoJoHQMU2fxsfJwE+6N5hHy0dVNoZOvwKP8Z2k7k1KDeD54N20apcJnG75TBAStIrBA==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-core": "5.90.11"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tanstack/react-query-devtools": {
+ "version": "5.91.1",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.91.1.tgz",
+ "integrity": "sha512-tRnJYwEbH0kAOuToy8Ew7bJw1lX3AjkkgSlf/vzb+NpnqmHPdWM+lA2DSdGQSLi1SU0PDRrrCI1vnZnci96CsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/query-devtools": "5.91.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^5.90.10",
+ "react": "^18 || ^19"
+ }
+ },
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.11.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.3.tgz",
+ "integrity": "sha512-vCU+OTylXN3hdC8RKg68tPlBPjjxtzon7Ys46MgrSLE+JhSjSTPvoQifV6DQJeJmA8Q3KT6CphJbejupx85vFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.11.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.11.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz",
+ "integrity": "sha512-v2mrNSnMwnPJtcVqNvV0c5roGCBqeogN8jDtgtuHCphdwBasOZ17x8UV8qpHUh+u0MLfX43c0uUHKje0s+Zb0w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/lodash.debounce": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/lodash.debounce/-/lodash.debounce-4.0.9.tgz",
+ "integrity": "sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.11",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz",
+ "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^18.0.0"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.20",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
+ "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.23.3",
+ "caniuse-lite": "^1.0.30001646",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.8.32",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz",
+ "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
+ "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.8.25",
+ "caniuse-lite": "^1.0.30001754",
+ "electron-to-chromium": "^1.5.249",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.1.4"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001757",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
+ "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/color2k": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz",
+ "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==",
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/compute-scroll-into-view": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
+ "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "license": "MIT"
+ },
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "license": "MIT"
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.263",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.263.tgz",
+ "integrity": "sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "license": "MIT"
+ },
+ "node_modules/fast-equals": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.3.tgz",
+ "integrity": "sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "11.18.2",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
+ "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^11.18.1",
+ "motion-utils": "^11.18.1",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/input-otp": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.1.tgz",
+ "integrity": "sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/intl-messageformat": {
+ "version": "10.7.18",
+ "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz",
+ "integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.3.6",
+ "@formatjs/fast-memoize": "2.2.7",
+ "@formatjs/icu-messageformat-parser": "2.11.4",
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
+ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+ "license": "MIT"
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "11.18.1",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
+ "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^11.18.1"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "11.18.1",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
+ "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next-themes": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
+ "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
+ "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+ "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "license": "MIT"
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.10.0.tgz",
+ "integrity": "sha512-FVyCOH4IZ0eDDRycODfUqoN8ZSR2LbTvtx6RPsBgzvJ8xAXlMZNCrOFpu+jb8QbtZnpAd/cEki2pwE848pNGxw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.10.0.tgz",
+ "integrity": "sha512-Q4haR150pN/5N75O30iIsRJcr3ef7p7opFaKpcaREy0GQit6uCRu1NEiIFIwnHJQy0bsziRFBweR/5EkmHgVUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.10.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-smooth": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
+ "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-textarea-autosize": {
+ "version": "8.5.9",
+ "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz",
+ "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.20.13",
+ "use-composed-ref": "^1.3.0",
+ "use-latest": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/recharts": {
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz",
+ "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^18.3.1",
+ "react-smooth": "^4.0.4",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "license": "MIT",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.53.3",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+ "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.53.3",
+ "@rollup/rollup-android-arm64": "4.53.3",
+ "@rollup/rollup-darwin-arm64": "4.53.3",
+ "@rollup/rollup-darwin-x64": "4.53.3",
+ "@rollup/rollup-freebsd-arm64": "4.53.3",
+ "@rollup/rollup-freebsd-x64": "4.53.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+ "@rollup/rollup-linux-arm64-musl": "4.53.3",
+ "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
+ "@rollup/rollup-linux-x64-musl": "4.53.3",
+ "@rollup/rollup-openharmony-arm64": "4.53.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+ "@rollup/rollup-win32-x64-gnu": "4.53.3",
+ "@rollup/rollup-win32-x64-msvc": "4.53.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/scroll-into-view-if-needed": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.10.tgz",
+ "integrity": "sha512-t44QCeDKAPf1mtQH3fYpWz8IM/DyvHLjs8wUvvwMYxk5moOqCzrMSxK6HQVD0QVmVjXFavoFIPRVrMuJPKAvtg==",
+ "license": "MIT",
+ "dependencies": {
+ "compute-scroll-into-view": "^3.0.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
+ "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/sonner": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz",
+ "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.1",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+ "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "tinyglobby": "^0.2.11",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwind-variants": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.2.1.tgz",
+ "integrity": "sha512-2xmhAf4UIc3PijOUcJPA1LP4AbxhpcHuHM2C26xM0k81r0maAO6uoUSHl3APmvHZcY5cZCY/bYuJdfFa4eGoaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tailwind-merge": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=16.x",
+ "pnpm": ">=7.x"
+ },
+ "peerDependencies": {
+ "tailwindcss": "*"
+ }
+ },
+ "node_modules/tailwind-variants/node_modules/tailwind-merge": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
+ "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.13",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz",
+ "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.0",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.0",
+ "lilconfig": "^2.1.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.23",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.1",
+ "postcss-nested": "^6.0.1",
+ "postcss-selector-parser": "^6.0.11",
+ "resolve": "^1.22.2",
+ "sucrase": "^3.32.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
+ "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/use-composed-ref": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz",
+ "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-isomorphic-layout-effect": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
+ "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-latest": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz",
+ "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "use-isomorphic-layout-effect": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "license": "MIT AND ISC",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
+ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vite/node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ }
+ }
+}
diff --git a/apps/frontend/package.json b/apps/frontend/package.json
new file mode 100644
index 00000000..03b28f38
--- /dev/null
+++ b/apps/frontend/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "@playwright-reports/frontend",
+ "version": "5.7.4",
+ "description": "Playwright Reports Server Frontend",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "build:vite": "vite build",
+ "preview": "vite preview",
+ "typecheck": "tsc --noEmit",
+ "test": "echo 'No tests configured for frontend'",
+ "lint": "biome check src/",
+ "lint:fix": "biome check src/ --write",
+ "format": "biome format src/ --write",
+ "format:check": "biome format src/",
+ "clean": "rm -rf dist"
+ },
+ "dependencies": {
+ "@heroui/link": "2.2.12",
+ "@heroui/navbar": "2.2.13",
+ "@heroui/react": "2.7.4",
+ "@heroui/switch": "2.2.13",
+ "@heroui/system": "2.4.11",
+ "@heroui/theme": "2.4.11",
+ "@playwright-reports/shared": "*",
+ "@react-aria/selection": "^3.12.1",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.15.0",
+ "@react-aria/visually-hidden": "^3.8.28",
+ "@tanstack/react-query": "^5.59.15",
+ "@tanstack/react-query-devtools": "^5.59.15",
+ "clsx": "^2.1.1",
+ "framer-motion": "^11.11.8",
+ "next-themes": "^0.4.6",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^7.0.2",
+ "recharts": "^2.13.0",
+ "sonner": "^1.5.0",
+ "tailwind-merge": "^3.0.2",
+ "uuid": "^13.0.0"
+ },
+ "devDependencies": {
+ "@types/react": "18.3.11",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.4",
+ "autoprefixer": "10.4.20",
+ "postcss": "8.4.47",
+ "tailwind-variants": "0.2.1",
+ "tailwindcss": "3.4.13",
+ "typescript": "5.2.2",
+ "vite": "^6.0.3"
+ }
+}
diff --git a/postcss.config.js b/apps/frontend/postcss.config.cjs
similarity index 100%
rename from postcss.config.js
rename to apps/frontend/postcss.config.cjs
diff --git a/apps/frontend/src/App.tsx b/apps/frontend/src/App.tsx
new file mode 100644
index 00000000..626e17d9
--- /dev/null
+++ b/apps/frontend/src/App.tsx
@@ -0,0 +1,32 @@
+import { Route, Routes } from 'react-router-dom';
+import { Toaster } from 'sonner';
+import { Layout } from './components/Layout';
+import HomePage from './pages/HomePage';
+import LoginPage from './pages/LoginPage';
+import ReportDetailPage from './pages/ReportDetailPage';
+import ReportsPage from './pages/ReportsPage';
+import ResultsPage from './pages/ResultsPage';
+import SettingsPage from './pages/SettingsPage';
+import TrendsPage from './pages/TrendsPage';
+import { Providers } from './providers';
+
+function App() {
+ return (
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+ );
+}
+
+export default App;
diff --git a/apps/frontend/src/components/Layout.tsx b/apps/frontend/src/components/Layout.tsx
new file mode 100644
index 00000000..5f82c623
--- /dev/null
+++ b/apps/frontend/src/components/Layout.tsx
@@ -0,0 +1,33 @@
+import type { ReactNode } from 'react';
+import { Aside } from './aside';
+import { Navbar } from './navbar';
+
+interface LayoutProps {
+ children: ReactNode;
+}
+
+export function Layout({ children }: LayoutProps) {
+ return (
+
+ );
+}
diff --git a/app/components/aside.tsx b/apps/frontend/src/components/aside.tsx
similarity index 80%
rename from app/components/aside.tsx
rename to apps/frontend/src/components/aside.tsx
index d2a7309f..8b1c312b 100644
--- a/app/components/aside.tsx
+++ b/apps/frontend/src/components/aside.tsx
@@ -1,15 +1,12 @@
'use client';
-import { Card, CardBody, Link, Badge } from '@heroui/react';
-import NextLink from 'next/link';
-import { usePathname } from 'next/navigation';
-import { useSession } from 'next-auth/react';
-
+import { Badge, Card, CardBody, Link } from '@heroui/react';
+import { Link as RouterLink, useLocation } from 'react-router-dom';
+import { siteConfig } from '../config/site';
+import { useAuth } from '../hooks/useAuth';
import { useAuthConfig } from '../hooks/useAuthConfig';
-
-import { ReportIcon, ResultIcon, SettingsIcon, TrendIcon } from '@/app/components/icons';
-import { siteConfig } from '@/app/config/site';
-import useQuery from '@/app/hooks/useQuery';
+import useQuery from '../hooks/useQuery';
+import { ReportIcon, ResultIcon, SettingsIcon, TrendIcon } from './icons';
interface ServerInfo {
numOfReports: number;
@@ -24,18 +21,18 @@ const iconst = [
];
export const Aside: React.FC = () => {
- const pathname = usePathname();
- const session = useSession();
+ const location = useLocation();
+ const pathname = location.pathname;
+ const session = useAuth();
const { authRequired } = useAuthConfig();
+ const isAuthenticated = authRequired === false || session.status === 'authenticated';
const { data: serverInfo } = useQuery('/api/info', {
- dependencies: [],
+ enabled: isAuthenticated,
});
- const isAuthenticated = authRequired === false || session.status === 'authenticated';
-
return (
-
+
{siteConfig.navItems.map((item) => {
@@ -51,13 +48,13 @@ export const Aside: React.FC = () => {
return (
diff --git a/app/components/date-format.tsx b/apps/frontend/src/components/date-format.tsx
similarity index 70%
rename from app/components/date-format.tsx
rename to apps/frontend/src/components/date-format.tsx
index c6b54a8b..57055c61 100644
--- a/app/components/date-format.tsx
+++ b/apps/frontend/src/components/date-format.tsx
@@ -1,11 +1,11 @@
'use client';
-import { useState, useEffect } from 'react';
+import { useEffect, useState } from 'react';
/**
* Specific method for date formatting on the client
* as server locale and client locale may not match
*/
-export default function FormattedDate({ date }: { date: Date }) {
+export default function FormattedDate({ date }: Readonly<{ date: Date | string }>) {
const [formattedDate, setFormattedDate] = useState('');
useEffect(() => {
diff --git a/app/components/delete-report-button.tsx b/apps/frontend/src/components/delete-report-button.tsx
similarity index 84%
rename from app/components/delete-report-button.tsx
rename to apps/frontend/src/components/delete-report-button.tsx
index 6657166a..c7199a11 100644
--- a/app/components/delete-report-button.tsx
+++ b/apps/frontend/src/components/delete-report-button.tsx
@@ -1,12 +1,20 @@
'use client';
-import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure, Button } from '@heroui/react';
+import {
+ Button,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ useDisclosure,
+} from '@heroui/react';
import { useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
-import useMutation from '@/app/hooks/useMutation';
-import { DeleteIcon } from '@/app/components/icons';
-import { invalidateCache } from '@/app/lib/query-cache';
+import useMutation from '../hooks/useMutation';
+import { invalidateCache } from '../lib/query-cache';
+import { DeleteIcon } from './icons';
interface DeleteProjectButtonProps {
reportId: string;
@@ -22,7 +30,10 @@ export default function DeleteReportButton({ reportId, onDeleted }: DeleteProjec
} = useMutation('/api/report/delete', {
method: 'DELETE',
onSuccess: () => {
- invalidateCache(queryClient, { queryKeys: ['/api/info'], predicate: '/api/report' });
+ invalidateCache(queryClient, {
+ queryKeys: ['/api/info'],
+ predicate: '/api/report',
+ });
toast.success(`report "${reportId}" deleted`);
},
});
diff --git a/app/components/delete-results-button.tsx b/apps/frontend/src/components/delete-results-button.tsx
similarity index 80%
rename from app/components/delete-results-button.tsx
rename to apps/frontend/src/components/delete-results-button.tsx
index 3ed0ce85..1264641f 100644
--- a/app/components/delete-results-button.tsx
+++ b/apps/frontend/src/components/delete-results-button.tsx
@@ -1,12 +1,20 @@
'use client';
-import { Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, useDisclosure, Button } from '@heroui/react';
+import {
+ Button,
+ Modal,
+ ModalBody,
+ ModalContent,
+ ModalFooter,
+ ModalHeader,
+ useDisclosure,
+} from '@heroui/react';
import { useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
-import useMutation from '@/app/hooks/useMutation';
-import { invalidateCache } from '@/app/lib/query-cache';
-import { DeleteIcon } from '@/app/components/icons';
+import useMutation from '../hooks/useMutation';
+import { invalidateCache } from '../lib/query-cache';
+import { DeleteIcon } from './icons';
interface DeleteProjectButtonProps {
resultIds: string[];
@@ -14,7 +22,11 @@ interface DeleteProjectButtonProps {
label?: string;
}
-export default function DeleteResultsButton({ resultIds, onDeletedResult, label }: Readonly
) {
+export default function DeleteResultsButton({
+ resultIds,
+ onDeletedResult,
+ label,
+}: Readonly) {
const queryClient = useQueryClient();
const {
mutate: deleteResult,
@@ -23,7 +35,10 @@ export default function DeleteResultsButton({ resultIds, onDeletedResult, label
} = useMutation('/api/result/delete', {
method: 'DELETE',
onSuccess: () => {
- invalidateCache(queryClient, { queryKeys: ['/api/info'], predicate: '/api/result' });
+ invalidateCache(queryClient, {
+ queryKeys: ['/api/info'],
+ predicate: '/api/result',
+ });
toast.success(`result${resultIds.length ? '' : 's'} ${resultIds ?? 'are'} deleted`);
},
});
diff --git a/app/components/generate-report-button.tsx b/apps/frontend/src/components/generate-report-button.tsx
similarity index 64%
rename from app/components/generate-report-button.tsx
rename to apps/frontend/src/components/generate-report-button.tsx
index f93e4219..ce98a2a6 100644
--- a/app/components/generate-report-button.tsx
+++ b/apps/frontend/src/components/generate-report-button.tsx
@@ -1,26 +1,22 @@
-'use client';
-
import {
+ Autocomplete,
+ AutocompleteItem,
Button,
+ Input,
Modal,
+ ModalBody,
ModalContent,
+ ModalFooter,
ModalHeader,
- ModalBody,
useDisclosure,
- ModalFooter,
- Autocomplete,
- AutocompleteItem,
- Input,
} from '@heroui/react';
-import { useEffect, useState } from 'react';
+import type { Result } from '@playwright-reports/shared';
import { useQueryClient } from '@tanstack/react-query';
+import { useEffect, useState } from 'react';
import { toast } from 'sonner';
-
-import { type Result } from '../lib/storage';
-
-import useQuery from '@/app/hooks/useQuery';
-import useMutation from '@/app/hooks/useMutation';
-import { invalidateCache } from '@/app/lib/query-cache';
+import useMutation from '../hooks/useMutation';
+import useQuery from '../hooks/useQuery';
+import { invalidateCache } from '../lib/query-cache';
interface DeleteProjectButtonProps {
results: Result[];
@@ -39,7 +35,14 @@ export default function GenerateReportButton({
const { mutate: generateReport, isPending } = useMutation('/api/report/generate', {
method: 'POST',
onSuccess: (data: { reportId: string }) => {
- invalidateCache(queryClient, { queryKeys: ['/api/info'], predicate: '/api/report' });
+ invalidateCache(queryClient, {
+ queryKeys: ['/api/info'],
+ predicate: '/api/report',
+ });
+ invalidateCache(queryClient, {
+ queryKeys: ['/api/info'],
+ predicate: '/api/result',
+ });
toast.success(`report ${data?.reportId} is generated`);
setProjectName('');
setCustomName('');
@@ -48,7 +51,20 @@ export default function GenerateReportButton({
onGeneratedReport?.();
},
onError: (err: Error) => {
- setGenerationError(err.message);
+ let errorMessage = err.message;
+ if (
+ err.message.includes('ENOENT') ||
+ err.message.includes('not found') ||
+ err.message.includes('404')
+ ) {
+ errorMessage =
+ 'One or more selected results were not found. Please refresh the page and try again.';
+ } else if (err.message.includes('ResultID not found')) {
+ errorMessage =
+ 'Some selected results no longer exist. Please refresh the page and select valid results.';
+ }
+
+ setGenerationError(errorMessage);
},
});
@@ -63,7 +79,7 @@ export default function GenerateReportButton({
useEffect(() => {
!projectName && setProjectName(projects?.at(0) ?? '');
- }, [projects]);
+ }, [projects, projectName]);
const { isOpen, onOpen, onClose, onOpenChange } = useDisclosure();
@@ -78,12 +94,28 @@ export default function GenerateReportButton({
}
setGenerationError(null);
- generateReport({ body: { resultsIds: results.map((r) => r.resultID), project: projectName, title: customName } });
+
+ const validResults = results.filter((r) => r.resultID && r.resultID.trim() !== '');
+ if (validResults.length !== results.length) {
+ setGenerationError('Some selected results are invalid or missing IDs');
+ return;
+ }
+
+ const resultIds = validResults.map((r) => r.resultID);
+ generateReport({
+ body: { resultsIds: resultIds, project: projectName, title: customName },
+ });
};
return (
<>
-
+
Generate Report
@@ -107,16 +139,20 @@ export default function GenerateReportButton({
inputValue={projectName}
isDisabled={isPending}
isLoading={isResultProjectsLoading}
- items={Array.from(new Set([...projects, ...(resultProjects ?? [])])).map((project) => ({
- label: project,
- value: project,
- }))}
+ items={Array.from(new Set([...projects, ...(resultProjects ?? [])])).map(
+ (project) => ({
+ label: project,
+ value: project,
+ })
+ )}
label="Project name"
labelPlacement="outside"
placeholder="leave empty if not required"
variant="bordered"
onInputChange={(value) => setProjectName(value)}
- onSelectionChange={(value) => value && setProjectName(value?.toString() ?? '')}
+ onSelectionChange={(value) =>
+ value && setProjectName(value?.toString() ?? '')
+ }
>
{(item) => {item.label} }
@@ -129,7 +165,9 @@ export default function GenerateReportButton({
placeholder="leave empty if not required"
value={customName}
variant="bordered"
- onChange={(e: { target: { value: any } }) => setCustomName(e.target.value ?? '')}
+ onChange={(e: React.ChangeEvent) =>
+ setCustomName(e.target.value ?? '')
+ }
onClear={() => setCustomName('')}
/>
>
@@ -140,7 +178,12 @@ export default function GenerateReportButton({
Close
{!generationError && (
-
+
Generate
)}
diff --git a/app/components/header-links.tsx b/apps/frontend/src/components/header-links.tsx
similarity index 81%
rename from app/components/header-links.tsx
rename to apps/frontend/src/components/header-links.tsx
index 382a1cd0..b7df1233 100644
--- a/app/components/header-links.tsx
+++ b/apps/frontend/src/components/header-links.tsx
@@ -1,7 +1,14 @@
import { Link } from '@heroui/link';
-
-import { GithubIcon, DiscordIcon, TelegramIcon, LinkIcon, BitbucketIcon, CyborgTestIcon } from '@/app/components/icons';
-import { SiteWhiteLabelConfig } from '@/app/types';
+import type { SiteWhiteLabelConfig } from '@playwright-reports/shared';
+import {
+ BitbucketIcon,
+ CyborgTestIcon,
+ DiscordIcon,
+ GithubIcon,
+ LinkIcon,
+ SlackIcon,
+ TelegramIcon,
+} from './icons';
interface HeaderLinksProps {
config: SiteWhiteLabelConfig;
@@ -17,6 +24,7 @@ export const HeaderLinks: React.FC = ({ config, withTitle = fa
{ name: 'github', Icon: GithubIcon, title: 'GitHub' },
{ name: 'cyborgTest', Icon: CyborgTestIcon, title: 'Cyborg Test' },
{ name: 'bitbucket', Icon: BitbucketIcon, title: 'Bitbucket' },
+ { name: 'slack', Icon: SlackIcon, title: 'Slack' },
];
const socialLinks = Object.entries(links).map(([name, href]) => {
diff --git a/app/components/icons.tsx b/apps/frontend/src/components/icons.tsx
similarity index 95%
rename from app/components/icons.tsx
rename to apps/frontend/src/components/icons.tsx
index fe8af021..5cfdda38 100644
--- a/app/components/icons.tsx
+++ b/apps/frontend/src/components/icons.tsx
@@ -1,6 +1,5 @@
-import { FC } from 'react';
-
-import { IconSvgProps } from '@/app/types';
+import type { IconSvgProps } from '@playwright-reports/shared';
+import type { FC } from 'react';
export const DiscordIcon: FC = ({ size = 40, width, height, ...props }) => {
return (
@@ -26,6 +25,23 @@ export const GithubIcon: FC = ({ size = 40, width, height, ...prop
);
};
+export const SlackIcon: FC = ({ size = 40, width, height, ...props }) => (
+
+
+
+
+
+
+);
+
export const BitbucketIcon: FC = ({ size = 40, width, height, ...props }) => {
return (
= ({ size = 40, width, height, ...p
export const CyborgTestIcon: FC = ({ size = 40, width, height, ...props }) => {
return (
-
+
@@ -168,7 +190,7 @@ export const ResultIcon: FC = ({ size = 40, width, height, ...prop
export const TrendIcon: FC = ({ size = 40, width, height, ...props }) => {
return (
= ({ size = 40, width, height, ...props
};
export const DeleteIcon: FC = (props) => (
-
+
= (props) => (
);
-export const EyeIcon: FC = (props) => (
-
-
-
-
-);
-
export const SearchIcon: FC = (props) => (
= (props) => (
strokeLinejoin="round"
strokeWidth="2"
/>
-
+
);
@@ -300,15 +317,15 @@ export const SettingsIcon: FC = ({ size = 24, width, height, ...pr
return (
= ({ stats }) => {
+ if (!stats.total) return null;
+
+ const passedPercentage = ((stats.expected || 0) / (stats.total - (stats.skipped || 0))) * 100;
+
+ return (
+
+ );
+};
+
+export default InlineStatsCircle;
diff --git a/app/components/jira-ticket-modal.tsx b/apps/frontend/src/components/jira-ticket-modal.tsx
similarity index 78%
rename from app/components/jira-ticket-modal.tsx
rename to apps/frontend/src/components/jira-ticket-modal.tsx
index 1e38361e..c3c19ff5 100644
--- a/app/components/jira-ticket-modal.tsx
+++ b/apps/frontend/src/components/jira-ticket-modal.tsx
@@ -1,22 +1,21 @@
'use client';
-import { useState, useEffect } from 'react';
import {
+ Button,
+ Input,
Modal,
- ModalContent,
- ModalHeader,
ModalBody,
+ ModalContent,
ModalFooter,
- Button,
- Input,
- Textarea,
+ ModalHeader,
Select,
SelectItem,
+ Textarea,
} from '@heroui/react';
+import type { JiraApiResponse, ReportTest } from '@playwright-reports/shared';
+import { useEffect, useState } from 'react';
import { toast } from 'sonner';
-import { type ReportTest } from '@/app/lib/parser';
-
interface JiraTicketModalProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
@@ -31,7 +30,12 @@ interface JiraTicketData {
projectKey: string;
}
-export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }: JiraTicketModalProps) {
+export default function JiraTicketModal({
+ isOpen,
+ onOpenChange,
+ test,
+ reportId,
+}: JiraTicketModalProps) {
const [ticketData, setTicketData] = useState({
summary: '',
description: '',
@@ -39,7 +43,7 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
projectKey: '',
});
const [isSubmitting, setIsSubmitting] = useState(false);
- const [jiraConfig, setJiraConfig] = useState(null);
+ const [jiraConfig, setJiraConfig] = useState(null);
const [isLoadingConfig, setIsLoadingConfig] = useState(true);
useEffect(() => {
@@ -51,7 +55,10 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
setJiraConfig(config);
if (config.configured && config.defaultProjectKey && !ticketData.projectKey) {
- setTicketData((prev) => ({ ...prev, projectKey: config.defaultProjectKey }));
+ setTicketData((prev) => ({
+ ...prev,
+ projectKey: config.defaultProjectKey,
+ }));
}
if (config.configured) {
@@ -75,7 +82,7 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
if (isOpen) {
loadJiraConfig();
}
- }, [isOpen]);
+ }, [isOpen, ticketData.issueType, ticketData.projectKey]);
const handleSubmit = async () => {
if (!test) return;
@@ -84,7 +91,7 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
try {
const testAttachments =
- test.results?.[0]?.attachments?.map((att: any) => ({
+ test.attachments?.map((att) => ({
name: att.name,
path: att.path,
contentType: att.contentType,
@@ -124,7 +131,9 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
projectKey: ticketData.projectKey, // Keep the current project key
});
} catch (error) {
- toast.error(`Failed to create Jira ticket: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ toast.error(
+ `Failed to create Jira ticket: ${error instanceof Error ? error.message : 'Unknown error'}`
+ );
} finally {
setIsSubmitting(false);
}
@@ -141,9 +150,9 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
return `Test Failure Details
Test: ${test.title}
- Project: ${test.projectName}
- Location: ${test.location.file}:${test.location.line}
- Test ID: ${test.testId}
+ Project: ${test.projectName || 'Unknown'}
+ Location: ${test.location?.file || 'Unknown'}:${test.location?.line || 'Unknown'}
+ Test ID: ${test.testId || 'Unknown'}
Steps to Reproduce:
1. Run the test suite
@@ -156,9 +165,9 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
Test is failing
Additional Information:
- - Duration: ${test.duration}ms
- - Tags: ${test.tags.join(', ') || 'None'}
- - Annotations: ${test.annotations.join(', ') || 'None'}`;
+ - Duration: ${test.duration || 0}ms
+ - Tags: ${test.tags?.join(', ') || 'None'}
+ - Annotations: ${test.annotations?.map((a) => a.description).join(', ') || 'None'}`;
};
// Auto-populate form when test changes
@@ -187,7 +196,12 @@ export default function JiraTicketModal({ isOpen, onOpenChange, test, reportId }
) : !jiraConfig?.configured ? (
-
+
setTicketData((prev) => ({ ...prev, summary: value }))}
+ onValueChange={(value) =>
+ setTicketData((prev) => ({ ...prev, summary: value }))
+ }
/>
diff --git a/app/components/reports-table.tsx b/apps/frontend/src/components/reports-table.tsx
similarity index 71%
rename from app/components/reports-table.tsx
rename to apps/frontend/src/components/reports-table.tsx
index 5b8d5f3b..680cc928 100644
--- a/app/components/reports-table.tsx
+++ b/apps/frontend/src/components/reports-table.tsx
@@ -1,38 +1,37 @@
'use client';
-import { useCallback, useState, useMemo } from 'react';
import {
+ Button,
+ Chip,
+ LinkIcon,
+ Pagination,
+ Spinner,
Table,
- TableHeader,
- TableColumn,
TableBody,
- TableRow,
TableCell,
- Button,
- Spinner,
- Pagination,
- LinkIcon,
- Chip,
+ TableColumn,
+ TableHeader,
+ TableRow,
} from '@heroui/react';
-import Link from 'next/link';
+import type { ReadReportsHistory, ReportHistory } from '@playwright-reports/shared';
import { keepPreviousData } from '@tanstack/react-query';
+import { useCallback, useMemo, useState } from 'react';
+import { Link } from 'react-router-dom';
import { toast } from 'sonner';
-
+import useQuery from '../hooks/useQuery';
+import { defaultProjectName } from '../lib/constants';
+import { withQueryParams } from '../lib/network';
import { withBase } from '../lib/url';
-
+import FormattedDate from './date-format';
+import DeleteReportButton from './delete-report-button';
+import { BranchIcon, FolderIcon } from './icons';
+import InlineStatsCircle from './inline-stats-circle';
import TablePaginationOptions from './table-pagination-options';
-import { withQueryParams } from '@/app/lib/network';
-import { defaultProjectName } from '@/app/lib/constants';
-import useQuery from '@/app/hooks/useQuery';
-import DeleteReportButton from '@/app/components/delete-report-button';
-import FormattedDate from '@/app/components/date-format';
-import { BranchIcon, FolderIcon } from '@/app/components/icons';
-import { ReadReportsHistory, ReportHistory } from '@/app/lib/storage';
-
const columns = [
{ name: 'Title', uid: 'title' },
{ name: 'Project', uid: 'project' },
+ { name: 'Pass Rate', uid: 'passRate' },
{ name: 'Created at', uid: 'createdAt' },
{ name: 'Size', uid: 'size' },
{ name: '', uid: 'actions' },
@@ -45,6 +44,7 @@ const coreFields = [
'createdAt',
'size',
'sizeBytes',
+ 'options',
'reportUrl',
'metadata',
'startTime',
@@ -53,6 +53,7 @@ const coreFields = [
'projectNames',
'stats',
'errors',
+ 'playwrightVersion',
];
const getMetadataItems = (item: ReportHistory) => {
@@ -71,9 +72,25 @@ const getMetadataItems = (item: ReportHistory) => {
metadata.push({ key: 'workingDir', value: dirName, icon: });
}
if (itemWithMetadata.branch) {
- metadata.push({ key: 'branch', value: itemWithMetadata.branch, icon: });
+ metadata.push({
+ key: 'branch',
+ value: itemWithMetadata.branch,
+ icon: ,
+ });
}
+ if (itemWithMetadata.playwrightVersion) {
+ metadata.push({
+ key: 'playwright',
+ value: itemWithMetadata.playwrightVersion,
+ });
+ }
+
+ metadata.push({
+ key: 'workers',
+ value: itemWithMetadata.metadata?.actualWorkers,
+ });
+
// Add any other metadata fields
Object.entries(itemWithMetadata).forEach(([key, value]) => {
if (!coreFields.includes(key) && !['environment', 'workingDir', 'branch'].includes(key)) {
@@ -113,27 +130,22 @@ export default function ReportsTable({ onChange }: Readonly)
placeholderData: keepPreviousData,
});
- const { reports, total } = reportResponse ?? {};
+ const { reports } = reportResponse ?? {};
+ const total = reportResponse?.pagination?.total || reportResponse?.total || 0;
const onDeleted = () => {
onChange?.();
refetch();
};
- const onPageChange = useCallback(
- (page: number) => {
- setPage(page);
- },
- [page, rowsPerPage],
- );
+ const onPageChange = useCallback((page: number) => {
+ setPage(page);
+ }, []);
- const onProjectChange = useCallback(
- (project: string) => {
- setProject(project);
- setPage(1);
- },
- [page, rowsPerPage],
- );
+ const onProjectChange = useCallback((project: string) => {
+ setProject(project);
+ setPage(1);
+ }, []);
const onSearchChange = useCallback((searchTerm: string) => {
setSearch(searchTerm);
@@ -142,7 +154,7 @@ export default function ReportsTable({ onChange }: Readonly)
const pages = useMemo(() => {
return total ? Math.ceil(total / rowsPerPage) : 0;
- }, [project, total, rowsPerPage]);
+ }, [total, rowsPerPage]);
error && toast.error(error.message);
@@ -182,7 +194,10 @@ export default function ReportsTable({ onChange }: Readonly)
>
{(column) => (
-
+
{column.name}
)}
@@ -195,10 +210,10 @@ export default function ReportsTable({ onChange }: Readonly)
>
{(item) => (
-
+
{/* Main title and link */}
-
+
{item.title || item.reportID}
@@ -224,14 +239,30 @@ export default function ReportsTable({ onChange }: Readonly
)
- {item.project}
-
+ {item.project}
+
+ {
+
+ }
+
+
- {item.size}
-
+ {item.size}
+
-
+
Open report
diff --git a/app/components/reports.tsx b/apps/frontend/src/components/reports.tsx
similarity index 59%
rename from app/components/reports.tsx
rename to apps/frontend/src/components/reports.tsx
index 528cd8e2..5c8b16b0 100644
--- a/app/components/reports.tsx
+++ b/apps/frontend/src/components/reports.tsx
@@ -1,11 +1,11 @@
-import ReportsTable from '@/app/components/reports-table';
-import { title } from '@/app/components/primitives';
+import { title } from './primitives';
+import ReportsTable from './reports-table';
interface ReportsProps {
onChange: () => void;
}
-export default function Reports({ onChange }: ReportsProps) {
+export default function Reports({ onChange }: Readonly
) {
return (
<>
diff --git a/app/components/results-table.tsx b/apps/frontend/src/components/results-table.tsx
similarity index 83%
rename from app/components/results-table.tsx
rename to apps/frontend/src/components/results-table.tsx
index 33692d29..dcab0ccb 100644
--- a/app/components/results-table.tsx
+++ b/apps/frontend/src/components/results-table.tsx
@@ -1,28 +1,25 @@
-'use client';
-
-import { useCallback, useState, useMemo } from 'react';
import {
- Table,
- TableHeader,
- TableColumn,
- TableBody,
- TableRow,
- TableCell,
Chip,
+ Pagination,
type Selection,
Spinner,
- Pagination,
+ Table,
+ TableBody,
+ TableCell,
+ TableColumn,
+ TableHeader,
+ TableRow,
} from '@heroui/react';
+import type { ReadResultsOutput, Result } from '@playwright-reports/shared';
import { keepPreviousData } from '@tanstack/react-query';
+import { useCallback, useMemo, useState } from 'react';
import { toast } from 'sonner';
-
-import { withQueryParams } from '@/app/lib/network';
-import { defaultProjectName } from '@/app/lib/constants';
-import TablePaginationOptions from '@/app/components/table-pagination-options';
-import useQuery from '@/app/hooks/useQuery';
-import FormattedDate from '@/app/components/date-format';
-import { ReadResultsOutput, type Result } from '@/app/lib/storage';
-import DeleteResultsButton from '@/app/components/delete-results-button';
+import useQuery from '../hooks/useQuery';
+import { defaultProjectName } from '../lib/constants';
+import { withQueryParams } from '../lib/network';
+import FormattedDate from './date-format';
+import DeleteResultsButton from './delete-results-button';
+import TablePaginationOptions from './table-pagination-options';
const columns = [
{ name: 'Title', uid: 'title' },
@@ -45,7 +42,11 @@ interface ResultsTableProps {
onDeleted?: () => void;
}
-export default function ResultsTable({ onSelect, onDeleted, selected }: Readonly
) {
+export default function ResultsTable({
+ onSelect,
+ onDeleted,
+ selected,
+}: Readonly) {
const resultListEndpoint = '/api/result/list';
const [project, setProject] = useState(defaultProjectName);
const [selectedTags, setSelectedTags] = useState([]);
@@ -72,27 +73,21 @@ export default function ResultsTable({ onSelect, onDeleted, selected }: Readonly
placeholderData: keepPreviousData,
});
- const { results, total } = resultsResponse ?? {};
+ const { results, total } = resultsResponse ?? { results: [], total: 0 };
const shouldRefetch = () => {
onDeleted?.();
refetch();
};
- const onPageChange = useCallback(
- (page: number) => {
- setPage(page);
- },
- [page, rowsPerPage],
- );
+ const onPageChange = useCallback((page: number) => {
+ setPage(page);
+ }, []);
- const onProjectChange = useCallback(
- (project: string) => {
- setProject(project);
- setPage(1);
- },
- [page, rowsPerPage],
- );
+ const onProjectChange = useCallback((project: string) => {
+ setProject(project);
+ setPage(1);
+ }, []);
const onTagsChange = useCallback((tags: string[]) => {
setSelectedTags(tags);
@@ -106,7 +101,7 @@ export default function ResultsTable({ onSelect, onDeleted, selected }: Readonly
const pages = useMemo(() => {
return total ? Math.ceil(total / rowsPerPage) : 0;
- }, [project, total, rowsPerPage]);
+ }, [total, rowsPerPage]);
const onChangeSelect = (keys: Selection) => {
if (keys === 'all') {
@@ -212,7 +207,10 @@ export default function ResultsTable({ onSelect, onDeleted, selected }: Readonly
{item.size}
-
+
diff --git a/apps/frontend/src/components/results.tsx b/apps/frontend/src/components/results.tsx
new file mode 100644
index 00000000..612d0954
--- /dev/null
+++ b/apps/frontend/src/components/results.tsx
@@ -0,0 +1,58 @@
+import { getUniqueProjectsList, type Result } from '@playwright-reports/shared';
+import { useState } from 'react';
+import DeleteResultsButton from './delete-results-button';
+import GenerateReportButton from './generate-report-button';
+import { title } from './primitives';
+import ResultsTable from './results-table';
+import UploadResultsButton from './upload-results-button';
+
+interface ResultsProps {
+ onChange: () => void;
+}
+
+export default function Results({ onChange }: Readonly) {
+ const [selectedResults, setSelectedResults] = useState([]);
+
+ const selectedResultIds = selectedResults.map((r) => r.resultID);
+
+ const projects = getUniqueProjectsList(selectedResults);
+
+ const onListUpdate = () => {
+ setSelectedResults([]);
+ onChange?.();
+ };
+
+ return (
+ <>
+
+
+
Results
+
+
+ {selectedResults.length > 0 && (
+
+ Results selected: {selectedResults.length}
+
+ )}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/settings/components/AddLinkModal.tsx b/apps/frontend/src/components/settings/components/AddLinkModal.tsx
similarity index 88%
rename from app/settings/components/AddLinkModal.tsx
rename to apps/frontend/src/components/settings/components/AddLinkModal.tsx
index fc7115df..7373c464 100644
--- a/app/settings/components/AddLinkModal.tsx
+++ b/apps/frontend/src/components/settings/components/AddLinkModal.tsx
@@ -1,6 +1,6 @@
'use client';
-import { Input, Button } from '@heroui/react';
+import { Button, Input } from '@heroui/react';
interface AddLinkModalProps {
isOpen: boolean;
@@ -16,7 +16,7 @@ export default function AddLinkModal({
onAddLink,
onCancel,
onUpdateLinkData,
-}: AddLinkModalProps) {
+}: Readonly) {
if (!isOpen) return null;
return (
@@ -54,7 +54,11 @@ export default function AddLinkModal({
Cancel
-
+
Add Link
diff --git a/app/settings/components/CronConfiguration.tsx b/apps/frontend/src/components/settings/components/CronConfiguration.tsx
similarity index 91%
rename from app/settings/components/CronConfiguration.tsx
rename to apps/frontend/src/components/settings/components/CronConfiguration.tsx
index 5ee31f90..5734984d 100644
--- a/app/settings/components/CronConfiguration.tsx
+++ b/apps/frontend/src/components/settings/components/CronConfiguration.tsx
@@ -1,8 +1,8 @@
'use client';
-import { Card, CardBody, CardHeader, Input, Button, Chip } from '@heroui/react';
+import { Button, Card, CardBody, CardHeader, Chip, Input } from '@heroui/react';
-import { ServerConfig } from '../types';
+import type { ServerConfig } from '@playwright-reports/shared';
interface CronConfigurationProps {
config: ServerConfig;
@@ -24,7 +24,7 @@ export default function CronConfiguration({
onSave,
onCancel,
onUpdateTempConfig,
-}: CronConfigurationProps) {
+}: Readonly) {
return (
- Number of days before test results are automatically deleted
+
+ Number of days before test results are automatically deleted
+
@@ -108,7 +110,8 @@ export default function CronConfiguration({
}}
/>
- Cron expression for when to run result cleanup (e.g., "0 2 * * *" for daily at 2 AM)
+ Cron expression for when to run result cleanup (e.g., "0 2 * * *" for daily
+ at 2 AM)
@@ -137,7 +140,9 @@ export default function CronConfiguration({
}
}}
/>
- Number of days before test reports are automatically deleted
+
+ Number of days before test reports are automatically deleted
+
@@ -165,7 +170,8 @@ export default function CronConfiguration({
}}
/>
- Cron expression for when to run report cleanup (e.g., "0 3 * * *" for daily at 3 AM)
+ Cron expression for when to run report cleanup (e.g., "0 3 * * *" for daily
+ at 3 AM)
diff --git a/apps/frontend/src/components/settings/components/DatabaseInfo.tsx b/apps/frontend/src/components/settings/components/DatabaseInfo.tsx
new file mode 100644
index 00000000..f40b6192
--- /dev/null
+++ b/apps/frontend/src/components/settings/components/DatabaseInfo.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import { Button } from '@heroui/react';
+import type { DatabaseStats } from '@playwright-reports/shared';
+import { useQueryClient } from '@tanstack/react-query';
+import { toast } from 'sonner';
+import useMutation from '../../../hooks/useMutation';
+import { invalidateCache } from '../../../lib/query-cache';
+
+interface DatabaseInfoProps {
+ stats?: DatabaseStats;
+}
+
+export default function DatabaseInfo({ stats }: Readonly) {
+ const queryClient = useQueryClient();
+ const {
+ mutate: cacheRefresh,
+ isPending,
+ error,
+ } = useMutation('/api/cache/refresh', {
+ method: 'POST',
+ onSuccess: () => {
+ invalidateCache(queryClient, { queryKeys: ['/api'] });
+ toast.success(`db refreshed successfully`);
+ },
+ });
+
+ return (
+
+
Size: {stats?.sizeOnDisk ?? 'n/a'}
+
RAM: {stats?.estimatedRAM}
+
Results: {stats?.results}
+
Reports: {stats?.reports}
+
{
+ cacheRefresh({});
+ }}
+ >
+ Force Refresh
+
+ {error && toast.error(error.message)}
+
+ );
+}
diff --git a/app/settings/components/EnvironmentInfo.tsx b/apps/frontend/src/components/settings/components/EnvironmentInfo.tsx
similarity index 79%
rename from app/settings/components/EnvironmentInfo.tsx
rename to apps/frontend/src/components/settings/components/EnvironmentInfo.tsx
index 741f17aa..0f48a86d 100644
--- a/app/settings/components/EnvironmentInfo.tsx
+++ b/apps/frontend/src/components/settings/components/EnvironmentInfo.tsx
@@ -1,8 +1,8 @@
'use client';
import { Card, CardBody, CardHeader, Skeleton } from '@heroui/react';
-
-import { useAuthConfig } from '@/app/hooks/useAuthConfig';
+import { useAuthConfig } from '../../../hooks/useAuthConfig';
+import DatabaseInfo from './DatabaseInfo';
export default function EnvironmentInfo() {
const { config: envInfo, isLoading } = useAuthConfig();
@@ -19,15 +19,17 @@ export default function EnvironmentInfo() {
{isLoading ? (
) : (
- {envInfo?.authRequired ? 'Enabled' : 'Disabled'}
+
+ {envInfo?.authRequired ? 'Enabled' : 'Disabled'}
+
)}
-
Server Cache
+
Database
{isLoading ? (
) : (
-
{envInfo?.serverCache ? 'Enabled' : 'Disabled'}
+
)}
diff --git a/app/settings/components/JiraConfiguration.tsx b/apps/frontend/src/components/settings/components/JiraConfiguration.tsx
similarity index 86%
rename from app/settings/components/JiraConfiguration.tsx
rename to apps/frontend/src/components/settings/components/JiraConfiguration.tsx
index 03aa32fc..8fa11d3a 100644
--- a/app/settings/components/JiraConfiguration.tsx
+++ b/apps/frontend/src/components/settings/components/JiraConfiguration.tsx
@@ -1,8 +1,8 @@
'use client';
-import { Card, CardBody, CardHeader, Input, Button, Divider, Chip } from '@heroui/react';
+import { Button, Card, CardBody, CardHeader, Chip, Divider, Input } from '@heroui/react';
-import { ServerConfig, JiraConfig } from '../types';
+import type { JiraConfig, ServerConfig } from '@playwright-reports/shared';
interface JiraConfigurationProps {
config: ServerConfig;
@@ -26,7 +26,7 @@ export default function JiraConfiguration({
onSave,
onCancel,
onUpdateTempConfig,
-}: JiraConfigurationProps) {
+}: Readonly) {
return (
editingSection === 'jira' &&
onUpdateTempConfig({
@@ -83,7 +87,9 @@ export default function JiraConfiguration({
id="jira-email"
isDisabled={editingSection !== 'jira'}
placeholder="your-email@example.com"
- value={editingSection === 'jira' ? tempConfig.jira?.email || '' : config.jira?.email || ''}
+ value={
+ editingSection === 'jira' ? tempConfig.jira?.email || '' : config.jira?.email || ''
+ }
onChange={(e) =>
editingSection === 'jira' &&
onUpdateTempConfig({
@@ -102,7 +108,11 @@ export default function JiraConfiguration({
isDisabled={editingSection !== 'jira'}
placeholder="Your Jira API token"
type="password"
- value={editingSection === 'jira' ? tempConfig.jira?.apiToken || '' : config.jira?.apiToken || ''}
+ value={
+ editingSection === 'jira'
+ ? tempConfig.jira?.apiToken || ''
+ : config.jira?.apiToken || ''
+ }
onChange={(e) =>
editingSection === 'jira' &&
onUpdateTempConfig({
@@ -120,7 +130,11 @@ export default function JiraConfiguration({
id="jira-project-key"
isDisabled={editingSection !== 'jira'}
placeholder="PROJECT"
- value={editingSection === 'jira' ? tempConfig.jira?.projectKey || '' : config.jira?.projectKey || ''}
+ value={
+ editingSection === 'jira'
+ ? tempConfig.jira?.projectKey || ''
+ : config.jira?.projectKey || ''
+ }
onChange={(e) =>
editingSection === 'jira' &&
onUpdateTempConfig({
@@ -163,7 +177,9 @@ export default function JiraConfiguration({
Jira integration failed
-
Connection Error
+
+ Connection Error
+
{jiraConfig.error}
@@ -181,8 +197,8 @@ export default function JiraConfiguration({
Fill in the Jira configuration fields above and save the configuration.
- You can also set environment variables as a fallback: JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN,
- JIRA_PROJECT_KEY
+ You can also set environment variables as a fallback: JIRA_BASE_URL, JIRA_EMAIL,
+ JIRA_API_TOKEN, JIRA_PROJECT_KEY
diff --git a/app/settings/components/ServerConfiguration.tsx b/apps/frontend/src/components/settings/components/ServerConfiguration.tsx
similarity index 75%
rename from app/settings/components/ServerConfiguration.tsx
rename to apps/frontend/src/components/settings/components/ServerConfiguration.tsx
index 15dcd64d..46c0e4f5 100644
--- a/app/settings/components/ServerConfiguration.tsx
+++ b/apps/frontend/src/components/settings/components/ServerConfiguration.tsx
@@ -1,11 +1,9 @@
'use client';
-import { Card, CardBody, CardHeader, Input, Button, Divider, Chip } from '@heroui/react';
+import { Button, Card, CardBody, CardHeader, Chip, Divider, Input } from '@heroui/react';
+import type { ServerConfig } from '@playwright-reports/shared';
import { useRef, useState } from 'react';
-
-import { ServerConfig } from '../types';
-
-import { defaultLinks } from '@/app/config/site';
+import { defaultLinks } from '../../../config/site';
interface ServerConfigurationProps {
config: ServerConfig;
@@ -29,7 +27,7 @@ export default function ServerConfiguration({
onCancel,
onUpdateTempConfig,
onAddHeaderLink,
-}: ServerConfigurationProps) {
+}: Readonly) {
const logoFileRef = useRef(null);
const faviconFileRef = useRef(null);
const [logoFile, setLogoFile] = useState(null);
@@ -124,7 +122,9 @@ export default function ServerConfiguration({
isDisabled={editingSection !== 'server'}
placeholder="Enter site title"
value={editingSection === 'server' ? tempConfig.title || '' : config.title || ''}
- onChange={(e) => editingSection === 'server' && onUpdateTempConfig({ title: e.target.value })}
+ onChange={(e) =>
+ editingSection === 'server' && onUpdateTempConfig({ title: e.target.value })
+ }
/>
@@ -140,7 +140,11 @@ export default function ServerConfiguration({
@@ -163,7 +167,11 @@ export default function ServerConfiguration({
logoFileRef.current?.click()}>
{logoFile ? 'Change Logo' : 'Upload Logo'}
- {logoFile && {logoFile.name} }
+ {logoFile && (
+
+ {logoFile.name}
+
+ )}
Reset
@@ -192,7 +200,9 @@ export default function ServerConfiguration({
alt="Current favicon"
className="h-8 w-8 object-contain"
src={
- config.faviconPath.startsWith('http') ? config.faviconPath : `/api/static${config.faviconPath}`
+ config.faviconPath.startsWith('http')
+ ? config.faviconPath
+ : `/api/static${config.faviconPath}`
}
/>
@@ -213,11 +223,17 @@ export default function ServerConfiguration({
onChange={handleFaviconFileChange}
/>
-
faviconFileRef.current?.click()}>
+ faviconFileRef.current?.click()}
+ >
{faviconFile ? 'Change Favicon' : 'Upload Favicon'}
{faviconFile && (
- {faviconFile.name}
+
+ {faviconFile.name}
+
)}
Reset
@@ -240,39 +256,40 @@ export default function ServerConfiguration({
Custom Reporter Paths
- {(editingSection === 'server' ? tempConfig.reporterPaths || [] : config.reporterPaths || []).map(
- (path, index) => (
-
+ ))}
{editingSection === 'server' && (
)}
{editingSection === 'server' && (
- onUpdateTempConfig({ reporterPaths: [] })}>
+ onUpdateTempConfig({ reporterPaths: [] })}
+ >
Reset
)}
@@ -312,7 +333,11 @@ export default function ServerConfiguration({
)}
- {Object.entries(editingSection === 'server' ? tempConfig.headerLinks || {} : config.headerLinks || {})
+ {Object.entries(
+ editingSection === 'server'
+ ? tempConfig.headerLinks || {}
+ : config.headerLinks || {}
+ )
.filter(([key]) => key !== 'cyborgTest')
.map(([key, value]) => {
const isDefaultLink = ['github', 'telegram', 'discord'].includes(key);
@@ -320,7 +345,12 @@ export default function ServerConfiguration({
return (
diff --git a/app/components/stat-chart.tsx b/apps/frontend/src/components/stat-chart.tsx
similarity index 90%
rename from app/components/stat-chart.tsx
rename to apps/frontend/src/components/stat-chart.tsx
index d5766dd7..0bf4b4d6 100644
--- a/app/components/stat-chart.tsx
+++ b/apps/frontend/src/components/stat-chart.tsx
@@ -1,6 +1,6 @@
import { Label, Pie, PieChart } from 'recharts';
-import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from '@/app/components/ui/chart';
+import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from './ui/chart';
const chartConfig = {
count: {
@@ -65,7 +65,11 @@ export function StatChart({ stats }: Readonly) {
if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {
return (
-
+
{`${Math.round((stats.expected / (stats.total - stats.skipped)) * 100)}%`}
diff --git a/app/components/table-pagination-options.tsx b/apps/frontend/src/components/table-pagination-options.tsx
similarity index 88%
rename from app/components/table-pagination-options.tsx
rename to apps/frontend/src/components/table-pagination-options.tsx
index 32534090..b3f840bb 100644
--- a/app/components/table-pagination-options.tsx
+++ b/apps/frontend/src/components/table-pagination-options.tsx
@@ -1,9 +1,8 @@
+import { Input, Select, SelectItem } from '@heroui/react';
import { type ChangeEvent, useCallback } from 'react';
-import { Select, SelectItem, Input } from '@heroui/react';
-
-import ProjectSelect from '@/app/components/project-select';
-import TagSelect from '@/app/components/tag-select';
-import { SearchIcon } from '@/app/components/icons';
+import { SearchIcon } from './icons';
+import ProjectSelect from './project-select';
+import TagSelect from './tag-select';
interface TablePaginationRowProps {
total?: number;
@@ -29,7 +28,7 @@ export default function TablePaginationOptions({
onProjectChange,
onSearchChange,
onTagsChange,
-}: TablePaginationRowProps) {
+}: Readonly) {
const rowPerPageItems = rowPerPageOptions ?? defaultRowPerPageOptions;
const onRowsPerPageChange = useCallback(
@@ -39,7 +38,7 @@ export default function TablePaginationOptions({
setRowsPerPage(rows);
setPage(1);
},
- [rowsPerPage],
+ [setPage, setRowsPerPage]
);
return (
diff --git a/app/components/tag-select.tsx b/apps/frontend/src/components/tag-select.tsx
similarity index 75%
rename from app/components/tag-select.tsx
rename to apps/frontend/src/components/tag-select.tsx
index 0e35e297..ebff8fe5 100644
--- a/app/components/tag-select.tsx
+++ b/apps/frontend/src/components/tag-select.tsx
@@ -1,9 +1,9 @@
'use client';
-import { Select, SelectItem, SharedSelection } from '@heroui/react';
+import { Select, SelectItem, type SharedSelection } from '@heroui/react';
import { toast } from 'sonner';
-
import useQuery from '../hooks/useQuery';
+import { buildUrl } from '../lib/url';
interface TagSelectProps {
onSelect?: (tags: string[]) => void;
@@ -12,12 +12,17 @@ interface TagSelectProps {
project?: string;
}
-export default function TagSelect({ refreshId, onSelect, entity, project }: Readonly) {
+export default function TagSelect({
+ refreshId,
+ onSelect,
+ entity,
+ project,
+}: Readonly) {
const {
data: tags,
error,
isLoading,
- } = useQuery(`/api/${entity}/tags${project ? `?project=${encodeURIComponent(project)}` : ''}`, {
+ } = useQuery(buildUrl(`/api/${entity}/tags`, project ? { project } : undefined), {
dependencies: [refreshId, project],
});
diff --git a/app/components/theme-switch.tsx b/apps/frontend/src/components/theme-switch.tsx
similarity index 81%
rename from app/components/theme-switch.tsx
rename to apps/frontend/src/components/theme-switch.tsx
index 262745a0..6f2782dd 100644
--- a/app/components/theme-switch.tsx
+++ b/apps/frontend/src/components/theme-switch.tsx
@@ -1,15 +1,15 @@
'use client';
-import { FC } from 'react';
-import { VisuallyHidden } from '@react-aria/visually-hidden';
-import { SwitchProps, useSwitch } from '@heroui/switch';
-import { useTheme } from 'next-themes';
+import { type SwitchProps, useSwitch } from '@heroui/switch';
import { useIsSSR } from '@react-aria/ssr';
+import { VisuallyHidden } from '@react-aria/visually-hidden';
import clsx from 'clsx';
+import { useTheme } from 'next-themes';
+import type { FC } from 'react';
-import { SunFilledIcon, MoonFilledIcon } from '@/app/components/icons';
+import { MoonFilledIcon, SunFilledIcon } from './icons';
-export interface ThemeSwitchProps {
+interface ThemeSwitchProps {
className?: string;
classNames?: SwitchProps['classNames'];
}
@@ -34,7 +34,11 @@ export const ThemeSwitch: FC = ({ className, classNames }) =>
return (
@@ -55,7 +59,7 @@ export const ThemeSwitch: FC = ({ className, classNames }) =>
'px-0',
'mx-0',
],
- classNames?.wrapper,
+ classNames?.wrapper
),
})}
>
diff --git a/app/components/trend-chart.tsx b/apps/frontend/src/components/trend-chart.tsx
similarity index 82%
rename from app/components/trend-chart.tsx
rename to apps/frontend/src/components/trend-chart.tsx
index be389803..1ed40825 100644
--- a/app/components/trend-chart.tsx
+++ b/apps/frontend/src/components/trend-chart.tsx
@@ -1,8 +1,8 @@
'use client';
+import { Alert } from '@heroui/react';
+import type { ReportHistory } from '@playwright-reports/shared';
+import { Link } from 'react-router-dom';
import { Area, AreaChart, XAxis } from 'recharts';
-import Link from 'next/link';
-
-import { type ReportHistory } from '@/app/lib/storage';
import {
type ChartConfig,
ChartContainer,
@@ -10,7 +10,7 @@ import {
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
-} from '@/app/components/ui/chart';
+} from './ui/chart';
const chartConfig = {
failed: {
@@ -48,22 +48,26 @@ export function TrendChart({ reportHistory }: Readonly) {
const chartData = reportHistory.map((r) => ({
date: new Date(r.createdAt).getTime(),
- passed: getPercentage(r.stats.expected, r.stats.total),
- passedCount: r.stats.expected,
- failed: getPercentage(r.stats.unexpected, r.stats.total),
- failedCount: r.stats.unexpected,
- skipped: getPercentage(r.stats.skipped, r.stats.total),
- skippedCount: r.stats.skipped,
- flaky: getPercentage(r.stats.flaky, r.stats.total),
- flakyCount: r.stats.flaky,
- total: r.stats.total,
- reportUrl: r.reportUrl,
+ passed: getPercentage(r.stats?.expected || 0, r.stats?.total || 0),
+ passedCount: r.stats?.expected || 0,
+ failed: getPercentage(r.stats?.unexpected || 0, r.stats?.total || 0),
+ failedCount: r.stats?.unexpected || 0,
+ skipped: getPercentage(r.stats?.skipped || 0, r.stats?.total || 0),
+ skippedCount: r.stats?.skipped || 0,
+ flaky: getPercentage(r.stats?.flaky || 0, r.stats?.total || 0),
+ flakyCount: r.stats?.flaky || 0,
+ total: r.stats?.total || 0,
+ reportUrl: `/report/${r.reportID}`,
}));
return (
{reportHistory.length <= 1 ? (
- Not enough data for trend chart
+
) : (
) {
{/* Add this after the last item */}
{index === 3 && (
<>
-
+
Total
@@ -133,7 +137,7 @@ export function TrendChart({ reportHistory }: Readonly
) {
{new Date(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
- item.payload.date,
+ item.payload.date
).toLocaleString()}
diff --git a/app/components/ui/chart.tsx b/apps/frontend/src/components/ui/chart.tsx
similarity index 83%
rename from app/components/ui/chart.tsx
rename to apps/frontend/src/components/ui/chart.tsx
index 440133f2..543e9bf5 100644
--- a/app/components/ui/chart.tsx
+++ b/apps/frontend/src/components/ui/chart.tsx
@@ -1,9 +1,17 @@
'use client';
-import { ReactNode, ComponentType, createContext, useContext, forwardRef, useId, useMemo } from 'react';
+import {
+ type ComponentType,
+ createContext,
+ forwardRef,
+ type ReactNode,
+ useContext,
+ useId,
+ useMemo,
+} from 'react';
import * as RechartsPrimitive from 'recharts';
-import { cn } from '@/app/lib/tailwind';
+import { cn } from '../../styles/globals';
const THEMES = { light: '', dark: '.dark' } as const;
@@ -11,7 +19,10 @@ export type ChartConfig = {
[k in string]: {
label?: ReactNode;
icon?: ComponentType;
- } & ({ color?: string; theme?: never } | { color?: never; theme: Record
});
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ );
};
type ChartContextProps = {
@@ -46,7 +57,7 @@ const ChartContainer = forwardRef<
ref={ref}
className={cn(
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
- className,
+ className
)}
data-chart={chartId}
{...props}
@@ -69,6 +80,7 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
return (