diff --git a/.gitignore b/.gitignore index 129ac24b..e0d1af08 100644 --- a/.gitignore +++ b/.gitignore @@ -92,4 +92,6 @@ packages/back-end/cdk.context.json packages/front-end/test-results packages/front-end/tests/e2e/.auth/*.json packages/front-end/playwright-report +!packages/back-end/.env.local.example +!config/.env.nuxt.local.example !/.projenrc.ts diff --git a/.projenrc.ts b/.projenrc.ts index 0e089289..ba101347 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -343,12 +343,16 @@ const backEndApp = new awscdk.AwsCdkTypeScriptApp({ devDeps: [ '@aws-sdk/types', '@types/aws-lambda', + '@types/express', '@types/jsonwebtoken', '@types/node', '@types/uuid', + 'aws-jwt-verify', 'aws-sdk-client-mock', - 'prettier', 'eslint-plugin-prettier', + 'express', + 'prettier', + 'tsx', ], }); backEndApp.addScripts({ @@ -357,6 +361,9 @@ backEndApp.addScripts({ ['deploy']: 'pnpm cdk bootstrap && pnpm dlx projen deploy', ['build-and-deploy']: 'pnpm -w run build-back-end && pnpm run deploy --require-approval any-change', // Run root build-back-end script to inc shared-lib ['lint']: "eslint 'src/**/*.{js,ts}' --fix", + ['local-server']: 'tsx src/local-server/index.ts', + ['local-server:watch']: 'tsx watch src/local-server/index.ts', + ['invoke-process-handler']: 'tsx src/local-server/invoke-process-handler.ts', }); if (backEndApp.eslint) { @@ -547,6 +554,8 @@ root.gitignore.addPatterns( 'packages/front-end/tests/e2e/.auth/*.json', 'packages/front-end/playwright-report', ); +// Exception: Include .env example files (used for local dev setup documentation) +root.gitignore.addPatterns('!packages/back-end/.env.local.example', '!config/.env.nuxt.local.example'); // Synthesize the project root.synth(); diff --git a/docs/LOCAL_BACKEND_DEV.md b/docs/LOCAL_BACKEND_DEV.md new file mode 100644 index 00000000..9afb5baa --- /dev/null +++ b/docs/LOCAL_BACKEND_DEV.md @@ -0,0 +1,209 @@ +# Local Back-End Development Setup + +#### 1. Deploy the back-end stack (if not already deployed) + +You need a deployed dev stack so you can obtain AWS resource identifiers. + +```bash +# From repo root +pnpm run build-and-deploy +``` + +Or deploy only the back-end: + +```bash +pnpm run build-back-end +cd packages/back-end && pnpm run deploy +``` + +#### 2. Create and populate `packages/back-end/.env.local` + +```bash +cp packages/back-end/.env.local.example packages/back-end/.env.local +``` + +Fill in the values. Use this reference: + +| Variable | How to obtain | +| ----------------------------- | ------------------------------------------------------------------------------------------------ | +| `NAME_PREFIX` | `{env-type}-{env-name}` from easy-genomics.yaml (e.g. `dev-demo`) | +| `ACCOUNT_ID` | AWS account ID (12 digits) | +| `REGION` | From easy-genomics.yaml `aws-region` | +| `DOMAIN_NAME` | From easy-genomics.yaml `app-domain-name` | +| `ENV_TYPE` | From easy-genomics.yaml `env-type` | +| `ENV_NAME` | Config key from easy-genomics.yaml (e.g. `demo`) | +| `COGNITO_USER_POOL_ID` | AWS Console → Cognito → User Pools → _{name-prefix}-easy-genomics-auth-user-pool_ → User pool ID | +| `COGNITO_USER_POOL_CLIENT_ID` | Same pool → App integration → App client ID | +| `JWT_SECRET_KEY` | From easy-genomics.yaml `back-end.jwt-secret-key` (must match deployed value) | +| `SNS_*_TOPIC` | AWS Console → SNS → Topics. Look for `{name-prefix}-*-topic` | +| `COGNITO_KMS_KEY_ID` | AWS Console → KMS → Customer managed keys → `{name-prefix}-easy-genomics-cognito-idp-kms-key` | +| `COGNITO_KMS_KEY_ARN` | Same key → ARN | +| `SEQERA_API_BASE_URL` | Optional. Default: `https://api.cloud.seqera.io` | + +**SNS topic names:** + +- `{name-prefix}-organization-deletion-topic` +- `{name-prefix}-laboratory-deletion-topic` +- `{name-prefix}-user-deletion-topic` +- `{name-prefix}-laboratory-run-update-topic` +- `{name-prefix}-user-invite-topic` + +#### 3. Generate front-end config (if not already done) + +**Prerequisite: AWS credentials.** The script calls AWS (API Gateway and Cognito) to fetch your stack’s API URL and +Cognito IDs. Configure credentials first: + +- **Long‑lived credentials:** run `aws configure` and set Access Key ID, Secret Access Key, and Default region (match + `aws-region` in `easy-genomics.yaml`). +- **AWS SSO:** run `aws sso login` (and set `AWS_PROFILE` if needed). +- **Env vars:** set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_REGION` (and `AWS_SESSION_TOKEN` if using + temporary credentials). + +Then from `packages/front-end`, run (optional) `nuxt prepare`, then the settings script: + +```bash +cd packages/front-end +pnpm run nuxt-prepare # optional but fixes ".nuxt/tsconfig.json" warning if you see it +pnpm run nuxt-load-settings +``` + +This compiles `@easy-genomics/shared-lib` (if needed) and creates `config/.env.nuxt` by querying AWS for API Gateway +URL, Cognito IDs, etc. + +#### 4. Point the front-end at the local back-end (when using the local server) + +**Option A – Env var (recommended, no file toggling):** Run the dev server with: + +```bash +cd packages/front-end +USE_LOCAL_BACKEND=1 pnpm run nuxt-dev +``` + +Use the deployed API by running `pnpm run nuxt-dev` without the env var. Optional: `LOCAL_API_URL=http://localhost:3002` +if your local server uses a different port. + +**Option B – File override:** Copy and edit the local env file so the front-end always uses the local API while the file +exists: + +```bash +cp config/.env.nuxt.local.example config/.env.nuxt.local +``` + +Set `AWS_API_GATEWAY_URL=http://localhost:3001` (no trailing slash). To use the deployed API again, rename or delete +`config/.env.nuxt.local`. + +#### 5. (Optional) Add localhost to Cognito callback URLs + +For local development, Cognito must allow `http://localhost:3000` (or your dev port) in callback and logout URLs. Update +`easy-genomics.yaml`: + +```yaml +callback-urls: 'http://localhost:3000/auth/callback' +logout-urls: 'http://localhost:3000/signin' +``` + +Then redeploy the back-end so Cognito picks up the new URLs. Alternatively, if you have multiple URLs configured, ensure +localhost is included. + +--- + +## Quick Reference: Switching Between Local and Deployed Back-End + +You can switch without renaming or editing files by using an environment variable. + +| Mode | How to run | Result | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| **Deployed API** | `pnpm run nuxt-dev` (from `packages/front-end`) | Front-end uses API Gateway URL from `config/.env.nuxt` | +| **Local API** | `USE_LOCAL_BACKEND=1 pnpm run nuxt-dev` (macOS/Linux) or set `USE_LOCAL_BACKEND=1` in your shell then run `pnpm run nuxt-dev` | Front-end uses `http://localhost:3001` (or `LOCAL_API_URL` if set) | + +Optional: set `LOCAL_API_URL` when using a different port (e.g. `LOCAL_API_URL=http://localhost:3002`). No need to +create or edit `config/.env.nuxt.local` when using the env var. + +**Alternative (file-based):** If `config/.env.nuxt.local` exists with `AWS_API_GATEWAY_URL=http://localhost:3001`, the +front-end uses the local server. Delete or rename that file to use the deployed API again. + +--- + +## Phase 2: Local API Server + +Phase 2 is implemented in `packages/back-end/src/local-server/`. The server runs Lambda handlers locally and talks to +real AWS (DynamoDB, Cognito, S3, etc.) using `packages/back-end/.env.local`. + +### Running the local server + +1. Ensure Phase 1 is complete (`.env.local` exists and is populated). +2. From **packages/back-end** (or repo root with `--filter`): + + ```bash + cd packages/back-end + pnpm run local-server + ``` + + The server listens on **http://localhost:3001** (or `LOCAL_SERVER_PORT` if set). + +3. Run the front-end against it: + + ```bash + cd packages/front-end + USE_LOCAL_BACKEND=1 pnpm run nuxt-dev + ``` + +**Optional:** `pnpm run local-server:watch` restarts the server when files under `src/local-server` or Lambda handlers +change. + +### Troubleshooting + +| Symptom | What to check | +| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Module not found `@BE/*` or `@SharedLib/*`** | Run the server with `pnpm run local-server` (uses `tsx`). Do not run with plain `node`; path aliases require the TypeScript runner. | +| **401 Unauthorized** | See [401 Unauthorized](#401-unauthorized) below. | +| **DynamoDB or table errors** | Verify `NAME_PREFIX` in `.env.local` matches your deployed stack (e.g. `dev-demo`). Table names are `{NAME_PREFIX}-organization-table`, etc. | +| **Region is missing** | The local server sets `AWS_REGION` from `REGION` in `.env.local` so the AWS SDK (DynamoDB, S3, etc.) can resolve the region. Ensure `REGION` is set in `packages/back-end/.env.local` (e.g. `us-east-1`). | +| **ExpiredTokenException / security token expired** | This refers to **AWS credentials** (used by the server to call DynamoDB, S3, etc.), not your Cognito login. If you use AWS SSO, run `aws sso login` again; if you use temporary credentials, refresh them. Then restart the local server. | +| **CORS errors in browser** | The server allows all origins and credentials. If you use a different front-end origin, ensure it is correct. | +| **Missing required env vars** | The server exits on startup if `NAME_PREFIX`, `ACCOUNT_ID`, `REGION`, `COGNITO_USER_POOL_ID`, or `COGNITO_USER_POOL_CLIENT_ID` are missing. See `.env.local.example`. | + +#### 401 Unauthorized + +If the front-end is signed in but API calls to the local server return 401: + +1. **Cognito IDs must match the front-end** + The local server verifies the JWT using `COGNITO_USER_POOL_ID` and `COGNITO_USER_POOL_CLIENT_ID` from + `packages/back-end/.env.local`. These must be the **same** pool and client the front-end uses to sign in (from + `config/.env.nuxt`). + + - In `config/.env.nuxt`: `AWS_COGNITO_USER_POOL_ID`, `AWS_COGNITO_USER_POOL_CLIENT_ID` + - In `packages/back-end/.env.local`: `COGNITO_USER_POOL_ID`, `COGNITO_USER_POOL_CLIENT_ID` + Copy the values from `.env.nuxt` into `.env.local` (same pool ID and client ID). + +2. **Ensure you are signed in** + The front-end only sends a token when the user has a Cognito session. Open the app, sign in, then retry the API call. + +3. **Check that the token is sent** + In DevTools → Network, select a failing request and confirm the **Request Headers** include + `Authorization: Bearer `. If it’s missing, the front-end may not have a session (sign in again) or the request + path may not be going through the repository that adds the token. + +4. **See why verification failed** + In `packages/back-end/.env.local` set `DEBUG_AUTH=true`, restart the local server, and trigger the request again. The + server console will log whether the token was missing or JWT verification failed (e.g. wrong issuer, expired, wrong + client). + +5. **Quick local-only bypass (no verification)** + For local development only, you can skip JWT verification: in `packages/back-end/.env.local` set + `SKIP_JWT_VERIFY=true`. The server will decode the JWT and accept it without verifying the signature. Use only in a + safe local environment. + +### process-\* (async) Lambdas + +The following Lambdas are **not** exposed as HTTP routes; they run in AWS when SQS or Cognito triggers fire: + +- **SQS-triggered:** `process-create-user-invites`, `process-delete-organization`, `process-delete-laboratory`, + `process-delete-user`, `process-update-laboratory-run` +- **Cognito-triggered:** `process-pre-signup`, `process-post-authentication`, `process-custom-email-sender`, + `process-pre-token-generation` + +When using the local server, these still run in the deployed stack (e.g. user invite emails, org/lab/user deletion +workflows). To test one locally, you can invoke its handler manually with a script. + +--- diff --git a/packages/back-end/.env.local.example b/packages/back-end/.env.local.example new file mode 100644 index 00000000..13685761 --- /dev/null +++ b/packages/back-end/.env.local.example @@ -0,0 +1,69 @@ +# ============================================================================= +# Easy Genomics - Local Back-End Development Environment +# ============================================================================= +# +# Copy this file to .env.local and fill in the values from your deployed +# dev stack. These values must match the AWS resources (DynamoDB, Cognito, +# SNS, SQS, SSM, S3, etc.) that the local server will connect to. +# +# How to obtain these values: +# 1. Deploy the back-end once: pnpm run build-and-deploy (from repo root) +# 2. CDK outputs and AWS Console - see Phase 1 checklist in docs/LOCAL_BACKEND_DEV.md +# 3. easy-genomics.yaml - for jwt-secret-key, seqera-api-base-url +# +# ============================================================================= + +# --- Required: Common (all Lambda handlers) --- +# Format: {env-type}-{env-name} (e.g. dev-demo). Used for DynamoDB table names. +NAME_PREFIX= + +# AWS account ID (12 digits) +ACCOUNT_ID= + +# AWS region (e.g. us-east-1, us-west-2) +REGION= + +# From easy-genomics.yaml app-domain-name +DOMAIN_NAME= + +# dev | pre-prod | prod +ENV_TYPE= + +# From easy-genomics.yaml config key (e.g. demo) +ENV_NAME= + +# --- Required: Cognito (most authenticated endpoints) --- +# From AWS Console > Cognito > User Pools > Your pool > User pool ID +COGNITO_USER_POOL_ID= + +# From AWS Console > Cognito > User Pools > Your pool > App integration > App client ID +COGNITO_USER_POOL_CLIENT_ID= + +# --- Required: JWT (invites, password reset, custom email) --- +# Must match the value used when the stack was deployed. From easy-genomics.yaml back-end.jwt-secret-key +JWT_SECRET_KEY= + +# --- Required: SNS Topics (async operations) --- +# ARNs for SNS topics. Get from AWS Console > SNS > Topics, or CDK outputs. +# Format: arn:aws:sns:{region}:{account}:{name-prefix}-easy-genomics-auth-*-topic +SNS_ORGANIZATION_DELETION_TOPIC= +SNS_LABORATORY_DELETION_TOPIC= +SNS_USER_DELETION_TOPIC= +SNS_LABORATORY_RUN_UPDATE_TOPIC= +SNS_USER_INVITE_TOPIC= + +# --- Required for some Lambdas: Cognito KMS (custom email sender) --- +# From AWS Console > KMS > Customer managed keys, find cognito-idp-kms-key +COGNITO_KMS_KEY_ID= +COGNITO_KMS_KEY_ARN= + +# --- Required for Seqera/NextFlow Tower integration --- +# Default: https://api.cloud.seqera.io. Override for self-hosted Seqera. +SEQERA_API_BASE_URL=https://api.cloud.seqera.io + +# --- Optional: Local server port (default 3001) --- +# LOCAL_SERVER_PORT=3001 + +# --- Optional: Auth troubleshooting (local dev only) --- +# DEBUG_AUTH=true - Log 401 reason to server console (missing token vs JWT verification failed) +# SKIP_JWT_VERIFY=true - Accept JWT without signature verification (dev only; do not use in production) diff --git a/packages/back-end/package.json b/packages/back-end/package.json index 710d15f9..efeff92b 100644 --- a/packages/back-end/package.json +++ b/packages/back-end/package.json @@ -22,7 +22,10 @@ "projen": "pnpm dlx projen", "cdk-audit": "export CDK_AUDIT=true && pnpm dlx projen build", "build-and-deploy": "pnpm -w run build-back-end && pnpm run deploy --require-approval any-change", - "lint": "eslint 'src/**/*.{js,ts}' --fix" + "lint": "eslint 'src/**/*.{js,ts}' --fix", + "local-server": "tsx src/local-server/index.ts", + "local-server:watch": "tsx watch src/local-server/index.ts", + "invoke-process-handler": "tsx src/local-server/invoke-process-handler.ts" }, "author": { "name": "DEPT Agency", @@ -32,10 +35,12 @@ "@aws-sdk/types": "^3.731.0", "@types/archiver": "^7.0.0", "@types/aws-lambda": "^8.10.149", + "@types/express": "^4.17.21", "@types/jest": "^29.5.14", "@types/jsonwebtoken": "^9.0.9", "@types/node": "^20.17.30", "@types/uuid": "^9.0.8", + "express": "^4.21.0", "@typescript-eslint/eslint-plugin": "^7", "@typescript-eslint/parser": "^7", "aws-cdk": "^2.176.0", @@ -50,6 +55,7 @@ "prettier": "^3.5.3", "ts-jest": "^29.3.1", "ts-node": "^10.9.2", + "tsx": "^4.19.0", "typescript": "^5.8.3" }, "dependencies": { @@ -74,11 +80,13 @@ "@easy-genomics/shared-lib": "workspace:*", "archiver": "^7.0.1", "aws-cdk-lib": "^2.176.0", + "aws-jwt-verify": "^4.0.1", "aws-lambda": "^1.0.7", "base64-js": "^1.5.1", "cdk-nag": "^2.35.67", "constructs": "^10.0.5", "dotenv": "^16.4.7", + "express": "^4.21.0", "jsonwebtoken": "^9.0.2", "uuid": "^8.3.2" }, @@ -125,14 +133,9 @@ "^.+\\.[t]sx?$": [ "ts-jest", { - "tsconfig": "tsconfig.test.json" + "tsconfig": "tsconfig.dev.json" } ] - }, - "moduleNameMapper": { - "^@BE/(.*)$": "/src/app/$1", - "^@FE/(.*)$": "/../front-end/src/app/$1", - "^@SharedLib/(.*)$": "/../shared-lib/src/app/$1" } }, "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm dlx projen\"." diff --git a/packages/back-end/src/local-server/auth-middleware.ts b/packages/back-end/src/local-server/auth-middleware.ts new file mode 100644 index 00000000..5f45289d --- /dev/null +++ b/packages/back-end/src/local-server/auth-middleware.ts @@ -0,0 +1,161 @@ +/** + * Auth middleware for the local API server. + * Validates Cognito JWT and attaches claims to the event, or returns 401. + * Unauthenticated endpoints (invite confirm, forgot password) skip validation. + */ + +import { CognitoJwtVerifier } from 'aws-jwt-verify'; +import type { APIGatewayProxyWithCognitoAuthorizerEvent } from 'aws-lambda'; +import type { Response } from 'express'; + +/** Paths that do not require a valid JWT (same as CDK NagSuppressions). */ +export const UNAUTHENTICATED_PATHS = new Set([ + 'POST /easy-genomics/user/confirm-user-invitation-request', + 'POST /easy-genomics/user/create-user-forgot-password-request', + 'POST /easy-genomics/user/confirm-user-forgot-password-request', +]); + +function getAuthKey(method: string, path: string): string { + return `${method} ${path}`; +} + +/** Normalize path: Express may give /read-organization/org-123, we need the pattern path for lookup. */ +function normalizedPathForAuth(path: string, hasIdParam: boolean): string { + if (hasIdParam && path.split('/').length > 0) { + const parts = path.split('/'); + parts[parts.length - 1] = ':id'; + return parts.join('/'); + } + return path; +} + +let verifier: ReturnType | null = null; + +function getVerifier(): ReturnType { + if (verifier) return verifier; + const userPoolId = process.env.COGNITO_USER_POOL_ID; + const clientId = process.env.COGNITO_USER_POOL_CLIENT_ID; + if (!userPoolId || !clientId) { + throw new Error('COGNITO_USER_POOL_ID and COGNITO_USER_POOL_CLIENT_ID are required for JWT verification'); + } + verifier = CognitoJwtVerifier.create({ + userPoolId, + tokenUse: 'id', + clientId, + }); + return verifier; +} + +/** + * If SKIP_JWT_VERIFY is set, decode the JWT without verification and set claims (dev only). + * Payload is not fully typed; we copy string/number claims into authorizer.claims. + */ +function decodeUnsafe(token: string): Record { + const parts = token.split('.'); + if (parts.length !== 3) throw new Error('Invalid JWT format'); + const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')); + const claims: Record = {}; + for (const [k, v] of Object.entries(payload)) { + if (typeof v === 'string' || typeof v === 'number') claims[k] = String(v); + } + return claims; +} + +export interface AuthMiddlewareResult { + authorized: true; + claims: Record; +} + +export interface AuthMiddlewareUnauthorized { + authorized: false; + statusCode: number; + body: string; +} + +export type AuthResult = AuthMiddlewareResult | AuthMiddlewareUnauthorized; + +/** + * Run auth for the given method + path. If the route is unauthenticated, returns + * authorized with empty claims. Otherwise validates Bearer token and returns claims or 401. + */ +export async function runAuth( + method: string, + path: string, + hasIdParam: boolean, + authHeader: string | undefined, +): Promise { + const pathForKey = normalizedPathForAuth(path, hasIdParam); + const key = getAuthKey(method, pathForKey); + + if (UNAUTHENTICATED_PATHS.has(key)) { + return { authorized: true, claims: {} }; + } + + const bearer = authHeader?.startsWith('Bearer ') ? authHeader.slice(7).trim() : authHeader; + const debugAuth = process.env.DEBUG_AUTH === 'true' || process.env.DEBUG_AUTH === '1'; + + if (!bearer) { + if (debugAuth) { + console.warn(`[auth-middleware] 401: no Bearer token for ${method} ${pathForKey}`); + } + return { + authorized: false, + statusCode: 401, + body: JSON.stringify({ Error: 'Authorization header missing or invalid', ErrorCode: 'EG-101' }), + }; + } + + const skipVerify = process.env.SKIP_JWT_VERIFY === 'true' || process.env.SKIP_JWT_VERIFY === '1'; + if (skipVerify) { + console.warn('[auth-middleware] SKIP_JWT_VERIFY is set; decoding JWT without verification (dev only)'); + try { + const claims = decodeUnsafe(bearer); + return { authorized: true, claims }; + } catch (e) { + return { + authorized: false, + statusCode: 401, + body: JSON.stringify({ Error: 'Invalid or malformed token', ErrorCode: 'EG-101' }), + }; + } + } + + try { + const payload = await getVerifier().verify(bearer); + const claims: Record = {}; + for (const [k, v] of Object.entries(payload)) { + if (typeof v === 'string' || typeof v === 'number') claims[k] = String(v); + } + return { authorized: true, claims }; + } catch (err) { + const message = err instanceof Error ? err.message : 'Invalid token'; + if (debugAuth) { + console.warn(`[auth-middleware] 401: JWT verification failed for ${method} ${pathForKey}:`, err); + } + return { + authorized: false, + statusCode: 401, + body: JSON.stringify({ Error: message, ErrorCode: 'EG-101' }), + }; + } +} + +/** + * Attach auth claims to the event's requestContext.authorizer. + * Call this after runAuth when authorized is true. + */ +export function attachClaimsToEvent( + event: APIGatewayProxyWithCognitoAuthorizerEvent, + claims: Record, +): void { + event.requestContext.authorizer.claims = claims; +} + +/** + * Send 401 response and return true so caller can short-circuit. Otherwise return false. + */ +export function sendUnauthorizedIfNeeded(res: Response, result: AuthResult): boolean { + if (result.authorized) return false; + res.status(result.statusCode).setHeader('Content-Type', 'application/json').send(result.body); + return true; +} diff --git a/packages/back-end/src/local-server/event-builder.ts b/packages/back-end/src/local-server/event-builder.ts new file mode 100644 index 00000000..7ae89d3c --- /dev/null +++ b/packages/back-end/src/local-server/event-builder.ts @@ -0,0 +1,100 @@ +/** + * Builds an API Gateway proxy event (Cognito authorizer shape) from an Express request. + * Auth middleware populates requestContext.authorizer.claims. + */ + +import { randomUUID } from 'crypto'; +import type { APIGatewayProxyWithCognitoAuthorizerEvent } from 'aws-lambda'; +import type { Request } from 'express'; + +export interface BuildEventOptions { + /** Raw request body string. If not provided, req.body is used (stringified if object). */ + rawBody?: string; +} + +/** + * Convert Express req to APIGatewayProxyWithCognitoAuthorizerEvent. + * Path parameters (e.g. id) and authorizer.claims are set by the caller (auth middleware fills claims). + */ +export function buildApiGatewayEvent( + req: Request, + pathParams: { id?: string }, + options?: BuildEventOptions, +): APIGatewayProxyWithCognitoAuthorizerEvent { + const requestId = randomUUID(); + const path = req.path; + const httpMethod = req.method as APIGatewayProxyWithCognitoAuthorizerEvent['httpMethod']; + + // API Gateway passes query string as a single string and also as key-value object + const queryStringParameters: Record | null = + req.query && Object.keys(req.query).length > 0 + ? (Object.fromEntries( + Object.entries(req.query).map(([k, v]) => [k, Array.isArray(v) ? v[0] : String(v ?? '')]), + ) as Record) + : null; + + const pathParameters = pathParams.id != null ? { id: pathParams.id } : null; + + // Headers: API Gateway uses lowercase keys; multi-value headers may be comma-joined + const headers: Record = {}; + for (const [key, value] of Object.entries(req.headers)) { + if (value !== undefined) { + headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value); + } + } + + let body: string | null = null; + if (options?.rawBody !== undefined) { + body = options.rawBody; + } else if (req.body !== undefined && req.body !== null) { + body = typeof req.body === 'string' ? req.body : JSON.stringify(req.body); + } + + const event: APIGatewayProxyWithCognitoAuthorizerEvent = { + httpMethod, + path, + pathParameters, + queryStringParameters, + multiValueQueryStringParameters: null, + headers, + multiValueHeaders: {}, + body, + isBase64Encoded: false, + requestContext: { + accountId: 'local', + apiId: 'local', + authorizer: { + claims: {}, + }, + protocol: req.protocol ?? 'HTTP/1.1', + httpMethod, + path, + stage: 'local', + requestId, + extendedRequestId: `local-${requestId}`, + requestTimeEpoch: Date.now(), + resourceId: 'local', + resourcePath: path, + identity: { + accessKey: null, + accountId: null, + apiKey: null, + apiKeyId: null, + caller: null, + clientCert: null, + cognitoAuthenticationProvider: null, + cognitoAuthenticationType: null, + cognitoIdentityId: null, + cognitoIdentityPoolId: null, + principalOrgId: null, + sourceIp: req.ip ?? req.socket?.remoteAddress ?? '127.0.0.1', + user: null, + userAgent: req.get('user-agent') ?? '', + userArn: null, + }, + }, + resource: path, + }; + + return event; +} diff --git a/packages/back-end/src/local-server/index.ts b/packages/back-end/src/local-server/index.ts new file mode 100644 index 00000000..2ba7fdc3 --- /dev/null +++ b/packages/back-end/src/local-server/index.ts @@ -0,0 +1,123 @@ +/** + * Local API server: runs Lambda handlers behind Express, using real AWS (DynamoDB, Cognito, etc.) + * via .env.local. Start with: pnpm run local-server (from packages/back-end). + */ + +import path from 'path'; +import { ACCESS_CONTROL_ALLOW_HEADERS } from '@easy-genomics/shared-lib/src/app/utils/common'; +import dotenv from 'dotenv'; +import express, { type Request, type Response } from 'express'; +import { runAuth, attachClaimsToEvent, sendUnauthorizedIfNeeded } from './auth-middleware'; +import { buildApiGatewayEvent } from './event-builder'; +import { invokeHandler } from './lambda-invoker'; +import { getRouteRegistry } from './route-registry'; + +const ENV_LOCAL_PATH = path.resolve(__dirname, '../../.env.local'); + +const REQUIRED_ENV = [ + 'NAME_PREFIX', + 'ACCOUNT_ID', + 'REGION', + 'COGNITO_USER_POOL_ID', + 'COGNITO_USER_POOL_CLIENT_ID', +] as const; + +function loadEnv(): void { + dotenv.config({ path: ENV_LOCAL_PATH }); + // AWS SDK (DynamoDB, S3, etc.) uses AWS_REGION; .env.local uses REGION + if (process.env.REGION && !process.env.AWS_REGION) { + process.env.AWS_REGION = process.env.REGION; + } + const missing = REQUIRED_ENV.filter((k) => !process.env[k]); + if (missing.length > 0) { + console.error( + `Missing required env vars (set in packages/back-end/.env.local): ${missing.join(', ')}.\n` + + 'See packages/back-end/.env.local.example for the full list.', + ); + process.exit(1); + } +} + +function corsMiddleware(_req: Request, res: Response, next: () => void): void { + res.setHeader('Access-Control-Allow-Origin', _req.headers.origin || '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', ACCESS_CONTROL_ALLOW_HEADERS.join(',')); + res.setHeader('Access-Control-Allow-Credentials', 'true'); + if (_req.method === 'OPTIONS') { + res.status(204).end(); + return; + } + next(); +} + +function sendLambdaResponse( + res: Response, + result: { statusCode: number; headers?: Record; body: string }, +): void { + res.status(result.statusCode); + // So the frontend can show "running locally" (only set by the local server, not API Gateway) + res.setHeader('X-Easy-Genomics-Backend', 'local'); + if (result.headers) { + for (const [k, v] of Object.entries(result.headers)) { + res.setHeader(k, v); + } + } + res.send(result.body); +} + +function main(): void { + loadEnv(); + + const app = express(); + app.use(express.json({ limit: '10mb' })); + app.use(corsMiddleware); + + const routes = getRouteRegistry(); + + for (const route of routes) { + const method = route.method.toLowerCase() as 'get' | 'post' | 'put' | 'patch' | 'delete'; + app[method](route.pathPattern, async (req: Request, res: Response) => { + try { + const authResult = await runAuth(req.method, req.path, route.hasIdParam, req.headers.authorization); + if (sendUnauthorizedIfNeeded(res, authResult)) return; + + const pathParams = { id: req.params?.id }; + const rawBody = + typeof req.body === 'string' + ? req.body + : req.body && Object.keys(req.body).length > 0 + ? JSON.stringify(req.body) + : undefined; + const event = buildApiGatewayEvent(req, pathParams, { rawBody }); + + if (authResult.authorized && authResult.claims && Object.keys(authResult.claims).length > 0) { + attachClaimsToEvent(event, authResult.claims); + } + + const result = await invokeHandler(route.handlerPath, event); + sendLambdaResponse(res, result); + } catch (err) { + console.error(`[local-server] ${route.method} ${route.pathPattern}`, err); + res.status(500).json({ + Error: err instanceof Error ? err.message : 'Internal server error', + ErrorCode: 'EG-100', + }); + } + }); + } + + app.use((err: Error, _req: Request, res: Response) => { + console.error('[local-server] Unhandled error', err); + res.status(500).json({ + Error: err.message || 'Internal server error', + ErrorCode: 'EG-100', + }); + }); + + const port = Number(process.env.LOCAL_SERVER_PORT) || 3001; + app.listen(port, () => { + console.log(`Local server listening on http://localhost:${port}`); + }); +} + +main(); diff --git a/packages/back-end/src/local-server/invoke-process-handler.ts b/packages/back-end/src/local-server/invoke-process-handler.ts new file mode 100644 index 00000000..e78e642f --- /dev/null +++ b/packages/back-end/src/local-server/invoke-process-handler.ts @@ -0,0 +1,70 @@ +/** + * Optional: Manually invoke a process-* (or any) Lambda handler locally with a JSON event. + * Useful for testing SQS/Cognito-triggered handlers without deploying. + * + * Usage: + * pnpm run invoke-process-handler -- + * Example: + * pnpm run invoke-process-handler -- src/app/controllers/easy-genomics/user/process-create-user-invites.lambda.ts ./sample-sqs-event.json + * + * The event file should be valid JSON (e.g. an SQSEvent or Cognito trigger event). + * .env.local is loaded so the handler can access AWS resources. + */ + +import fs from 'fs'; +import path from 'path'; +import { pathToFileURL } from 'url'; +import dotenv from 'dotenv'; +const ENV_LOCAL_PATH = path.resolve(__dirname, '../../.env.local'); + +function main(): void { + dotenv.config({ path: ENV_LOCAL_PATH }); + if (process.env.REGION && !process.env.AWS_REGION) { + process.env.AWS_REGION = process.env.REGION; + } + + const args = process.argv.slice(2); + if (args.length < 2) { + console.error( + 'Usage: pnpm run invoke-process-handler -- \n' + + 'Example: pnpm run invoke-process-handler -- src/app/controllers/easy-genomics/user/process-create-user-invites.lambda.ts ./sample-sqs-event.json', + ); + process.exit(1); + } + + const [handlerPathArg, eventPathArg] = args; + const handlerPath = path.resolve(process.cwd(), handlerPathArg); + const eventPath = path.resolve(process.cwd(), eventPathArg); + + if (!fs.existsSync(handlerPath)) { + console.error(`Handler file not found: ${handlerPath}`); + process.exit(1); + } + if (!fs.existsSync(eventPath)) { + console.error(`Event file not found: ${eventPath}`); + process.exit(1); + } + + const eventJson = fs.readFileSync(eventPath, 'utf8'); + const event = JSON.parse(eventJson); + + void (async () => { + try { + // process-* handlers take SQS or Cognito events, not APIGatewayProxy*. We still use + // the same dynamic import; the handler signature is (event: SQSEvent | ...) => Promise<...>. + const moduleURL = pathToFileURL(handlerPath).href; + const mod = await import(moduleURL); + if (typeof mod.handler !== 'function') { + console.error(`Module did not export "handler": ${handlerPath}`); + process.exit(1); + } + const result = await mod.handler(event); + console.log('Result:', JSON.stringify(result, null, 2)); + } catch (err) { + console.error('Invocation failed:', err); + process.exit(1); + } + })(); +} + +main(); diff --git a/packages/back-end/src/local-server/lambda-invoker.ts b/packages/back-end/src/local-server/lambda-invoker.ts new file mode 100644 index 00000000..45e1b7c5 --- /dev/null +++ b/packages/back-end/src/local-server/lambda-invoker.ts @@ -0,0 +1,28 @@ +/** + * Invokes a Lambda handler locally by dynamically importing the module and calling handler(event). + */ + +import path from 'path'; +import { pathToFileURL } from 'url'; +import type { APIGatewayProxyWithCognitoAuthorizerEvent, APIGatewayProxyResult } from 'aws-lambda'; + +/** + * Dynamically import and run the Lambda handler at handlerPath. + * handlerPath should be the absolute path to the .lambda.ts file. + * Requires running with tsx (or similar) so that @BE/* and @SharedLib/* resolve via tsconfig paths. + */ +export async function invokeHandler( + handlerPath: string, + event: APIGatewayProxyWithCognitoAuthorizerEvent, +): Promise { + const resolvedPath = path.resolve(handlerPath); + const moduleURL = pathToFileURL(resolvedPath).href; + + const mod = await import(moduleURL); + if (typeof mod.handler !== 'function') { + throw new Error(`Handler module at ${resolvedPath} did not export a function named "handler"`); + } + + const result = await mod.handler(event); + return result as APIGatewayProxyResult; +} diff --git a/packages/back-end/src/local-server/route-registry.ts b/packages/back-end/src/local-server/route-registry.ts new file mode 100644 index 00000000..82648ad3 --- /dev/null +++ b/packages/back-end/src/local-server/route-registry.ts @@ -0,0 +1,125 @@ +/** + * Route registry for the local API server. + * Discovers REST Lambda handlers the same way the CDK Lambda construct does + * and builds a (method, path) → handler map for Express. + */ + +import * as fs from 'fs'; +import path from 'path'; + +const CONTROLLERS_ROOT = path.resolve(__dirname, '../app/controllers'); + +/** HTTP methods used by API Gateway / front-end */ +type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; + +/** Single route entry for the local server */ +export interface RouteEntry { + method: HttpMethod; + /** Express path pattern, e.g. /easy-genomics/organization/list-organizations or .../read-organization/:id */ + pathPattern: string; + /** Absolute path to the .lambda.ts handler file for dynamic import */ + handlerPath: string; + /** Command parsed from filename, e.g. list, read, create */ + command: string; + /** True if this route expects path param :id */ + hasIdParam: boolean; +} + +// Must match lambda-construct.ts ALLOWED_LAMBDA_FUNCTION_OPERATIONS +const COMMAND_TO_HTTP_METHOD: Record = { + create: 'POST', + confirm: 'POST', + list: 'GET', + read: 'GET', + update: 'PUT', + cancel: 'PUT', + patch: 'PATCH', + delete: 'DELETE', + add: 'POST', + edit: 'POST', + request: 'POST', + remove: 'POST', +}; + +// Commands that use path parameter {id} (must match lambda-construct.ts) +const COMMANDS_WITH_RESOURCE_ID = new Set(['read', 'update', 'cancel', 'patch', 'delete']); + +/** Controller subdirs that expose REST endpoints (excludes auth, which uses process-* and Cognito triggers) */ +const REST_CONTROLLER_DIRS = ['easy-genomics', 'aws-healthomics', 'nf-tower']; + +interface DiscoveredLambda { + path: string; + command: string; + apiEndpoint: string; // e.g. easy-genomics/organization/list-organizations +} + +/** + * Recursively find all *.lambda.ts files under dir and return path, command, and API endpoint. + * Skips process-* (no REST route). Endpoint = path relative to controllers root with forward slashes. + */ +function discoverLambdasInDir(directory: string, lambdaFunctions: DiscoveredLambda[] = []): DiscoveredLambda[] { + const files = fs.readdirSync(directory); + for (const file of files) { + const absolutePath = path.join(directory, file); + if (fs.statSync(absolutePath).isDirectory()) { + discoverLambdasInDir(absolutePath, lambdaFunctions); + } else if (file.endsWith('.lambda.ts')) { + const command = file.split('-', 1)[0]; + if (command === 'process') { + continue; // Skip process-*; no REST endpoint + } + if (!(command in COMMAND_TO_HTTP_METHOD)) { + continue; + } + const relativePath = path.relative(CONTROLLERS_ROOT, absolutePath); + const lambdaName = path.basename(file, '.lambda.ts'); + const apiDir = path.dirname(relativePath); + const apiEndpoint = path.join(apiDir, lambdaName).split(path.sep).join('/'); + lambdaFunctions.push({ path: absolutePath, command, apiEndpoint }); + } + } + return lambdaFunctions; +} + +/** + * Build the full route registry for the local server. + * Scans easy-genomics, aws-healthomics, nf-tower and returns one RouteEntry per REST endpoint. + */ +export function getRouteRegistry(): RouteEntry[] { + const entries: RouteEntry[] = []; + + for (const subdir of REST_CONTROLLER_DIRS) { + const dir = path.join(CONTROLLERS_ROOT, subdir); + if (!fs.existsSync(dir)) { + continue; + } + const lambdas = discoverLambdasInDir(dir); + for (const lambda of lambdas) { + const method = COMMAND_TO_HTTP_METHOD[lambda.command]; + const hasIdParam = COMMANDS_WITH_RESOURCE_ID.has(lambda.command); + const pathPrefix = `/${lambda.apiEndpoint}`; + const pathPattern = hasIdParam ? `${pathPrefix}/:id` : pathPrefix; + entries.push({ + method, + pathPattern, + handlerPath: lambda.path, + command: lambda.command, + hasIdParam, + }); + } + } + + return entries; +} + +/** + * Run directly to print the route registry (for verification). + * Example: npx tsx src/local-server/route-registry.ts + */ +if (require.main === module) { + const routes = getRouteRegistry(); + console.log(`Discovered ${routes.length} REST route(s):\n`); + for (const r of routes) { + console.log(` ${r.method.padEnd(6)} ${r.pathPattern}`); + } +} diff --git a/packages/front-end/env.nuxt.config.ts b/packages/front-end/env.nuxt.config.ts index 4d720a4f..50cd84bd 100644 --- a/packages/front-end/env.nuxt.config.ts +++ b/packages/front-end/env.nuxt.config.ts @@ -1,14 +1,36 @@ import * as dotenv from 'dotenv'; +import * as path from 'path'; +import * as fs from 'fs'; + +const configDir = path.resolve(__dirname, '../../config'); + +const LOCAL_BACKEND_VALUES = new Set(['1', 'true', 'yes']); +const DEFAULT_LOCAL_API_URL = 'http://localhost:3001'; /** * Load and return the following environmental variables from * {easy-genomics root dir}/config/.env.nuxt file. + * If config/.env.nuxt.local exists, it is loaded afterward and overrides values. + * + * To point at the local back-end without editing files, set USE_LOCAL_BACKEND=1 + * (or "true"/"yes") when starting the app. Optionally set LOCAL_API_URL (default + * http://localhost:3001) for a different port. */ export function loadNuxtSettings() { dotenv.config({ - path: '../../config/.env.nuxt', + path: path.join(configDir, '.env.nuxt'), }); + const localEnvPath = path.join(configDir, '.env.nuxt.local'); + if (fs.existsSync(localEnvPath)) { + dotenv.config({ path: localEnvPath }); + } + + // Env var toggle: use local back-end without renaming/editing .env.nuxt.local + if (LOCAL_BACKEND_VALUES.has(String(process.env.USE_LOCAL_BACKEND ?? '').toLowerCase())) { + process.env.AWS_API_GATEWAY_URL = (process.env.LOCAL_API_URL ?? DEFAULT_LOCAL_API_URL).replace(/\/+$/, ''); + } + if ( process.env.npm_lifecycle_script === 'nuxt dev' || process.env.npm_lifecycle_script === 'nuxt generate' || diff --git a/packages/front-end/nuxt-load-configuration-settings.ts b/packages/front-end/nuxt-load-configuration-settings.ts index ec29395f..33f169a5 100644 --- a/packages/front-end/nuxt-load-configuration-settings.ts +++ b/packages/front-end/nuxt-load-configuration-settings.ts @@ -74,45 +74,64 @@ export async function exportNuxtConfigurationSettings(awsRegion: string, envName }); } +function isCredentialsError(error: unknown): boolean { + const name = error && typeof error === 'object' && 'name' in error ? (error as { name?: string }).name : ''; + const code = error && typeof error === 'object' && 'code' in error ? (error as { code?: string }).code : ''; + return name === 'CredentialsProviderError' || code === 'CredentialsProviderError'; +} + // eslint-disable-next-line no-void void (async () => { - if (process.env.CI_CD === 'true') { - const awsRegion: string = process.env.AWS_REGION; - const envName: string = process.env.ENV_NAME; - const envType: string = process.env.ENV_TYPE; + try { + if (process.env.CI_CD === 'true') { + const awsRegion: string = process.env.AWS_REGION; + const envName: string = process.env.ENV_NAME; + const envType: string = process.env.ENV_TYPE; - await exportNuxtConfigurationSettings(awsRegion, envName, envType).catch((error) => { - throw error; - }); - } else { - // @ts-ignore - const configurations: { [p: string]: ConfigurationSettings }[] = loadConfigurations( - join(__dirname, '../../config/easy-genomics.yaml'), - ); - if (configurations.length === 0) { - throw new Error('Easy Genomics Configuration missing / invalid, please update: easy-genomics.yaml'); - } else if (configurations.length > 1) { - throw new Error('Too many Easy Genomics Configurations found, please update: easy-genomics.yaml'); + await exportNuxtConfigurationSettings(awsRegion, envName, envType); } else { - const configuration: { [p: string]: ConfigurationSettings } | undefined = configurations.shift(); + // @ts-ignore + const configurations: { [p: string]: ConfigurationSettings }[] = loadConfigurations( + join(__dirname, '../../config/easy-genomics.yaml'), + ); + if (configurations.length === 0) { + throw new Error('Easy Genomics Configuration missing / invalid, please update: easy-genomics.yaml'); + } else if (configurations.length > 1) { + throw new Error('Too many Easy Genomics Configurations found, please update: easy-genomics.yaml'); + } else { + const configuration: { [p: string]: ConfigurationSettings } | undefined = configurations.shift(); - if (configuration) { - const envName: string | undefined = Object.keys(configuration).shift(); - const configSettings: ConfigurationSettings | undefined = Object.values(configuration).shift(); + if (configuration) { + const envName: string | undefined = Object.keys(configuration).shift(); + const configSettings: ConfigurationSettings | undefined = Object.values(configuration).shift(); - if (!envName || !configSettings) { - throw new Error( - 'Easy Genomics Configuration missing / invalid, please check the easy-genomics.yaml configuration', - ); - } + if (!envName || !configSettings) { + throw new Error( + 'Easy Genomics Configuration missing / invalid, please check the easy-genomics.yaml configuration', + ); + } - const envType: string = configSettings['env-type']; // dev | pre-prod | prod - const awsRegion: string = configSettings['aws-region']; + const envType: string = configSettings['env-type']; // dev | pre-prod | prod + const awsRegion: string = configSettings['aws-region']; - await exportNuxtConfigurationSettings(awsRegion, envName, envType).catch((error) => { - throw error; - }); + await exportNuxtConfigurationSettings(awsRegion, envName, envType); + } } } + } catch (error) { + if (isCredentialsError(error)) { + console.error( + '\nAWS credentials could not be loaded. This script needs credentials to call API Gateway and Cognito.', + ); + console.error('Configure credentials using one of these methods:\n'); + console.error(' 1. Run: aws configure'); + console.error(' Then set AWS Access Key ID, Secret Access Key, and Default region (e.g. us-west-2).\n'); + console.error(' 2. If using AWS SSO, run: aws sso login'); + console.error(' Then ensure AWS_PROFILE is set or use: aws sso login --profile YOUR_PROFILE\n'); + console.error(' 3. Set environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION'); + console.error(' (and AWS_SESSION_TOKEN if using temporary credentials).\n'); + process.exit(1); + } + throw error; } })(); diff --git a/packages/front-end/package.json b/packages/front-end/package.json index 0fff0b24..d08a1e06 100644 --- a/packages/front-end/package.json +++ b/packages/front-end/package.json @@ -39,7 +39,8 @@ "test-e2e:lab-technician:headed": "USER_TYPE=lab-technician npx playwright test --project=lab-technician --ui", "nuxt-reset": "nuxt cleanup", "nftower-spec-to-zod": "pnpm typed-openapi ../shared-lib/src/app/types/nf-tower/seqera-api-latest.yml -r 'zod'", - "lint": "eslint 'src/**/*.{js,ts}' --fix" + "lint": "eslint 'src/**/*.{js,ts}' --fix", + "local-server": "USE_LOCAL_BACKEND=1 pnpm run nuxt-dev" }, "author": { "name": "DEPT Agency", @@ -47,7 +48,6 @@ }, "devDependencies": { "@aws-sdk/types": "^3.775.0", - "@iconify-json/logos": "^1.2.10", "@nuxt/types": "^2.18.1", "@nuxtjs/eslint-config-typescript": "^12.1.0", "@types/jest": "^29.5.14", diff --git a/packages/front-end/src/app/components/EGLabView.vue b/packages/front-end/src/app/components/EGLabView.vue index 98b94b7a..56ac4f68 100644 --- a/packages/front-end/src/app/components/EGLabView.vue +++ b/packages/front-end/src/app/components/EGLabView.vue @@ -51,10 +51,12 @@ const userToRemove = ref(); const missingPAT = ref(false); const tabIndex = ref(0); + /** Prevents infinite loop: only refresh lab once when HasNextFlowTowerAccessToken is null */ + const hasRefreshedLabForNullToken = ref(false); let intervalId: number | undefined; - const orgId = computed(() => labStore.labs[props.labId].OrganizationId ?? null); const lab = computed(() => labStore.labs[props.labId] ?? null); + const orgId = computed(() => lab.value?.OrganizationId ?? null); const labName = computed(() => lab.value?.Name || ''); /** @@ -566,10 +568,28 @@ setTimeout(setTabIndex, 100); // there's a slight delay to get around a race condition } - watch(lab, async (lab) => { - if (lab === null) { + /** Tracks which lab we've already run secondary fetches for; avoids re-running when watch re-fires for same reference */ + const lastProcessedLabRef = ref(null); + + // Reset guards when navigating to a different lab + watch( + () => props.labId, + () => { + hasRefreshedLabForNullToken.value = false; + lastProcessedLabRef.value = null; + }, + ); + + watch(lab, async (newLab) => { + if (newLab === null) { + return; + } + + // Avoid running secondary fetches multiple times for the same lab object (e.g. re-entrant or duplicate watch runs) + if (lastProcessedLabRef.value === newLab) { return; } + lastProcessedLabRef.value = newLab; const promises = [getLabUsers()]; @@ -579,15 +599,17 @@ return; } - if (lab.NextFlowTowerEnabled) { - if (lab.HasNextFlowTowerAccessToken == null) { - // Current lab doesn't have the correct details + if (newLab.NextFlowTowerEnabled) { + if (newLab.HasNextFlowTowerAccessToken == null) { + // Current lab doesn't have the correct details — refresh at most once to avoid infinite loop if (uiStore.isRequestPending('loadLabData')) { // In the process of loading the lab, which will trigger this code again when it completes - } else { - loadLabData(); // Refresh the lab + } else if (!hasRefreshedLabForNullToken.value) { + hasRefreshedLabForNullToken.value = true; + await loadLabData(); + // loadLabData() assigns a new lab in the store; watch will run again with that new reference and then run the fetches below (lastProcessedLabRef will differ) } - } else if (!lab.HasNextFlowTowerAccessToken) { + } else if (!newLab.HasNextFlowTowerAccessToken) { // Seqera enabled but creds not present, show the modal missingPAT.value = true; showRedirectModal(); @@ -599,7 +621,7 @@ } } - if (lab.AwsHealthOmicsEnabled) { + if (newLab.AwsHealthOmicsEnabled) { // fetch the Omics stuff promises.push(getOmicsWorkflows()); promises.push(getOmicsRuns()); diff --git a/packages/front-end/src/app/pages/labs/[labId]/run/[labRunId].vue b/packages/front-end/src/app/pages/labs/[labId]/run/[labRunId].vue index 327e1c63..6a823a27 100644 --- a/packages/front-end/src/app/pages/labs/[labId]/run/[labRunId].vue +++ b/packages/front-end/src/app/pages/labs/[labId]/run/[labRunId].vue @@ -159,6 +159,7 @@
@@ -175,7 +176,7 @@
{{ pipelineOrWorkflow }} Run Status
- +
@@ -205,7 +206,7 @@ =18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.17.19': resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} @@ -2295,6 +2310,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.17.19': resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} engines: {node: '>=12'} @@ -2319,6 +2340,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.17.19': resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} engines: {node: '>=12'} @@ -2343,6 +2370,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.17.19': resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} engines: {node: '>=12'} @@ -2367,6 +2400,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.17.19': resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} engines: {node: '>=12'} @@ -2391,6 +2430,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.17.19': resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} engines: {node: '>=12'} @@ -2415,6 +2460,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} engines: {node: '>=12'} @@ -2439,6 +2490,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.17.19': resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} engines: {node: '>=12'} @@ -2463,6 +2520,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.17.19': resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} engines: {node: '>=12'} @@ -2487,6 +2550,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.17.19': resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} engines: {node: '>=12'} @@ -2511,6 +2580,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.17.19': resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} engines: {node: '>=12'} @@ -2535,6 +2610,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.17.19': resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} engines: {node: '>=12'} @@ -2559,6 +2640,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.17.19': resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} engines: {node: '>=12'} @@ -2583,6 +2670,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.17.19': resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} engines: {node: '>=12'} @@ -2607,6 +2700,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.17.19': resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} engines: {node: '>=12'} @@ -2631,6 +2730,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.17.19': resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} engines: {node: '>=12'} @@ -2655,6 +2760,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.24.2': resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} engines: {node: '>=18'} @@ -2667,6 +2778,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.19': resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} engines: {node: '>=12'} @@ -2691,6 +2808,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.24.2': resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} engines: {node: '>=18'} @@ -2703,6 +2826,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} engines: {node: '>=12'} @@ -2727,6 +2856,18 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.17.19': resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} engines: {node: '>=12'} @@ -2751,6 +2892,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.17.19': resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} engines: {node: '>=12'} @@ -2775,6 +2922,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.17.19': resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} engines: {node: '>=12'} @@ -2799,6 +2952,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.17.19': resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} engines: {node: '>=12'} @@ -2823,6 +2982,12 @@ packages: cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.5.1': resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2879,9 +3044,6 @@ packages: '@iconify-json/heroicons@1.2.2': resolution: {integrity: sha512-qoW4pXr5kTTL6juEjgTs83OJIwpePu7q1tdtKVEdj+i0zyyVHgg/dd9grsXJQnpTpBt6/VwNjrXBvFjRsKPENg==} - '@iconify-json/logos@1.2.10': - resolution: {integrity: sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA==} - '@iconify/collections@1.0.536': resolution: {integrity: sha512-ih/SpVV6+U/4YU0lasSOwqh8+EJn8UPA09tJmH4pH6jIkqbH1aq+U70NaK/DFDmYJlzGERJjDWmUcQWEbLob/A==} @@ -4119,11 +4281,11 @@ packages: '@types/etag@1.8.3': resolution: {integrity: sha512-QYHv9Yeh1ZYSMPQOoxY4XC4F1r+xRUiAriB303F4G6uBsT3KKX60DjiogvVv+2VISVDuJhcIzMdbjT+Bm938QQ==} - '@types/express-serve-static-core@5.0.6': - resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} - '@types/express@5.0.1': - resolution: {integrity: sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==} + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} '@types/file-loader@5.0.4': resolution: {integrity: sha512-aB4X92oi5D2nIGI8/kolnJ47btRM2MQjQS4eJgA/VnCD12x0+kP5v7b5beVQWKHLOcquwUXvv6aMt8PmMy9uug==} @@ -4837,6 +4999,9 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -4947,6 +5112,10 @@ packages: engines: {node: '>= 14.15.0'} hasBin: true + aws-jwt-verify@4.0.1: + resolution: {integrity: sha512-kzvi71eD3w/mCpYRUY7cz6DX4bfYihGdI2yV3FYQ2JuZZenqAqDPz0gWj0ew6vlAtdEVBNb7p+Dm2TAIxpVYMA==} + engines: {node: '>=14.0.0'} + aws-lambda@1.0.7: resolution: {integrity: sha512-9GNFMRrEMG5y3Jvv+V4azWvc+qNWdWLTjDdhf/zgMlz8haaaLWv0xeAIWxz9PuWUBawsVxy0zZotjCdR3Xq+2w==} hasBin: true @@ -5053,6 +5222,10 @@ packages: bn.js@5.2.1: resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -5483,10 +5656,17 @@ packages: cookie-es@2.0.0: resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -6053,6 +6233,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -6311,6 +6496,10 @@ packages: exponential-backoff@3.1.2: resolution: {integrity: sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==} + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + exsolve@1.0.4: resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} @@ -6407,6 +6596,10 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + find-cache-dir@2.1.0: resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} engines: {node: '>=6'} @@ -6476,6 +6669,10 @@ packages: resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -6773,6 +6970,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-shutdown@1.2.2: resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -6895,6 +7096,10 @@ packages: resolution: {integrity: sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==} engines: {node: '>=12.22.0'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -7721,6 +7926,9 @@ packages: resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} engines: {node: '>=10'} + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -8364,6 +8572,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -8821,6 +9032,10 @@ packages: protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -8874,6 +9089,10 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -9395,6 +9614,10 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -9765,6 +9988,11 @@ packages: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} engines: {node: '>=0.6.x'} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -14273,6 +14501,9 @@ snapshots: '@esbuild/aix-ppc64@0.25.2': optional: true + '@esbuild/aix-ppc64@0.27.3': + optional: true + '@esbuild/android-arm64@0.17.19': optional: true @@ -14285,6 +14516,9 @@ snapshots: '@esbuild/android-arm64@0.25.2': optional: true + '@esbuild/android-arm64@0.27.3': + optional: true + '@esbuild/android-arm@0.17.19': optional: true @@ -14297,6 +14531,9 @@ snapshots: '@esbuild/android-arm@0.25.2': optional: true + '@esbuild/android-arm@0.27.3': + optional: true + '@esbuild/android-x64@0.17.19': optional: true @@ -14309,6 +14546,9 @@ snapshots: '@esbuild/android-x64@0.25.2': optional: true + '@esbuild/android-x64@0.27.3': + optional: true + '@esbuild/darwin-arm64@0.17.19': optional: true @@ -14321,6 +14561,9 @@ snapshots: '@esbuild/darwin-arm64@0.25.2': optional: true + '@esbuild/darwin-arm64@0.27.3': + optional: true + '@esbuild/darwin-x64@0.17.19': optional: true @@ -14333,6 +14576,9 @@ snapshots: '@esbuild/darwin-x64@0.25.2': optional: true + '@esbuild/darwin-x64@0.27.3': + optional: true + '@esbuild/freebsd-arm64@0.17.19': optional: true @@ -14345,6 +14591,9 @@ snapshots: '@esbuild/freebsd-arm64@0.25.2': optional: true + '@esbuild/freebsd-arm64@0.27.3': + optional: true + '@esbuild/freebsd-x64@0.17.19': optional: true @@ -14357,6 +14606,9 @@ snapshots: '@esbuild/freebsd-x64@0.25.2': optional: true + '@esbuild/freebsd-x64@0.27.3': + optional: true + '@esbuild/linux-arm64@0.17.19': optional: true @@ -14369,6 +14621,9 @@ snapshots: '@esbuild/linux-arm64@0.25.2': optional: true + '@esbuild/linux-arm64@0.27.3': + optional: true + '@esbuild/linux-arm@0.17.19': optional: true @@ -14381,6 +14636,9 @@ snapshots: '@esbuild/linux-arm@0.25.2': optional: true + '@esbuild/linux-arm@0.27.3': + optional: true + '@esbuild/linux-ia32@0.17.19': optional: true @@ -14393,6 +14651,9 @@ snapshots: '@esbuild/linux-ia32@0.25.2': optional: true + '@esbuild/linux-ia32@0.27.3': + optional: true + '@esbuild/linux-loong64@0.17.19': optional: true @@ -14405,6 +14666,9 @@ snapshots: '@esbuild/linux-loong64@0.25.2': optional: true + '@esbuild/linux-loong64@0.27.3': + optional: true + '@esbuild/linux-mips64el@0.17.19': optional: true @@ -14417,6 +14681,9 @@ snapshots: '@esbuild/linux-mips64el@0.25.2': optional: true + '@esbuild/linux-mips64el@0.27.3': + optional: true + '@esbuild/linux-ppc64@0.17.19': optional: true @@ -14429,6 +14696,9 @@ snapshots: '@esbuild/linux-ppc64@0.25.2': optional: true + '@esbuild/linux-ppc64@0.27.3': + optional: true + '@esbuild/linux-riscv64@0.17.19': optional: true @@ -14441,6 +14711,9 @@ snapshots: '@esbuild/linux-riscv64@0.25.2': optional: true + '@esbuild/linux-riscv64@0.27.3': + optional: true + '@esbuild/linux-s390x@0.17.19': optional: true @@ -14453,6 +14726,9 @@ snapshots: '@esbuild/linux-s390x@0.25.2': optional: true + '@esbuild/linux-s390x@0.27.3': + optional: true + '@esbuild/linux-x64@0.17.19': optional: true @@ -14465,12 +14741,18 @@ snapshots: '@esbuild/linux-x64@0.25.2': optional: true + '@esbuild/linux-x64@0.27.3': + optional: true + '@esbuild/netbsd-arm64@0.24.2': optional: true '@esbuild/netbsd-arm64@0.25.2': optional: true + '@esbuild/netbsd-arm64@0.27.3': + optional: true + '@esbuild/netbsd-x64@0.17.19': optional: true @@ -14483,12 +14765,18 @@ snapshots: '@esbuild/netbsd-x64@0.25.2': optional: true + '@esbuild/netbsd-x64@0.27.3': + optional: true + '@esbuild/openbsd-arm64@0.24.2': optional: true '@esbuild/openbsd-arm64@0.25.2': optional: true + '@esbuild/openbsd-arm64@0.27.3': + optional: true + '@esbuild/openbsd-x64@0.17.19': optional: true @@ -14501,6 +14789,12 @@ snapshots: '@esbuild/openbsd-x64@0.25.2': optional: true + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + '@esbuild/sunos-x64@0.17.19': optional: true @@ -14513,6 +14807,9 @@ snapshots: '@esbuild/sunos-x64@0.25.2': optional: true + '@esbuild/sunos-x64@0.27.3': + optional: true + '@esbuild/win32-arm64@0.17.19': optional: true @@ -14525,6 +14822,9 @@ snapshots: '@esbuild/win32-arm64@0.25.2': optional: true + '@esbuild/win32-arm64@0.27.3': + optional: true + '@esbuild/win32-ia32@0.17.19': optional: true @@ -14537,6 +14837,9 @@ snapshots: '@esbuild/win32-ia32@0.25.2': optional: true + '@esbuild/win32-ia32@0.27.3': + optional: true + '@esbuild/win32-x64@0.17.19': optional: true @@ -14549,6 +14852,9 @@ snapshots: '@esbuild/win32-x64@0.25.2': optional: true + '@esbuild/win32-x64@0.27.3': + optional: true + '@eslint-community/eslint-utils@4.5.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -14605,10 +14911,6 @@ snapshots: dependencies: '@iconify/types': 2.0.0 - '@iconify-json/logos@1.2.10': - dependencies: - '@iconify/types': 2.0.0 - '@iconify/collections@1.0.536': dependencies: '@iconify/types': 2.0.0 @@ -15049,23 +15351,23 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))': dependencies: '@nuxt/kit': 3.15.2(magicast@0.3.5)(rollup@4.39.0) '@nuxt/schema': 3.15.2 execa: 7.2.0 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) transitivePeerDependencies: - magicast - rollup - supports-color - '@nuxt/devtools-kit@2.3.2(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))': + '@nuxt/devtools-kit@2.3.2(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))': dependencies: '@nuxt/kit': 3.16.2(magicast@0.3.5) '@nuxt/schema': 3.16.2 execa: 8.0.1 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) transitivePeerDependencies: - magicast @@ -15082,13 +15384,13 @@ snapshots: rc9: 2.1.2 semver: 7.7.1 - '@nuxt/devtools@1.7.0(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@nuxt/devtools@1.7.0(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) '@nuxt/devtools-wizard': 1.7.0 '@nuxt/kit': 3.15.2(magicast@0.3.5)(rollup@4.39.0) - '@vue/devtools-core': 7.6.8(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) + '@vue/devtools-core': 7.6.8(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.4.2 @@ -15117,9 +15419,9 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.10 unimport: 3.14.6(rollup@4.39.0) - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.2(magicast@0.3.5)(rollup@4.39.0))(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) - vite-plugin-vue-inspector: 5.3.1(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.2(magicast@0.3.5)(rollup@4.39.0))(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) + vite-plugin-vue-inspector: 5.3.1(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) which: 3.0.1 ws: 8.18.1 transitivePeerDependencies: @@ -15129,13 +15431,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/icon@1.11.0(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@nuxt/icon@1.11.0(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@iconify/collections': 1.0.536 '@iconify/types': 2.0.0 '@iconify/utils': 2.3.0 '@iconify/vue': 4.3.0(vue@3.5.13(typescript@5.8.3)) - '@nuxt/devtools-kit': 2.3.2(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) + '@nuxt/devtools-kit': 2.3.2(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) '@nuxt/kit': 3.16.2(magicast@0.3.5) consola: 3.4.2 local-pkg: 1.1.1 @@ -15255,12 +15557,12 @@ snapshots: '@types/webpack-bundle-analyzer': 3.9.5 '@types/webpack-hot-middleware': 2.25.5 - '@nuxt/ui@2.18.4(axios@1.8.4)(change-case@4.1.2)(jwt-decode@4.0.0)(magicast@0.3.5)(qrcode@1.5.0)(ts-node@10.9.2(@types/node@20.17.30)(typescript@5.8.3))(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@nuxt/ui@2.18.4(axios@1.8.4)(change-case@4.1.2)(jwt-decode@4.0.0)(magicast@0.3.5)(qrcode@1.5.0)(ts-node@10.9.2(@types/node@20.17.30)(typescript@5.8.3))(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@headlessui/tailwindcss': 0.2.2(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@20.17.30)(typescript@5.8.3))) '@headlessui/vue': 1.7.23(vue@3.5.13(typescript@5.8.3)) '@iconify-json/heroicons': 1.2.2 - '@nuxt/icon': 1.11.0(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) + '@nuxt/icon': 1.11.0(magicast@0.3.5)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) '@nuxt/kit': 3.16.2(magicast@0.3.5) '@nuxtjs/color-mode': 3.5.2(magicast@0.3.5) '@nuxtjs/tailwindcss': 6.13.2(magicast@0.3.5)(ts-node@10.9.2(@types/node@20.17.30)(typescript@5.8.3)) @@ -15298,12 +15600,12 @@ snapshots: - vite - vue - '@nuxt/vite-builder@3.15.2(@types/node@20.17.30)(eslint@8.57.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))(yaml@2.7.1)': + '@nuxt/vite-builder@3.15.2(@types/node@20.17.30)(eslint@8.57.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))(yaml@2.7.1)': dependencies: '@nuxt/kit': 3.15.2(magicast@0.3.5)(rollup@4.39.0) '@rollup/plugin-replace': 6.0.2(rollup@4.39.0) - '@vitejs/plugin-vue': 5.2.3(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) - '@vitejs/plugin-vue-jsx': 4.1.2(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) + '@vitejs/plugin-vue': 5.2.3(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) + '@vitejs/plugin-vue-jsx': 4.1.2(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) autoprefixer: 10.4.21(postcss@8.5.3) consola: 3.4.2 cssnano: 7.0.6(postcss@8.5.3) @@ -15327,9 +15629,9 @@ snapshots: ufo: 1.6.1 unenv: 1.10.0 unplugin: 2.3.0 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) vite-node: 2.1.9(@types/node@20.17.30)(sass@1.86.3)(terser@5.39.0) - vite-plugin-checker: 0.8.0(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) + vite-plugin-checker: 0.8.0(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) vue: 3.5.13(typescript@5.8.3) vue-bundle-renderer: 2.1.1 transitivePeerDependencies: @@ -16579,7 +16881,7 @@ snapshots: '@types/compression@1.7.5': dependencies: - '@types/express': 5.0.1 + '@types/express': 4.17.25 '@types/connect@3.4.38': dependencies: @@ -16602,17 +16904,18 @@ snapshots: dependencies: '@types/node': 20.17.30 - '@types/express-serve-static-core@5.0.6': + '@types/express-serve-static-core@4.19.8': dependencies: '@types/node': 20.17.30 '@types/qs': 6.9.18 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - '@types/express@5.0.1': + '@types/express@4.17.25': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 5.0.6 + '@types/express-serve-static-core': 4.19.8 + '@types/qs': 6.9.18 '@types/serve-static': 1.15.7 '@types/file-loader@5.0.4': @@ -17035,19 +17338,19 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-vue-jsx@4.1.2(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@vitejs/plugin-vue-jsx@4.1.2(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@babel/core': 7.26.10 '@babel/plugin-transform-typescript': 7.27.0(@babel/core@7.26.10) '@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.26.10) - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) vue: 3.5.13(typescript@5.8.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.3(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.3(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) vue: 3.5.13(typescript@5.8.3) '@vue-macros/common@1.16.1(vue@3.5.13(typescript@5.8.3))': @@ -17122,14 +17425,14 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/devtools-core@7.6.8(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@vue/devtools-core@7.6.8(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@vue/devtools-kit': 7.6.8 '@vue/devtools-shared': 7.7.2 mitt: 3.0.1 nanoid: 5.1.5 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)) + vite-hot-client: 0.2.4(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)) vue: 3.5.13(typescript@5.8.3) transitivePeerDependencies: - vite @@ -17230,13 +17533,13 @@ snapshots: '@vueuse/metadata@11.3.0': {} - '@vueuse/nuxt@11.3.0(magicast@0.3.5)(nuxt@3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': + '@vueuse/nuxt@11.3.0(magicast@0.3.5)(nuxt@3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))': dependencies: '@nuxt/kit': 3.16.2(magicast@0.3.5) '@vueuse/core': 11.3.0(vue@3.5.13(typescript@5.8.3)) '@vueuse/metadata': 11.3.0 local-pkg: 0.5.1 - nuxt: 3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1) + nuxt: 3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1) vue-demi: 0.14.10(vue@3.5.13(typescript@5.8.3)) transitivePeerDependencies: - '@vue/composition-api' @@ -17434,6 +17737,8 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 + array-flatten@1.1.1: {} + array-ify@1.0.0: {} array-includes@3.1.8: @@ -17566,6 +17871,8 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + aws-jwt-verify@4.0.1: {} + aws-lambda@1.0.7: dependencies: aws-sdk: 2.1692.0 @@ -17732,6 +18039,23 @@ snapshots: bn.js@5.2.1: {} + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + boolbase@1.0.0: {} bowser@2.11.0: {} @@ -18212,8 +18536,12 @@ snapshots: cookie-es@2.0.0: {} + cookie-signature@1.0.7: {} + cookie@0.4.2: {} + cookie@0.7.2: {} + cookie@1.0.2: {} cookies@0.9.1: @@ -18855,6 +19183,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.2 '@esbuild/win32-x64': 0.25.2 + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -19219,6 +19576,42 @@ snapshots: exponential-backoff@3.1.2: {} + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.4: {} external-editor@3.1.0: @@ -19324,6 +19717,18 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + find-cache-dir@2.1.0: dependencies: commondir: 1.0.1 @@ -19397,6 +19802,8 @@ snapshots: es-set-tostringtag: 2.1.0 mime-types: 2.1.35 + forwarded@0.2.0: {} + fraction.js@4.3.7: {} fresh@0.5.2: {} @@ -19740,6 +20147,14 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-shutdown@1.2.2: {} https-proxy-agent@7.0.6: @@ -19870,6 +20285,8 @@ snapshots: transitivePeerDependencies: - supports-color + ipaddr.js@1.9.1: {} + iron-webcrypto@1.2.1: {} is-arguments@1.2.0: @@ -20915,6 +21332,8 @@ snapshots: type-fest: 0.18.1 yargs-parser: 20.2.9 + merge-descriptors@1.0.3: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -21415,15 +21834,15 @@ snapshots: nullthrows@1.1.1: {} - nuxt@3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1): + nuxt@3.15.2(@parcel/watcher@2.5.1)(@types/node@20.17.30)(db0@0.3.1)(eslint@8.57.1)(ioredis@5.6.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(xml2js@0.6.2)(yaml@2.7.1): dependencies: '@nuxt/cli': 3.24.1(magicast@0.3.5) '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) + '@nuxt/devtools': 1.7.0(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3)) '@nuxt/kit': 3.15.2(magicast@0.3.5)(rollup@4.39.0) '@nuxt/schema': 3.15.2 '@nuxt/telemetry': 2.6.6(magicast@0.3.5) - '@nuxt/vite-builder': 3.15.2(@types/node@20.17.30)(eslint@8.57.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))(yaml@2.7.1) + '@nuxt/vite-builder': 3.15.2(@types/node@20.17.30)(eslint@8.57.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.39.0)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3))(yaml@2.7.1) '@unhead/dom': 1.11.20 '@unhead/shared': 1.11.20 '@unhead/ssr': 1.11.20 @@ -21882,6 +22301,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@0.1.12: {} + path-to-regexp@6.3.0: {} path-type@4.0.0: {} @@ -22241,6 +22662,11 @@ snapshots: protocols@2.0.2: {} + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + proxy-from-env@1.1.0: {} punycode@1.3.2: {} @@ -22282,6 +22708,13 @@ snapshots: range-parser@1.2.1: {} + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -22906,6 +23339,8 @@ snapshots: statuses@2.0.1: {} + statuses@2.0.2: {} + std-env@3.9.0: {} stream-browserify@3.0.0: @@ -23329,6 +23764,13 @@ snapshots: tsscmp@1.0.6: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.3 + get-tsconfig: 4.10.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -23740,9 +24182,9 @@ snapshots: vary@1.1.2: {} - vite-hot-client@0.2.4(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)): + vite-hot-client@0.2.4(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)): dependencies: - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) vite-node@2.1.9(@types/node@20.17.30)(sass@1.86.3)(terser@5.39.0): dependencies: @@ -23762,7 +24204,7 @@ snapshots: - supports-color - terser - vite-plugin-checker@0.8.0(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)): + vite-plugin-checker@0.8.0(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)): dependencies: '@babel/code-frame': 7.26.2 ansi-escapes: 4.3.2 @@ -23774,7 +24216,7 @@ snapshots: npm-run-path: 4.0.1 strip-ansi: 6.0.1 tiny-invariant: 1.3.3 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) vscode-languageclient: 7.0.0 vscode-languageserver: 7.0.0 vscode-languageserver-textdocument: 1.0.12 @@ -23784,7 +24226,7 @@ snapshots: optionator: 0.9.4 typescript: 5.8.3 - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.2(magicast@0.3.5)(rollup@4.39.0))(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.2(magicast@0.3.5)(rollup@4.39.0))(rollup@4.39.0)(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.4(rollup@4.39.0) @@ -23795,14 +24237,14 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 3.0.1 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) optionalDependencies: '@nuxt/kit': 3.15.2(magicast@0.3.5)(rollup@4.39.0) transitivePeerDependencies: - rollup - supports-color - vite-plugin-vue-inspector@5.3.1(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)): + vite-plugin-vue-inspector@5.3.1(vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1)): dependencies: '@babel/core': 7.26.10 '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.10) @@ -23813,7 +24255,7 @@ snapshots: '@vue/compiler-dom': 3.5.13 kolorist: 1.8.0 magic-string: 0.30.17 - vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1) + vite: 6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1) transitivePeerDependencies: - supports-color @@ -23828,7 +24270,7 @@ snapshots: sass: 1.86.3 terser: 5.39.0 - vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1): + vite@6.2.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.86.3)(terser@5.39.0)(tsx@4.21.0)(yaml@2.7.1): dependencies: esbuild: 0.25.2 postcss: 8.5.3 @@ -23839,6 +24281,7 @@ snapshots: jiti: 2.4.2 sass: 1.86.3 terser: 5.39.0 + tsx: 4.21.0 yaml: 2.7.1 vlq@1.0.1: {}