CVE-2025-53832
Description
Lara Translate MCP Server is a Model Context Protocol (MCP) Server for Lara Translate API. Versions 0.0.11 and below contain a command injection vulnerability which exists in the @translated/lara-mcp MCP Server. The vulnerability is caused by the unsanitized use of input parameters within a call to child_process.exec, enabling an attacker to inject arbitrary system commands. Successful exploitation can lead to remote code execution under the server process's privileges. The server constructs and executes shell commands using unvalidated user input directly within command-line strings. This introduces the possibility of shell metacharacter injection (|, >, &&, etc.). This vulnerability is fixed in version 0.0.12.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@translated/lara-mcpnpm | < 0.0.12 | 0.0.12 |
Affected products
1- Range: v0.0.1, v0.0.10, v0.0.11, …
Patches
2d46169616dcae534ef690adfStreammable HTTP events (#10)
46 files changed · +2078 −1004
docker-compose.yaml+21 −0 added@@ -0,0 +1,21 @@ +services: + lara-mcp: + build: + context: . + dockerfile: Dockerfile + container_name: lara-mcp + restart: unless-stopped + working_dir: /opt + command: ["pnpm", "--reporter=silent", "dev"] + ports: + - ${PORT}:${PORT} + env_file: + - .env + volumes: + - ./src:/opt/src + - ./.env:/opt/.env + - ./package.json:/opt/package.json + - ./pnpm-lock.yaml:/opt/pnpm-lock.yaml + - ./vitest.config.ts:/opt/vitest.config.ts + - ./nodemon.json:/opt/nodemon.json + - ./tsconfig.json:/opt/tsconfig.json \ No newline at end of file
Dockerfile+8 −20 modified@@ -1,27 +1,15 @@ -FROM node:22.13.1-alpine AS builder +FROM node:22.14.0-alpine -WORKDIR /app +ENV CI=true -COPY package.json pnpm-lock.yaml ./ -RUN npm install -g pnpm@latest-10 -RUN pnpm install --frozen-lockfile +RUN apk update && apk upgrade +RUN apk add curl +WORKDIR /opt COPY . . -RUN pnpm run test -RUN pnpm run build - - -FROM node:22.13.1-alpine AS release - -WORKDIR /app - -COPY --from=builder /app/dist /app/dist -COPY --from=builder /app/package.json /app/package.json -COPY --from=builder /app/pnpm-lock.yaml /app/pnpm-lock.yaml - -ENV NODE_ENV=production RUN npm install -g pnpm@latest-10 -RUN pnpm install --frozen-lockfile --prod +RUN pnpm install --frozen-lockfile +RUN pnpm build -ENTRYPOINT ["node", "/app/dist/index.js"] +CMD ["node", "dist/index.js"] \ No newline at end of file
.env.example+7 −0 added@@ -0,0 +1,7 @@ +# Http +HOST=0.0.0.0 +PORT=80 +TRANSPORT=http + +# Logging +LOGGING_LEVEL=info \ No newline at end of file
.gitignore+3 −0 modified@@ -18,6 +18,9 @@ coverage/* # IntelliJ out/ +# Env +.env + # mpeltonen/sbt-idea plugin .idea_modules/
nodemon.json+5 −0 added@@ -0,0 +1,5 @@ +{ + "execMap": { + "ts": "tsx" + } +} \ No newline at end of file
package.json+21 −6 modified@@ -24,10 +24,17 @@ "files": [ "dist" ], + "imports": { + "#env": "./dist/env.js", + "#exception": "./dist/exception.js", + "#logger": "./dist/logger.js", + "#rest/server": "./dist/rest/server.js", + "#mcp/server": "./dist/mcp/server.js" + }, "scripts": { - "build": "tsc", + "build": "rm -rf dist && tsc", + "dev": "nodemon src/index.ts", "start": "node dist/index.js", - "dev": "ts-node-esm src/index.ts", "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage" @@ -44,15 +51,23 @@ "translated" ], "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", + "@modelcontextprotocol/sdk": "^1.12.3", "@translated/lara": "^1.4.0", - "zod": "^3.24.2", - "zod-to-json-schema": "^3.24.5" + "cors": "^2.8.5", + "express": "^5.1.0", + "helmet": "^8.1.0", + "pino": "^9.7.0", + "zod": "^3.25.0" }, "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.3", "@types/node": "^22.13.14", + "@types/supertest": "^6.0.3", "@vitest/coverage-v8": "^3.1.1", - "ts-node": "^10.9.2", + "nodemon": "^3.1.10", + "supertest": "^7.1.1", + "tsx": "^4.20.3", "typescript": "^5.8.2", "vitest": "^3.1.1" }
pnpm-lock.yaml+1008 −513 modified@@ -9,211 +9,231 @@ importers: .: dependencies: '@modelcontextprotocol/sdk': - specifier: ^1.8.0 - version: 1.8.0 + specifier: ^1.12.3 + version: 1.13.0 '@translated/lara': specifier: ^1.4.0 - version: 1.4.0 + version: 1.5.5 + cors: + specifier: ^2.8.5 + version: 2.8.5 + express: + specifier: ^5.1.0 + version: 5.1.0 + helmet: + specifier: ^8.1.0 + version: 8.1.0 + pino: + specifier: ^9.7.0 + version: 9.7.0 zod: - specifier: ^3.24.2 - version: 3.24.2 - zod-to-json-schema: - specifier: ^3.24.5 - version: 3.24.5(zod@3.24.2) + specifier: ^3.25.0 + version: 3.25.67 devDependencies: + '@types/cors': + specifier: ^2.8.19 + version: 2.8.19 + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 '@types/node': specifier: ^22.13.14 - version: 22.13.14 + version: 22.15.32 + '@types/supertest': + specifier: ^6.0.3 + version: 6.0.3 '@vitest/coverage-v8': specifier: ^3.1.1 - version: 3.1.1(vitest@3.1.1(@types/node@22.13.14)) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@22.13.14)(typescript@5.8.2) + version: 3.2.4(vitest@3.2.4(@types/node@22.15.32)(tsx@4.20.3)) + nodemon: + specifier: ^3.1.10 + version: 3.1.10 + supertest: + specifier: ^7.1.1 + version: 7.1.1 + tsx: + specifier: ^4.20.3 + version: 4.20.3 typescript: specifier: ^5.8.2 - version: 5.8.2 + version: 5.8.3 vitest: specifier: ^3.1.1 - version: 3.1.1(@types/node@22.13.14) + version: 3.2.4(@types/node@22.15.32)(tsx@4.20.3) packages: '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.0': - resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@esbuild/aix-ppc64@0.25.2': - resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.2': - resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.2': - resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.2': - resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.2': - resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.2': - resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.2': - resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.2': - resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.2': - resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.2': - resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.2': - resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.2': - resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.2': - resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.2': - resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.2': - resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.2': - resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.2': - resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.2': - resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.2': - resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.2': - resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.2': - resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.2': - resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.2': - resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.2': - resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.2': - resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -244,189 +264,226 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@modelcontextprotocol/sdk@1.8.0': - resolution: {integrity: sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==} + '@modelcontextprotocol/sdk@1.13.0': + resolution: {integrity: sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw==} engines: {node: '>=18'} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.39.0': - resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} + '@rollup/rollup-android-arm-eabi@4.44.0': + resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.39.0': - resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} + '@rollup/rollup-android-arm64@4.44.0': + resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.39.0': - resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} + '@rollup/rollup-darwin-arm64@4.44.0': + resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.39.0': - resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} + '@rollup/rollup-darwin-x64@4.44.0': + resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.39.0': - resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} + '@rollup/rollup-freebsd-arm64@4.44.0': + resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.39.0': - resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} + '@rollup/rollup-freebsd-x64@4.44.0': + resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.39.0': - resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': + resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.39.0': - resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} + '@rollup/rollup-linux-arm-musleabihf@4.44.0': + resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.39.0': - resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} + '@rollup/rollup-linux-arm64-gnu@4.44.0': + resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.39.0': - resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} + '@rollup/rollup-linux-arm64-musl@4.44.0': + resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.39.0': - resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': + resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': - resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': + resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.39.0': - resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} + '@rollup/rollup-linux-riscv64-gnu@4.44.0': + resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.39.0': - resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} + '@rollup/rollup-linux-riscv64-musl@4.44.0': + resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.39.0': - resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} + '@rollup/rollup-linux-s390x-gnu@4.44.0': + resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.39.0': - resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} + '@rollup/rollup-linux-x64-gnu@4.44.0': + resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.39.0': - resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} + '@rollup/rollup-linux-x64-musl@4.44.0': + resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.39.0': - resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} + '@rollup/rollup-win32-arm64-msvc@4.44.0': + resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.39.0': - resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} + '@rollup/rollup-win32-ia32-msvc@4.44.0': + resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.39.0': - resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} + '@rollup/rollup-win32-x64-msvc@4.44.0': + resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} cpu: [x64] os: [win32] - '@translated/lara@1.4.0': - resolution: {integrity: sha512-LaB26hdP+5WeegThTKoxc+i0K6GM/FPiEl8CLTxlHzkWvRCxm5473xAiknUGb7KzQFjqOfKXQU6+W/9gi3EXoQ==} + '@translated/lara@1.5.5': + resolution: {integrity: sha512-xysQl0HSN87ug0749owQQfFNRw6J6E+BTSfrRny+Tf8IJPMcTiSe3WoTL/vosVWAygVzvlwhNlfptT9FTpVn3w==} engines: {node: '>=12.*'} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@5.0.6': + resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/node@22.15.32': + resolution: {integrity: sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA==} - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} - '@types/node@22.13.14': - resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==} + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} - '@vitest/coverage-v8@3.1.1': - resolution: {integrity: sha512-MgV6D2dhpD6Hp/uroUoAIvFqA8AuvXEFBC2eepG3WFc1pxTfdk1LEqqkWoWhjz+rytoqrnUUCdf6Lzco3iHkLQ==} + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + + '@vitest/coverage-v8@3.2.4': + resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: - '@vitest/browser': 3.1.1 - vitest: 3.1.1 + '@vitest/browser': 3.2.4 + vitest: 3.2.4 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.1.1': - resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/mocker@3.1.1': - resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.1.1': - resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/runner@3.1.1': - resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/snapshot@3.1.1': - resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/spy@3.1.1': - resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/utils@3.1.1': - resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -444,25 +501,47 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.3: + resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + body-parser@2.2.0: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} @@ -488,6 +567,10 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -499,6 +582,12 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.0.0: resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} engines: {node: '>= 0.6'} @@ -511,32 +600,23 @@ packages: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -556,9 +636,8 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} @@ -588,8 +667,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} @@ -599,8 +678,8 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.25.2: - resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} hasBin: true @@ -614,28 +693,53 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - eventsource-parser@3.0.1: - resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + eventsource-parser@3.0.2: + resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} engines: {node: '>=18.0.0'} - eventsource@3.0.6: - resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} engines: {node: '>= 16'} peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 + express: '>= 4.11' - express@5.0.1: - resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} engines: {node: '>= 18'} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + finalhandler@2.1.0: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} @@ -644,10 +748,14 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} engines: {node: '>= 6'} + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -672,6 +780,13 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -680,6 +795,10 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -696,6 +815,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + helmet@8.1.0: + resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} + engines: {node: '>=18.0.0'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -707,17 +830,36 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} @@ -743,8 +885,14 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - loupe@3.1.3: - resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -759,9 +907,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -794,6 +939,14 @@ packages: resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} engines: {node: '>= 0.6'} + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -802,9 +955,6 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -817,6 +967,15 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + nodemon@3.1.10: + resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==} + engines: {node: '>=10'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -825,6 +984,10 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -861,26 +1024,53 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - pkce-challenge@4.1.0: - resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.7.0: + resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==} + hasBin: true + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -889,8 +1079,19 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} - rollup@4.39.0: - resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + rollup@4.44.0: + resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -901,11 +1102,15 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true @@ -951,17 +1156,32 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} statuses@2.0.1: 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==} @@ -981,6 +1201,21 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + superagent@10.2.1: + resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} + engines: {node: '>=14.18.0'} + + supertest@7.1.1: + resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==} + engines: {node: '>=14.18.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -989,76 +1224,81 @@ packages: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinypool@1.0.2: - resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - typescript@5.8.2: - resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-node@3.1.1: - resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@6.2.6: - resolution: {integrity: sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==} + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -1097,16 +1337,16 @@ packages: yaml: optional: true - vitest@3.1.1: - resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.1 - '@vitest/ui': 3.1.1 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -1146,17 +1386,13 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - zod-to-json-schema@3.24.5: resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} peerDependencies: zod: ^3.24.1 - zod@3.24.2: - resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} snapshots: @@ -1165,98 +1401,94 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/parser@7.27.0': + '@babel/parser@7.27.5': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.27.6 - '@babel/types@7.27.0': + '@babel/types@7.27.6': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 '@bcoe/v8-coverage@1.0.2': {} - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@esbuild/aix-ppc64@0.25.2': + '@esbuild/aix-ppc64@0.25.5': optional: true - '@esbuild/android-arm64@0.25.2': + '@esbuild/android-arm64@0.25.5': optional: true - '@esbuild/android-arm@0.25.2': + '@esbuild/android-arm@0.25.5': optional: true - '@esbuild/android-x64@0.25.2': + '@esbuild/android-x64@0.25.5': optional: true - '@esbuild/darwin-arm64@0.25.2': + '@esbuild/darwin-arm64@0.25.5': optional: true - '@esbuild/darwin-x64@0.25.2': + '@esbuild/darwin-x64@0.25.5': optional: true - '@esbuild/freebsd-arm64@0.25.2': + '@esbuild/freebsd-arm64@0.25.5': optional: true - '@esbuild/freebsd-x64@0.25.2': + '@esbuild/freebsd-x64@0.25.5': optional: true - '@esbuild/linux-arm64@0.25.2': + '@esbuild/linux-arm64@0.25.5': optional: true - '@esbuild/linux-arm@0.25.2': + '@esbuild/linux-arm@0.25.5': optional: true - '@esbuild/linux-ia32@0.2 ... [truncated]
README.md+67 −37 modified@@ -10,9 +10,9 @@ A Model Context Protocol (MCP) Server for [Lara Translate](https://laratranslate - 📖 [Introduction](#-introduction) - 🛠 [Available Tools](#-available-tools) - 🚀 [Getting Started](#-getting-started) - - 📋 [Requirements](#-requirements) - - 🔌 [Installation](#-installation) -- 🧩 [Installation Engines](#-installation-engines) + - 📋 [HTTP Server](#http-server-) + - 🔌 [STDIO Server](#stdio-server-%EF%B8%8F) +- 🧪 [Verify Installation](#-verify-installation) - 💻 [Popular Clients that supports MCPs](#-popular-clients-that-supports-mcps) - 🆘 [Support](#-support) @@ -161,7 +161,7 @@ Lara also lowers the cost of using models like GPT-4 in non-English workflows. S **Inputs**: - `id` (string): ID of the memory to update -- `tmx` (file path): The path of the TMX file to upload +- `tmx_content` (string): The content of the tmx file to upload - `gzip` (boolean): Indicates if the file is compressed (.gz) **Returns**: Import details @@ -177,69 +177,87 @@ Lara also lowers the cost of using models like GPT-4 in non-English workflows. S </details> ## 🚀 Getting Started +Lara supports both the STDIO and streamable HTTP protocols. For a hassle-free setup, we recommend using the HTTP protocol. If you prefer to use STDIO, it must be installed locally on your machine. -### 📋 Requirements +You'll find setup instructions for both protocols in the sections below. -- Lara Translate API Credentials - - To get them you can refer to the [Official Documentation](https://developers.laratranslate.com/docs/getting-started#step-3---configure-your-credentials) -- An LLM client that supports Model Context Protocol (MCP), such as Claude Desktop, Cursors, or GitHub Copilot -- NPX or Docker (depending on your preferred installation method) +### HTTP Server 🌐 +<details> +<summary><strong>❌ Clients NOT supporting <code>url</code> configuration (e.g., Claude, OpenAI)</strong></summary> -### 🔌 Installation +This installation guide is intended for clients that do NOT support the url-based configuration. This option requires Node.js to be installed on your system. -#### Introduction -The installation process is standardized across all MCP clients. It involves manually adding a configuration object to your client's MCP configuration JSON file. > If you're unsure how to configure an MCP with your client, please refer to your MCP client's official documentation. -Lara Translate MCP supports multiple installation methods, including NPX and Docker. \ -Below, we'll use NPX as an example. - --- -#### Installation & Configuration - -**Step 1**: Open your client's MCP configuration JSON file with a text editor, then copy and paste the following snippet: +1. Open your client's MCP configuration JSON file with a text editor, then copy and paste the following snippet: ```json { "mcpServers": { - "lara-translate": { + "lara": { "command": "npx", "args": [ - "-y", - "@translated/lara-mcp@latest" + "mcp-remote", + "https://mcp.laratranslate.it/mcp", + "--header", + "x-lara-access-key-id: ${X_LARA_ACCESS_KEY_ID}", + "--header", + "x-lara-access-key-secret: ${X_LARA_ACCESS_KEY_SECRET}" ], "env": { - "LARA_ACCESS_KEY_ID": "<YOUR_ACCESS_KEY_ID>", - "LARA_ACCESS_KEY_SECRET": "<YOUR_ACCESS_KEY_SECRET>" + "X_LARA_ACCESS_KEY_ID": "<YOUR_ACCESS_KEY_ID>", + "X_LARA_ACCESS_KEY_SECRET": "<YOUR_ACCESS_KEY_SECRET>" } } } } ``` -**Step 2**: Replace `<YOUR_ACCESS_KEY_ID>` and `<YOUR_ACCESS_KEY_SECRET>` with your Lara Translate API credentials (refer to the [Official Documentation](https://developers.laratranslate.com/docs/getting-started#step-3---configure-your-credentials) for details). +2. Replace `<YOUR_ACCESS_KEY_ID>` and `<YOUR_ACCESS_KEY_SECRET>` with your Lara Translate API credentials. Refer to the [Official Documentation](https://developers.laratranslate.com/docs/getting-started#step-3---configure-your-credentials) for details. -**Step 3**: Restart your MCP client. +3. Restart your MCP client. ---- +</details> -#### Verify Installation +<details> +<summary><strong>✅ Clients supporting <code>url</code> configuration (e.g., Cursor, Continue)</strong></summary> -After restarting your MCP client, you should see Lara Translate MCP in the list of available MCPs. -> The method for viewing installed MCPs varies by client. Please consult your MCP client's documentation. +This installation guide is intended for clients that support the url-based configuration. These clients can connect to Lara through a remote HTTP endpoint by specifying a simple configuration object. -To verify that Lara Translate MCP is working correctly, try translating with a simple prompt: -```text -Translate with Lara "Hello world" to Spanish +Some examples of supported clients include Cursor, Continue, OpenDevin, and Aider. +> If you're unsure how to configure an MCP with your client, please refer to your MCP client's official documentation. + +--- + +1. Open your client's MCP configuration JSON file with a text editor, then copy and paste the following snippet: + +```json +{ + "mcpServers": { + "lara": { + "url": "https://mcp.laratranslate.it/mcp", + "headers": { + "x-lara-access-key-id": "<YOUR_ACCESS_KEY_ID>", + "x-lara-access-key-secret": "<YOUR_ACCESS_KEY_SECRET>" + } + } + } +} ``` -Your MCP client will begin generating a response. If Lara Translate MCP is properly installed and configured, your client will either request approval for the action or display a notification that Lara Translate is being used. +2. Replace `<YOUR_ACCESS_KEY_ID>` and `<YOUR_ACCESS_KEY_SECRET>` with your Lara Translate API credentials. Refer to the [Official Documentation](https://developers.laratranslate.com/docs/getting-started#step-3---configure-your-credentials) for details. -## 🧩 Installation Engines +3. Restart your MCP client. +</details> + +--- + +### STDIO Server 🖥️ <details> -<summary><strong>Option 1: Using NPX</strong></summary> +<summary><strong>Using NPX</strong></summary> This option requires Node.js to be installed on your system. @@ -263,7 +281,7 @@ This option requires Node.js to be installed on your system. </details> <details> -<summary><strong>Option 2: Using Docker</strong></summary> +<summary><strong>Using Docker</strong></summary> This option requires Docker to be installed on your system. @@ -296,7 +314,7 @@ This option requires Docker to be installed on your system. </details> <details> -<summary><strong>Option 3: Building from Source</strong></summary> +<summary><strong>Building from Source</strong></summary> #### Using Node.js @@ -375,6 +393,18 @@ docker build -t lara-mcp . 4. Replace `<YOUR_ACCESS_KEY_ID>` and `<YOUR_ACCESS_KEY_SECRET>` with your actual credentials. </details> +## 🧪 Verify Installation + +After restarting your MCP client, you should see Lara Translate MCP in the list of available MCPs. +> The method for viewing installed MCPs varies by client. Please consult your MCP client's documentation. + +To verify that Lara Translate MCP is working correctly, try translating with a simple prompt: +```text +Translate with Lara "Hello world" to Spanish +``` + +Your MCP client will begin generating a response. If Lara Translate MCP is properly installed and configured, your client will either request approval for the action or display a notification that Lara Translate is being used. + ## 💻 Popular Clients that supports MCPs > For a complete list of MCP clients and their feature support, visit the [official MCP clients page](https://modelcontextprotocol.io/clients).
src/env.ts+19 −0 added@@ -0,0 +1,19 @@ +import { z } from "zod/v4"; + +const envSchema = z.object({ + // Http Server + HOST: z.string().default("0.0.0.0"), + PORT: z.coerce.number().default(3000), + + // Transport + TRANSPORT: z.enum(["stdio", "http"]).default("stdio"), + + // Logging + LOGGING_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"), + + // Lara STDIO MCP Server + LARA_ACCESS_KEY_ID: z.string().optional(), + LARA_ACCESS_KEY_SECRET: z.string().optional(), +}); + +export const env = envSchema.parse(process.env);
src/exception.ts+47 −0 added@@ -0,0 +1,47 @@ +class ServerException extends Error { + code: number = -32603; + + constructor(message: string) { + super(message); + } +} + +class InvalidSessionIdError extends ServerException { + code: number = -32600; + + constructor() { + super("Bad Request: No valid session ID provided"); + } +} + +class MethodNotAllowedError extends ServerException { + code: number = -32600; + + constructor() { + super("Method not allowed"); + } +} + +class InvalidCredentialsError extends ServerException { + code: number = -32600; + + constructor() { + super("Invalid credentials"); + } +} + +class InvalidInputError extends ServerException { + code: number = -32600; + + constructor(message: string) { + super(message); + } +} + +export { + ServerException, + InvalidSessionIdError, + MethodNotAllowedError, + InvalidCredentialsError, + InvalidInputError, +};
src/index.ts+59 −292 modified@@ -1,306 +1,73 @@ -#!/usr/bin/env node - -import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; -import { - CallToolRequestSchema, - ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, - ListToolsRequestSchema, - ReadResourceRequestSchema, -} from "@modelcontextprotocol/sdk/types.js"; -import { Credentials, Translator } from "@translated/lara"; -import { z } from "zod"; -import { zodToJsonSchema } from "zod-to-json-schema"; -import { translateHandler, translateSchema } from "./tools/translate.js"; -import { - listMemories, - listMemoriesSchema, -} from "./tools/list_memories.js"; -import { listLanguages, listLanguagesSchema } from "./tools/list_languages.js"; -import { - addTranslationSchema, - addTranslation, -} from "./tools/add_translation.js"; -import { - createMemorySchema, - createMemory, -} from "./tools/create_memory.js"; -import { - deleteMemorySchema, - deleteMemory, -} from "./tools/delete_memory.js"; -import { - deleteTranslationSchema, - deleteTranslation, -} from "./tools/delete_translation.js"; -import { - updateMemorySchema, - updateMemory, -} from "./tools/update_memory.tool.js"; -import { importTmx, importTmxSchema } from "./tools/import_tmx.js"; -import { - checkImportStatus, - checkImportStatusSchema, -} from "./tools/check_import_status.js"; -import {getMemoryByName} from "./tools/get_memory_by_name.js"; +import { env } from "./env.js"; +import getMcpServer from "./mcp/server.js"; +import mcpRouter from "./rest/routes/mcp.js"; +import serverInfoRouter from "./rest/routes/server-info.js"; +import { RestServer } from "./rest/server.js"; +import { logger } from "./logger.js"; +import { Server as McpServer } from "@modelcontextprotocol/sdk/server/index.js"; + +// -- Start server +logger.info("Detected server mode: " + env.TRANSPORT); -const LARA_ACCESS_KEY_ID = process.env.LARA_ACCESS_KEY_ID; -const LARA_ACCESS_KEY_SECRET = process.env.LARA_ACCESS_KEY_SECRET; -if (!LARA_ACCESS_KEY_ID || !LARA_ACCESS_KEY_SECRET) { - throw new Error("LARA_ACCESS_KEY_ID and LARA_ACCESS_KEY_SECRET must be set"); +let server: RestServer | McpServer; +switch (env.TRANSPORT) { + case "stdio": + server = await stdioServer(); + break; + case "http": + server = httpServer(); + break; + default: + throw new Error("Invalid transport: " + env.TRANSPORT + ". Must be either 'stdio' or 'http'"); } -const credentials = new Credentials(LARA_ACCESS_KEY_ID, LARA_ACCESS_KEY_SECRET); -const lara = new Translator(credentials); +process.on("SIGINT", () => signalHandler(server)); +process.on("SIGTERM", () => signalHandler(server)); +process.on("SIGQUIT", () => signalHandler(server)); -const server = new Server( - { - name: "Lara Translate", - version: "0.0.10", - }, - { - capabilities: { - tools: {}, - resources: {}, - resourceTemplates: {}, - }, +// -- Signal handler +function signalHandler(server: RestServer | McpServer) { + if (server instanceof RestServer) { + server.stop(); + } else { + server.close(); } -); - -server.setRequestHandler(ListToolsRequestSchema, async () => { - return { - tools: [ - { - name: "translate", - description: - "Translate text between languages with support for language detection, context-aware translations and translation memories using Lara Translate.", - inputSchema: zodToJsonSchema(translateSchema), - }, - { - name: "create_memory", - description: - "Create a translation memory with a custom name in your Lara Translate account. Translation memories store pairs of source and target text segments (translation units) for reuse in future translations.", - inputSchema: zodToJsonSchema(createMemorySchema), - }, - { - name: "delete_memory", - description: - "Deletes a translation memory from your Lara Translate account.", - inputSchema: zodToJsonSchema(deleteMemorySchema), - }, - { - name: "update_memory", - description: - "Updates a translation memory in your Lara Translate account.", - inputSchema: zodToJsonSchema(updateMemorySchema), - }, - { - name: "add_translation", - description: - "Adds a translation to a translation memory in your Lara Translate account.", - inputSchema: zodToJsonSchema(addTranslationSchema), - }, - { - name: "delete_translation", - description: - "Deletes a translation from a translation memory from your Lara Translate account.", - inputSchema: zodToJsonSchema(deleteTranslationSchema), - }, - { - name: "import_tmx", - description: - "Imports a TMX file into a translation memory in your Lara Translate account.", - inputSchema: zodToJsonSchema(importTmxSchema), - }, - { - name: "check_import_status", - description: - "Checks the status of a TMX file import job in your Lara Translate account.", - inputSchema: zodToJsonSchema(checkImportStatusSchema), - }, - { - name: "list_memories", - description: "Lists all translation memories in your Lara Translate account.", - inputSchema: zodToJsonSchema(listMemoriesSchema), - }, - { - name: "list_languages", - description: "Lists all supported languages in your Lara Translate account.", - inputSchema: zodToJsonSchema(listLanguagesSchema), - }, - ], - }; -}); -server.setRequestHandler(CallToolRequestSchema, async (request) => { - const { name, arguments: args } = request.params; - - try { - switch (name) { - case "translate": { - const result = await translateHandler(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "create_memory": { - const result = await createMemory(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "delete_memory": { - const result = await deleteMemory(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "update_memory": { - const result = await updateMemory(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "add_translation": { - const result = await addTranslation(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "delete_translation": { - const result = await deleteTranslation(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "import_tmx": { - const result = await importTmx(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "check_import_status": { - const result = await checkImportStatus(args, lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "list_memories": { - const result = await listMemories(lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - case "list_languages": { - const result = await listLanguages(lara); - return { - content: [{ type: "text", text: JSON.stringify(result, null, 2) }], - }; - } - default: - throw new Error(`Unknown tool: ${name}`); - } - } catch (error) { - if (error instanceof z.ZodError) { - throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`); - } - throw error; - } -}); + process.exit(0); +} -server.setRequestHandler(ListResourcesRequestSchema, async () => { - return { - resources: [ - { - name: "Translation Memories", - description: "List of translation memories in your Lara Translate account.", - uri: "memories://list", - }, - { - name: "Supported Languages", - description: "List of Lara Translate supported languages.", - uri: "languages://list", - } - ], - }; -}); +// -- HTTP server +function httpServer() { + logger.info("Detected HTTP server mode, starting HTTP server..."); + const restServer = new RestServer(); -server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => { - return { - resourceTemplates: [ - { - name: "Get Memory by Name", - uriTemplate: "memories://list/{name}", - description: "Returns a memory by its name", - } - ] - }; -}) + restServer + .configure() + .use("/mcp", mcpRouter(restServer)) + .use("/server-info", serverInfoRouter(restServer)); -server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - const { uri } = request.params; + restServer.start(); + return restServer; +} - try { - switch (true) { - case uri === "memories://list": { - const memories = await listMemories(lara); - return { - contents: [{ - uri: uri, - text: JSON.stringify(memories, null, 2) - }] - }; - } - case uri === "languages://list": { - const languages = await listLanguages(lara); - return { - contents: [{ - uri: uri, - text: JSON.stringify(languages, null, 2) - }] - }; - } - case uri.startsWith(`memories://list/`): { - const name = uri.slice("memories://list/".length).trim(); - if (!name) { - return { - contents: [{ - uri: uri, - text: JSON.stringify({ error: "Memory name is required." }, null, 2) - }] - }; - } +// -- STDIO server +async function stdioServer() { + logger.info( + "Detected stdio server mode, starting MCP server with stdio transport..." + ); + if (!env.LARA_ACCESS_KEY_ID || !env.LARA_ACCESS_KEY_SECRET) { + throw new Error( + "LARA_ACCESS_KEY_ID and LARA_ACCESS_KEY_SECRET must be set when using stdio server" + ); + } - const memory = await getMemoryByName(lara, name); - if (!memory) { - return { - contents: [{ - uri: uri, - text: JSON.stringify({ error: `Memory with name "${name}" not found.` }, null, 2) - }] - }; - } + const mcpServer = getMcpServer(env.LARA_ACCESS_KEY_ID, env.LARA_ACCESS_KEY_SECRET); - return { - contents: [{ - uri: uri, - text: JSON.stringify(memory, null, 2) - }] - }; - } - default: - throw new Error(`Unknown resource: ${uri}`); - } - } catch (error) { - if (error instanceof z.ZodError) { - throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`); - } - throw error; - } -}); + await mcpServer.connect(new StdioServerTransport()).catch((error) => { + logger.error("Error connecting to MCP server: " + error); + process.exit(1); + }); -const transport = new StdioServerTransport(); -server.connect(transport).catch((error) => { - console.error("Server error:", error); - process.exit(1); -}); + return mcpServer; +}
src/logger.ts+13 −0 added@@ -0,0 +1,13 @@ +import { pino } from "pino"; +import { env } from "./env.js"; + +export const logger = pino( + { + level: env.LOGGING_LEVEL, + }, + // Since STDIO server logs to stdout, we need to log to stderr in order + // to avoid the client from reading the logs + env.TRANSPORT === "stdio" + ? pino.destination(process.stderr) + : pino.destination(process.stdout) +);
src/mcp/resources.ts+123 −0 added@@ -0,0 +1,123 @@ +import { + ListResourcesResult, + ListResourceTemplatesResult, + ReadResourceRequest, + ReadResourceResult, +} from "@modelcontextprotocol/sdk/types.js"; +import { getMemoryByName } from "./tools/get_memory_by_name.js"; +import { listLanguages } from "./tools/list_languages.js"; +import { listMemories } from "./tools/list_memories.js"; +import { Translator } from "@translated/lara"; +import * as z from "zod/v4"; +import { InvalidInputError } from "#exception"; +import { logger } from "#logger"; + +async function ListResourceTemplates(): Promise<ListResourceTemplatesResult> { + return { + resourceTemplates: [ + { + name: "Get Memory by Name", + uriTemplate: "memories://list/{name}", + description: "Returns a memory by its name", + }, + ], + }; +} + +async function ListResources(): Promise<ListResourcesResult> { + return { + resources: [ + { + name: "Translation Memories", + description: + "List of translation memories in your Lara Translate account.", + uri: "memories://list", + }, + { + name: "Supported Languages", + description: "List of Lara Translate supported languages.", + uri: "languages://list", + }, + ], + }; +} + +async function ReadResource( + request: ReadResourceRequest, + lara: Translator +): Promise<ReadResourceResult> { + const { uri } = request.params; + + try { + if (uri === "memories://list") { + const memories = await listMemories(lara); + return { + contents: [ + { + uri: uri, + text: JSON.stringify(memories, null, 2), + }, + ], + }; + } + + if (uri === "languages://list") { + const languages = await listLanguages(lara); + return { + contents: [ + { + uri: uri, + text: JSON.stringify(languages, null, 2), + }, + ], + }; + } + + if (uri.startsWith("memories://list/")) { + const name = uri.slice("memories://list/".length).trim(); + if (!name) { + const error = { error: "Memory name is required." }; + return { + contents: [ + { + uri: uri, + text: JSON.stringify(error, null, 2), + }, + ], + }; + } + + const memory = await getMemoryByName(lara, name); + if (!memory) { + const error = { error: `Memory with name "${name}" not found.` }; + return { + contents: [ + { + uri: uri, + text: JSON.stringify(error, null, 2), + }, + ], + }; + } + + return { + contents: [ + { + uri: uri, + text: JSON.stringify(memory, null, 2), + }, + ], + }; + } + + logger.warn(`Requested a resource with uri ${uri}, but it was not found`); + throw new InvalidInputError(`Unknown resource: ${uri}`); + } catch (error) { + if (error instanceof z.ZodError) { + throw new InvalidInputError(`Invalid input: ${JSON.stringify(error.issues)}`); + } + throw error; + } +} + +export { ListResourceTemplates, ListResources, ReadResource };
src/mcp/server.ts+54 −0 added@@ -0,0 +1,54 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { Credentials, Translator } from "@translated/lara"; +import { + CallToolRequestSchema, + ListResourcesRequestSchema, + ListResourceTemplatesRequestSchema, + ListToolsRequestSchema, + ReadResourceRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { CallTool, ListTools } from "./tools.js"; +import { ListResources, ListResourceTemplates, ReadResource } from "./resources.js"; +import { logger } from "#logger"; + +export default function getMcpServer( + accessKeyId: string, + accessKeySecret: string +) { + logger.debug("Creating MCP server with credentials: " + accessKeyId); + + const credentials = new Credentials(accessKeyId, accessKeySecret); + const lara = new Translator(credentials); + + const server = new Server( + { + name: "Lara Translate", + version: "0.0.11", + }, + { + capabilities: { + tools: {}, + resources: {}, + resourceTemplates: {}, + }, + } + ); + logger.debug("MCP server created! Setting request handlers..."); + + // -- Tools + server.setRequestHandler(ListToolsRequestSchema, ListTools); + server.setRequestHandler(CallToolRequestSchema, (request) => + CallTool(request, lara) + ); + + // -- Resources + server.setRequestHandler(ListResourceTemplatesRequestSchema, ListResourceTemplates); + server.setRequestHandler(ListResourcesRequestSchema, ListResources); + server.setRequestHandler(ReadResourceRequestSchema, (request) => + ReadResource(request, lara) + ); + + logger.debug("Request handlers set!"); + + return server; +}
src/mcp/tools/add_translation.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const addTranslationSchema = z.object({ id: z
src/mcp/tools/check_import_status.ts+2 −2 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const checkImportStatusSchema = z.object({ id: z.string().describe("The ID of the import job"), @@ -10,4 +10,4 @@ export async function checkImportStatus(args: any, lara: Translator) { const { id } = validatedArgs; return await lara.memories.getImportStatus(id); -} +} \ No newline at end of file
src/mcp/tools/create_memory.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const createMemorySchema = z.object({ name: z
src/mcp/tools/delete_memory.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const deleteMemorySchema = z.object({ id: z
src/mcp/tools/delete_translation.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const deleteTranslationSchema = z.object({ id: z
src/mcp/tools/get_memory_by_name.ts+0 −0 renamedsrc/mcp/tools/import_tmx.ts+48 −0 added@@ -0,0 +1,48 @@ +import { Translator } from "@translated/lara"; +import { z } from "zod/v4"; +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +export const importTmxSchema = z.object({ + id: z + .string() + .describe( + "The ID of the memory to update. Format: mem_xyz123." + ), + tmx_content: z + .string() + .describe( + "The content of the tmx file to upload." + ), +}); + +export async function importTmx(args: any, lara: Translator) { + const validatedArgs = importTmxSchema.parse(args); + const { id, tmx_content } = validatedArgs; + + // File size limit: 5MB + const MAX_TMX_SIZE = 5 * 1024 * 1024; // 5MB + if (Buffer.byteLength(tmx_content, 'utf8') > MAX_TMX_SIZE) { + throw new Error("TMX file too large. Maximum allowed size is 5MB."); + } + + const tempDir = path.join(os.tmpdir(), 'lara-tmx-imports'); + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + } + + const tempFilePath = path.join(tempDir, `tmx-${Date.now()}-${Math.random().toString(36).slice(2)}.tmx`); + + try { + fs.writeFileSync(tempFilePath, tmx_content); + + return await lara.memories.importTmx(id, tempFilePath); + } catch (err) { + throw err; + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } +} \ No newline at end of file
src/mcp/tools/list_languages.ts+1 −1 renamed@@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Translator } from "@translated/lara"; export const listLanguagesSchema = z.object({});
src/mcp/tools/list_memories.ts+1 −1 renamed@@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Translator } from "@translated/lara"; export const listMemoriesSchema = z.object({})
src/mcp/tools/translate.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const textBlockSchema = z.object({ text: z.string(),
src/mcp/tools.ts+152 −0 added@@ -0,0 +1,152 @@ +import { + CallToolRequest, + CallToolResult, +} from "@modelcontextprotocol/sdk/types.js"; +import { Translator } from "@translated/lara"; +import * as z from "zod/v4"; + +import { + addTranslation, + addTranslationSchema, +} from "./tools/add_translation.js"; +import { + checkImportStatus, + checkImportStatusSchema, +} from "./tools/check_import_status.js"; +import { createMemory, createMemorySchema } from "./tools/create_memory.js"; +import { deleteMemory, deleteMemorySchema } from "./tools/delete_memory.js"; +import { + deleteTranslation, + deleteTranslationSchema, +} from "./tools/delete_translation.js"; +import { importTmx, importTmxSchema } from "./tools/import_tmx.js"; +import { listLanguages, listLanguagesSchema } from "./tools/list_languages.js"; +import { listMemories, listMemoriesSchema } from "./tools/list_memories.js"; +import { translateHandler, translateSchema } from "./tools/translate.js"; +import { + updateMemory, + updateMemorySchema, +} from "./tools/update_memory.tool.js"; +import { InvalidInputError } from "#exception"; +import { logger } from "#logger"; + +type Handler = (args: any, lara: Translator) => Promise<any>; +type Lister = (lara: Translator) => Promise<any>; + +const handlers: Record<string, Handler> = { + translate: translateHandler, + create_memory: createMemory, + delete_memory: deleteMemory, + update_memory: updateMemory, + add_translation: addTranslation, + delete_translation: deleteTranslation, + import_tmx: importTmx, + check_import_status: checkImportStatus, +}; + +const listers: Record<string, Lister> = { + list_memories: listMemories, + list_languages: listLanguages, +}; + +async function CallTool( + request: CallToolRequest, + lara: Translator +): Promise<CallToolResult> { + const { name, arguments: args } = request.params; + + try { + if (name in handlers) { + const result = await handlers[name as keyof typeof handlers](args, lara); + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + }; + } + + if (name in listers) { + const result = await listers[name as keyof typeof listers](lara); + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + }; + } + + logger.warn(`Requested a tool with name ${name}, but it was not found`); + throw new InvalidInputError(`Tool ${name} not found`); + } catch (error) { + if (error instanceof z.ZodError) { + throw new InvalidInputError( + `Invalid input: ${JSON.stringify(error.issues)}` + ); + } + throw error; + } +} + +async function ListTools() { + return { + tools: [ + { + name: "translate", + description: + "Translate text between languages with support for language detection, context-aware translations and translation memories using Lara Translate.", + inputSchema: z.toJSONSchema(translateSchema), + }, + { + name: "create_memory", + description: + "Create a translation memory with a custom name in your Lara Translate account. Translation memories store pairs of source and target text segments (translation units) for reuse in future translations.", + inputSchema: z.toJSONSchema(createMemorySchema), + }, + { + name: "delete_memory", + description: + "Deletes a translation memory from your Lara Translate account.", + inputSchema: z.toJSONSchema(deleteMemorySchema), + }, + { + name: "update_memory", + description: + "Updates a translation memory in your Lara Translate account.", + inputSchema: z.toJSONSchema(updateMemorySchema), + }, + { + name: "add_translation", + description: + "Adds a translation to a translation memory in your Lara Translate account.", + inputSchema: z.toJSONSchema(addTranslationSchema), + }, + { + name: "delete_translation", + description: + "Deletes a translation from a translation memory from your Lara Translate account.", + inputSchema: z.toJSONSchema(deleteTranslationSchema), + }, + { + name: "import_tmx", + description: + "Imports a TMX file into a translation memory in your Lara Translate account.", + inputSchema: z.toJSONSchema(importTmxSchema), + }, + { + name: "check_import_status", + description: + "Checks the status of a TMX file import job in your Lara Translate account.", + inputSchema: z.toJSONSchema(checkImportStatusSchema), + }, + { + name: "list_memories", + description: + "Lists all translation memories in your Lara Translate account.", + inputSchema: z.toJSONSchema(listMemoriesSchema), + }, + { + name: "list_languages", + description: + "Lists all supported languages in your Lara Translate account.", + inputSchema: z.toJSONSchema(listLanguagesSchema), + }, + ], + }; +} + +export { CallTool, ListTools };
src/mcp/tools/update_memory.tool.ts+1 −1 renamed@@ -1,5 +1,5 @@ import { Translator } from "@translated/lara"; -import { z } from "zod"; +import { z } from "zod/v4"; export const updateMemorySchema = z.object({ id: z
src/rest/middleware/cors.ts+11 −0 added@@ -0,0 +1,11 @@ +import cors from "cors"; + +const corsMiddleware = cors({ + methods: ["POST", "GET", "DELETE"], + // Allow only necessary headers + allowedHeaders: ["Content-Type", "Accept", "x-lara-access-key-id", "x-lara-access-key-secret"], + // Prevent credentials from being sent to untrusted origins + credentials: false, +}); + +export default corsMiddleware;
src/rest/middleware/logging.ts+49 −0 added@@ -0,0 +1,49 @@ +import { Request, Response, NextFunction } from "express"; +import { logger } from "#logger"; + +// Sensitive headers that should not be logged +const SENSITIVE_HEADERS = [ + 'authorization', + 'x-lara-access-key-id', + 'x-lara-access-key-secret', + 'x-lara-api-id', + 'x-lara-api-secret', + 'cookie', + 'set-cookie', +]; + +// Sanitize headers to remove sensitive information +function sanitizeHeaders(headers: any): any { + const sanitized = { ...headers }; + SENSITIVE_HEADERS.forEach(header => { + if (sanitized[header]) { + sanitized[header] = '[REDACTED]'; + } + }); + return sanitized; +} + +// Sanitize request body to remove sensitive information +function sanitizeBody(body: any): any { + if (!body) return body; + + const sanitized = { ...body }; + // Remove sensitive fields from body if they exist + const sensitiveFields = ['password', 'token', 'secret', 'key', 'api_key']; + sensitiveFields.forEach(field => { + if (sanitized[field]) { + sanitized[field] = '[REDACTED]'; + } + }); + return sanitized; +} + +export default function loggingMiddleware(req: Request, res: Response, next: NextFunction) { + logger.info(`Received request: ${req.method} ${req.url}`); + logger.debug(`Body: ${JSON.stringify(sanitizeBody(req.body), null, 2)}`); + logger.debug(`Headers: ${JSON.stringify(sanitizeHeaders(req.headers), null, 2)}`); + + next(); + + logger.info(`Sending response: ${res.statusCode} ${res.statusMessage}`); +} \ No newline at end of file
src/rest/routes/mcp.ts+65 −0 added@@ -0,0 +1,65 @@ +import express from "express"; +import { RestServer } from "#rest/server"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import getMcpServer from "#mcp/server"; +import { + InvalidCredentialsError, + InvalidInputError, + MethodNotAllowedError, + ServerException, +} from "#exception"; +import { logger } from "#logger"; + +function mcpRouter(restServer: RestServer): express.Router { + const router = express.Router(); + + router.post("/", async (req, res) => { + const xLaraAccessKeyId = req.headers["x-lara-access-key-id"] as string | undefined; + const xLaraAccessKeySecret = req.headers["x-lara-access-key-secret"] as string | undefined; + + if (!xLaraAccessKeyId || !xLaraAccessKeySecret) { + restServer.sendJsonRpc(res, new InvalidCredentialsError()); + return; + } + + const transport: StreamableHTTPServerTransport = + new StreamableHTTPServerTransport({ + // In stateless mode we don't need to track the client session + sessionIdGenerator: undefined, + }); + + const server = getMcpServer(xLaraAccessKeyId, xLaraAccessKeySecret); + await server.connect(transport); + + try { + await transport.handleRequest(req, res, req.body); + } catch (error) { + if (error instanceof InvalidInputError) { + logger.error( + "Invalid input error while handling MCP request: " + error.message + ); + restServer.sendJsonRpc(res, error); + return; + } + + logger.error("Generic error while handling MCP request: " + error); + restServer.sendJsonRpc(res, new ServerException("Internal server error")); + } + }); + + router.get("/", (_req, res) => { + logger.debug("Received GET request on /mcp, sending MethodNotAllowedError"); + restServer.sendJsonRpc(res, new MethodNotAllowedError()); + }); + + router.delete("/", (_req, res) => { + logger.debug( + "Received DELETE request on /mcp, sending MethodNotAllowedError" + ); + restServer.sendJsonRpc(res, new MethodNotAllowedError()); + }); + + return router; +} + +export default mcpRouter;
src/rest/routes/server-info.ts+27 −0 added@@ -0,0 +1,27 @@ +import fs from "node:fs"; +import path from "node:path"; + +import express from "express"; +import { RestServer } from "#rest/server"; + +const buildInfoJson: string = fs.readFileSync( + path.join(import.meta.dirname, "..", "..", "..", "package.json"), + "utf8" +); + +type BuildInfo = { + name: string; + version: string; +}; + +export const BuildInfo: BuildInfo = JSON.parse(buildInfoJson); + +export default function serverInfoRouter(rest: RestServer): express.Router { + const router = express.Router(); + + router.all("/", (_req, res) => { + rest.send(res, { version: BuildInfo.version }); + }); + + return router; +}
src/rest/server.ts+108 −0 added@@ -0,0 +1,108 @@ +import express, { Express, Response } from "express"; +import helmet from "helmet"; +import cors from "./middleware/cors.js"; +import { env } from "#env"; +import { ServerException } from "#exception"; +import { + JSONRPCError, + JSONRPCResponse, +} from "@modelcontextprotocol/sdk/types.js"; +import { logger } from "#logger"; +import loggingMiddleware from "./middleware/logging.js"; +import { createServer, Server } from "node:http"; + +export class RestServer { + private port: number; + private host: string; + + private express: Express; + private httpServer?: Server; + + constructor() { + // -- Member variables + this.port = env.PORT; + this.host = env.HOST; + + this.express = express(); + + // -- Json parser + this.express.use(express.json()); + + // -- Security + this.express.use(cors); + this.express.use(helmet()); + + // -- Logging + this.express.use(loggingMiddleware); + } + + public start() { + logger.info(`Starting HTTP server on ${this.host}:${this.port}...`); + // Bind specifically on 127.0.0.1 to avoid binding to all interfaces + this.httpServer = createServer(this.express).listen(this.port, this.host, () => { + logger.info(`HTTP server successfully started on ${this.host}:${this.port}`); + }); + } + + public stop() { + if (!this.httpServer) { + logger.error("Cannot stop HTTP server, it is not started"); + return; + } + + logger.info(`Stopping HTTP server on ${this.host}:${this.port}...`); + this.httpServer.close(() => { + logger.info(`HTTP server successfully stopped on ${this.host}:${this.port}`); + }); + } + + public configure() { + return this.express; + } + + public send(res: Response, payload: ServerException | any): void { + logger.debug("Sending response to client: " + JSON.stringify(payload)); + if (payload instanceof ServerException) { + res.status(400).json({ + error: { + code: payload.code, + message: payload.message, + }, + }); + return; + } + + res.status(200).json(payload); + } + + public sendJsonRpc(res: Response, payload: ServerException | any): void { + logger.debug("Sending JSON-RPC response to client: " + JSON.stringify(payload)); + if (payload instanceof ServerException) { + res.status(400).json(this.createJsonRpcResponse(payload)); + return; + } + + res.status(200).json(this.createJsonRpcResponse(payload)); + } + + private createJsonRpcResponse( + payload: ServerException | any + ): JSONRPCResponse | JSONRPCError { + if (payload instanceof ServerException) { + return { + jsonrpc: "2.0", + error: { + code: payload.code, + message: payload.message, + }, + id: "", + }; + } + + return { + jsonrpc: "2.0", + result: payload, + id: "", + }; + } +}
src/__tests__/server/rest.server.test.ts+104 −0 added@@ -0,0 +1,104 @@ +import { describe, it, expect, beforeAll } from "vitest"; +import request from "supertest"; +import { RestServer } from "../../rest/server.js"; +import mcpRouter from "../../rest/routes/mcp.js"; +import serverInfoRouter from "../../rest/routes/server-info.js"; +import { Express } from "express"; + +let app: Express; + +beforeAll(() => { + const restServer = new RestServer(); + app = restServer.configure(); + app.use("/mcp", mcpRouter(restServer)); + app.use("/server-info", serverInfoRouter(restServer)); +}); + +const defaultHeaders = { + accept: "application/json, text/event-stream", + "content-type": "application/json", +}; + +const defaultPostMcpBody = { + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2.0", + capabilities: {}, + clientInfo: { + name: "MyClient", + version: "1.0.0", + }, + }, +}; + +describe("RestServer", () => { + it("GET /server-info should return version info", async () => { + const res = await request(app).get("/server-info").set(defaultHeaders); + + expect(res.status).toBe(200); + expect(res.body).toHaveProperty("version"); + }); + + it("POST /mcp without credentials should return error", async () => { + const res = await request(app) + .post("/mcp") + .set(defaultHeaders) + .send(defaultPostMcpBody); + + expect(res.status).toBe(400); + expect(res.body).toHaveProperty("error"); + expect(res.body.error).toHaveProperty("code"); + expect(res.body.error).toHaveProperty("message"); + }); + + it("POST /mcp with only one credential header should return error", async () => { + const res1 = await request(app) + .post("/mcp") + .set("x-lara-access-key-id", "test") + .set(defaultHeaders) + .send(defaultPostMcpBody); + + expect(res1.status).toBe(400); + expect(res1.body).toHaveProperty("error"); + + const res2 = await request(app) + .post("/mcp") + .set("x-lara-access-key-secret", "test") + .set(defaultHeaders) + .send(defaultPostMcpBody); + + expect(res2.status).toBe(400); + expect(res2.body).toHaveProperty("error"); + }); + + it("POST /mcp with both credentials should return success", async () => { + const res = await request(app) + .post("/mcp") + .set("x-lara-access-key-id", "test") + .set("x-lara-access-key-secret", "test") + .set(defaultHeaders) + .send(defaultPostMcpBody); + + expect(res.status).toBe(200); + }); + + it("GET /mcp should return MethodNotAllowedError", async () => { + const res = await request(app).get("/mcp").set(defaultHeaders); + + expect(res.status).toBe(400); + expect(res.body).toHaveProperty("error"); + expect(res.body.error).toHaveProperty("code"); + expect(res.body.error).toHaveProperty("message"); + }); + + it("DELETE /mcp should return MethodNotAllowedError", async () => { + const res = await request(app).delete("/mcp").set(defaultHeaders); + + expect(res.status).toBe(400); + expect(res.body).toHaveProperty("error"); + expect(res.body.error).toHaveProperty("code"); + expect(res.body.error).toHaveProperty("message"); + }); +});
src/__tests__/tools/add_translation.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { addTranslation, addTranslationSchema } from '../../tools/add_translation.js'; +import { addTranslation, addTranslationSchema } from '../../mcp/tools/add_translation.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/check_import_status.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { checkImportStatus, checkImportStatusSchema } from '../../tools/check_import_status.js'; +import { checkImportStatus, checkImportStatusSchema } from '../../mcp/tools/check_import_status.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/create_memory.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { createMemory, createMemorySchema } from '../../tools/create_memory.js'; +import { createMemory, createMemorySchema } from '../../mcp/tools/create_memory.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/delete_memory.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { deleteMemory, deleteMemorySchema } from '../../tools/delete_memory.js'; +import { deleteMemory, deleteMemorySchema } from '../../mcp/tools/delete_memory.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/delete_translation.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { deleteTranslation, deleteTranslationSchema } from '../../tools/delete_translation.js'; +import { deleteTranslation, deleteTranslationSchema } from '../../mcp/tools/delete_translation.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/import_tmx.test.ts+18 −44 modified@@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { importTmx, importTmxSchema } from '../../tools/import_tmx.js'; +import { importTmx, importTmxSchema } from '../../mcp/tools/import_tmx.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara'; @@ -16,33 +16,29 @@ describe('importTmxSchema', () => { expect(() => importTmxSchema.parse(validInput)).not.toThrow(); }); - it('should validate valid input with tmx_url', () => { - const validInput = { - id: 'mem_xyz123', - tmx_url: 'https://example.com/file.tmx' + it('should reject input with missing id', () => { + const invalidInput = { + // Missing id + tmx_content: '<tmx>...</tmx>' }; - expect(() => importTmxSchema.parse(validInput)).not.toThrow(); + expect(() => importTmxSchema.parse(invalidInput)).toThrow(); }); - it('should validate input with optional gzip', () => { - const validInput = { - id: 'mem_xyz123', - tmx_url: 'https://example.com/file.tmx.gz', - gzip: true + it('should reject input with missing tmx_content', () => { + const invalidInput = { + // Missing tmx_content + id: 'mem_xyz123' }; - expect(() => importTmxSchema.parse(validInput)).not.toThrow(); + expect(() => importTmxSchema.parse(invalidInput)).toThrow(); }); - it('should reject input with missing required fields', () => { - const invalidInput = { - id: 'mem_xyz123' - // Missing both tmx_content and tmx_url - }; + it('should reject input with missing id and tmx_content', () => { + // Missing id and tmx_content + const invalidInput = {}; - // Schema validation will pass as both are optional, but function will throw - expect(() => importTmxSchema.parse(invalidInput)).not.toThrow(); + expect(() => importTmxSchema.parse(invalidInput)).toThrow(); }); }); @@ -60,34 +56,12 @@ describe('importTmx input validation', () => { }); }); - it('should throw an error if both tmx_content and tmx_url are provided', async () => { + it('should call lara.memories.importTmx with correct parameters', async () => { const args = { id: 'mem_xyz123', - tmx_content: '<tmx>...</tmx>', - tmx_url: 'https://example.com/file.tmx' - }; - - // Use a try/catch pattern instead of expect().rejects to avoid timeouts - try { - await importTmx(args, mockTranslator as any as Translator); - // If we get here, fail the test - expect(true).toBe(false); // This should not be reached - } catch (error: any) { - expect(error.message).toBe("You can't provide both tmx_content and tmx_url."); - } - }); - - it('should throw an error if neither tmx_content nor tmx_url are provided', async () => { - const args = { - id: 'mem_xyz123' + tmx_content: '<tmx>...</tmx>' }; - try { - await importTmx(args, mockTranslator as any as Translator); - // If we get here, fail the test - expect(true).toBe(false); // This should not be reached - } catch (error: any) { - expect(error.message).toBe("You must provide either tmx_content or tmx_url."); - } + await importTmx(args, mockTranslator as any as Translator); }); }); \ No newline at end of file
src/__tests__/tools/list_languages.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { listLanguages, listLanguagesSchema } from '../../tools/list_languages.js'; +import { listLanguages, listLanguagesSchema } from '../../mcp/tools/list_languages.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/list_memories.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { listMemories, listMemoriesSchema } from '../../tools/list_memories.js'; +import { listMemories, listMemoriesSchema } from '../../mcp/tools/list_memories.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/translate.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { translateHandler, translateSchema } from '../../tools/translate.js'; +import { translateHandler, translateSchema } from '../../mcp/tools/translate.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/tools/update_memory.test.ts+1 −1 modified@@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { updateMemory, updateMemorySchema } from '../../tools/update_memory.tool.js'; +import { updateMemory, updateMemorySchema } from '../../mcp/tools/update_memory.tool.js'; import { getMockTranslator, setupTranslatorMock, type MockTranslator } from '../utils/mocks.js'; import { Translator } from '@translated/lara';
src/__tests__/utils/mocks.ts+2 −2 modified@@ -19,7 +19,7 @@ export function createMockTranslator() { getMemories: vi.fn(), addTranslation: vi.fn(), deleteTranslation: vi.fn(), - importTMX: vi.fn(), + importTmx: vi.fn(), getImportStatus: vi.fn(), memories: { create: vi.fn(), @@ -30,7 +30,7 @@ export function createMockTranslator() { addTranslation: vi.fn(), deleteTranslation: vi.fn(), importTmx: vi.fn(), - getImportStatus: vi.fn() + getImportStatus: vi.fn(), }, client: {} };
src/tools/import_tmx.ts+0 −69 removed@@ -1,69 +0,0 @@ -import { Translator } from "@translated/lara"; -import { z } from "zod"; -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; -import { exec } from "child_process"; -import { promisify } from "util"; - -const execAsync = promisify(exec); - -export const importTmxSchema = z.object({ - id: z - .string() - .describe( - "The ID of the memory to update. Format: mem_xyz123." - ), - tmx_content: z - .string() - .describe( - "The content of the tmx file to upload. Don't provide this if you choose to use tmx_url." - ) - .optional(), - tmx_url: z - .string() - .describe( - "A URL to the tmx file to upload. Don't provide this if you choose to use tmx_content." - ) - .optional(), - gzip: z - .boolean() - .describe( - "Indicates if the file is a compressed .gz file" - ) - .optional(), -}); - -export async function importTmx(args: any, lara: Translator) { - const validatedArgs = importTmxSchema.parse(args); - const { id, tmx_content, tmx_url, gzip } = validatedArgs; - - if (tmx_content && tmx_url) { - throw new Error("You can't provide both tmx_content and tmx_url."); - } - - if (!tmx_content && !tmx_url) { - throw new Error("You must provide either tmx_content or tmx_url."); - } - - const tempDir = path.join(os.tmpdir(), 'lara-tmx-imports'); - if (!fs.existsSync(tempDir)) { - fs.mkdirSync(tempDir, { recursive: true }); - } - - const tempFilePath = path.join(tempDir, `tmx-${Date.now()}-${Math.random().toString(36).slice(2)}.tmx`); - - try { - if (tmx_url) { - await execAsync(`curl -L "${tmx_url}" -o "${tempFilePath}"`); - } else if (tmx_content) { - fs.writeFileSync(tempFilePath, tmx_content); - } - - return await lara.memories.importTmx(id, tempFilePath, gzip); - } finally { - if (fs.existsSync(tempFilePath)) { - fs.unlinkSync(tempFilePath); - } - } -}
tsconfig.json+11 −2 modified@@ -6,7 +6,16 @@ "esModuleInterop": true, "strict": true, "outDir": "./dist", - "rootDir": "./src" + "rootDir": "./src", + "baseUrl": ".", + "paths": { + "#env": ["./src/env.js"], + "#exception": ["./src/exception.js"], + "#logger": ["./src/logger.js"], + "#rest/server": ["./src/rest/server.js"], + "#mcp/server": ["./src/mcp/server.js"] + } }, - "include": ["src/**/*"] + "include": ["src/**/*"], + "exclude": ["node_modules"] }
vitest.config.ts+9 −0 modified@@ -9,6 +9,15 @@ export default defineConfig({ provider: 'v8', reporter: ['text', 'json', 'html'], exclude: ['**/node_modules/**', '**/dist/**', '**/coverage/**'] + }, + }, + resolve: { + alias: { + '#env': new URL('./src/env.ts', import.meta.url).pathname, + '#exception': new URL('./src/exception.ts', import.meta.url).pathname, + '#logger': new URL('./src/logger.ts', import.meta.url).pathname, + '#rest/server': new URL('./src/rest/server.ts', import.meta.url).pathname, + '#mcp/server': new URL('./src/mcp/server.ts', import.meta.url).pathname } } }); \ No newline at end of file
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-xj5p-8h7g-76m7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-53832ghsaADVISORY
- equixly.com/blog/2025/03/29/mcp-server-new-security-nightmareghsaWEB
- github.com/translated/lara-mcp/blob/v0.0.11/src/tools/import_tmx.tsghsaWEB
- github.com/translated/lara-mcp/blob/v0.0.12/src/mcp/tools/import_tmx.tsghsaWEB
- github.com/translated/lara-mcp/commit/e534ef690adf390e4ac862a200b2a83f6cf45944nvdWEB
- github.com/translated/lara-mcp/security/advisories/GHSA-xj5p-8h7g-76m7nvdWEB
- invariantlabs.ai/blog/mcp-github-vulnerabilityghsaWEB
News mentions
0No linked articles in our index yet.