From 47171adc3dbf64807d4e8ce42bf728ec23be3cb0 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 5 Dec 2025 18:22:13 +0100 Subject: [PATCH 01/36] feat(pluggable-widgets-mcp): introduce pluggable-widgets-mcp --- packages/pluggable-widgets-mcp/.gitignore | 3 + .../pluggable-widgets-mcp/.prettierrc.cjs | 1 + packages/pluggable-widgets-mcp/.prettierrc.js | 1 + packages/pluggable-widgets-mcp/AGENTS.md | 205 + packages/pluggable-widgets-mcp/README.md | 148 + .../pluggable-widgets-mcp/eslint.config.mjs | 3 + .../pluggable-widgets-mcp/package-lock.json | 2139 ++++++ packages/pluggable-widgets-mcp/package.json | 42 + .../pluggable-widgets-mcp/src/api/handlers.ts | 0 packages/pluggable-widgets-mcp/src/config.ts | 25 + packages/pluggable-widgets-mcp/src/index.ts | 23 + .../pluggable-widgets-mcp/src/server/http.ts | 35 + .../src/server/routes.ts | 77 + .../src/server/server.ts | 49 + .../src/server/session.ts | 77 + .../pluggable-widgets-mcp/src/server/stdio.ts | 31 + .../pluggable-widgets-mcp/src/tools/index.ts | 13 + .../src/tools/scaffolding.tools.ts | 157 + .../pluggable-widgets-mcp/src/tools/types.ts | 69 + .../src/tools/utils/generator.ts | 250 + .../src/tools/utils/notifications.ts | 36 + .../src/tools/utils/progress-tracker.ts | 237 + .../src/tools/utils/response.ts | 19 + packages/pluggable-widgets-mcp/tsconfig.json | 34 + packages/pluggable-widgets-mcp/tscpaths.json | 4 + pnpm-lock.yaml | 6426 ++++++++++------- 26 files changed, 7580 insertions(+), 2524 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/.gitignore create mode 100644 packages/pluggable-widgets-mcp/.prettierrc.cjs create mode 100644 packages/pluggable-widgets-mcp/.prettierrc.js create mode 100644 packages/pluggable-widgets-mcp/AGENTS.md create mode 100644 packages/pluggable-widgets-mcp/README.md create mode 100644 packages/pluggable-widgets-mcp/eslint.config.mjs create mode 100644 packages/pluggable-widgets-mcp/package-lock.json create mode 100644 packages/pluggable-widgets-mcp/package.json create mode 100644 packages/pluggable-widgets-mcp/src/api/handlers.ts create mode 100644 packages/pluggable-widgets-mcp/src/config.ts create mode 100644 packages/pluggable-widgets-mcp/src/index.ts create mode 100644 packages/pluggable-widgets-mcp/src/server/http.ts create mode 100644 packages/pluggable-widgets-mcp/src/server/routes.ts create mode 100644 packages/pluggable-widgets-mcp/src/server/server.ts create mode 100644 packages/pluggable-widgets-mcp/src/server/session.ts create mode 100644 packages/pluggable-widgets-mcp/src/server/stdio.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/index.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/types.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/generator.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/progress-tracker.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/response.ts create mode 100644 packages/pluggable-widgets-mcp/tsconfig.json create mode 100644 packages/pluggable-widgets-mcp/tscpaths.json diff --git a/packages/pluggable-widgets-mcp/.gitignore b/packages/pluggable-widgets-mcp/.gitignore new file mode 100644 index 0000000000..e6cd8c7e6d --- /dev/null +++ b/packages/pluggable-widgets-mcp/.gitignore @@ -0,0 +1,3 @@ +dist/ +generations/ +node_modules/ \ No newline at end of file diff --git a/packages/pluggable-widgets-mcp/.prettierrc.cjs b/packages/pluggable-widgets-mcp/.prettierrc.cjs new file mode 100644 index 0000000000..0892704ab0 --- /dev/null +++ b/packages/pluggable-widgets-mcp/.prettierrc.cjs @@ -0,0 +1 @@ +module.exports = require("@mendix/prettier-config-web-widgets"); diff --git a/packages/pluggable-widgets-mcp/.prettierrc.js b/packages/pluggable-widgets-mcp/.prettierrc.js new file mode 100644 index 0000000000..0892704ab0 --- /dev/null +++ b/packages/pluggable-widgets-mcp/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require("@mendix/prettier-config-web-widgets"); diff --git a/packages/pluggable-widgets-mcp/AGENTS.md b/packages/pluggable-widgets-mcp/AGENTS.md new file mode 100644 index 0000000000..7a08310679 --- /dev/null +++ b/packages/pluggable-widgets-mcp/AGENTS.md @@ -0,0 +1,205 @@ +# Pluggable Widgets MCP Server - AI Agent Guide + +This document provides context for AI development assistants working on the MCP (Model Context Protocol) server for Mendix pluggable widgets. + +## Overview + +This package implements an MCP server that enables AI assistants to scaffold and manage Mendix pluggable widgets programmatically. It supports both HTTP and STDIO transports for flexible integration with various MCP clients. + +### Key Characteristics + +- **MCP SDK**: Built on `@modelcontextprotocol/sdk` for standardized AI tool integration +- **Dual Transport**: HTTP (Express) for web clients, STDIO for CLI clients (Claude Desktop, etc.) +- **TypeScript**: Fully typed with Zod schemas for runtime validation +- **Widget Generator**: Wraps `@mendix/generator-widget` via PTY for interactive scaffolding + +## Project Structure + +``` +src/ +├── index.ts # Entry point - transport mode selection +├── config.ts # Server configuration and constants +├── server/ +│ ├── server.ts # MCP server factory and tool registration +│ ├── http.ts # HTTP transport setup (Express) +│ ├── stdio.ts # STDIO transport setup +│ ├── routes.ts # Express route handlers +│ └── session.ts # HTTP session management +└── tools/ + ├── index.ts # Tool aggregation + ├── types.ts # MCP tool type definitions + ├── scaffolding.tools.ts # Widget creation tool + └── utils/ + ├── generator.ts # Widget generator PTY wrapper + ├── progress-tracker.ts # Progress/logging helper + ├── notifications.ts # MCP notification utilities + └── response.ts # Tool response helpers +``` + +## Architecture + +### Transport Layer + +The server supports two transport modes selected via CLI argument: + +- **HTTP** (default): Multi-session Express server on port 3100 +- **STDIO**: Single-session stdin/stdout for CLI integration + +### Tool Registration + +Tools are defined using the `ToolDefinition` interface: + +```typescript +interface ToolDefinition { + name: string; // Tool identifier + title: string; // Human-readable name + description: string; // LLM-facing description + inputSchema: ZodType; // Zod schema for validation + handler: ToolHandler; // Async handler function +} +``` + +New tools should be: + +1. Created in `src/tools/` with a `*.tools.ts` suffix +2. Export a `get*Tools()` function returning `ToolDefinition[]` +3. Registered in `src/tools/index.ts` + +### Widget Generator Integration + +The `create-widget` tool uses `node-pty` to interact with the Mendix widget generator CLI. Key implementation details: + +- **PTY Simulation**: Required because the generator uses interactive prompts +- **Prompt Detection**: Matches expected prompts in terminal output +- **Answer Automation**: Sends pre-configured answers based on user input +- **Progress Tracking**: Reports progress via MCP notifications + +## Development Commands + +```bash +pnpm dev # Development mode with hot reload (tsx watch) +pnpm build # TypeScript compilation + path alias resolution +pnpm start # Build and run (HTTP mode) +pnpm start:stdio # Build and run (STDIO mode) +pnpm lint # ESLint + Prettier check +``` + +## Adding New Tools + +1. **Create tool file**: `src/tools/my-feature.tools.ts` + +```typescript +import { z } from "zod"; +import type { ToolDefinition, ToolResponse } from "@/tools/types"; +import { createToolResponse, createErrorResponse } from "@/tools/utils/response"; + +const mySchema = z.object({ + param: z.string().describe("Parameter description for LLM") +}); + +type MyInput = z.infer; + +export function getMyTools(): ToolDefinition[] { + return [ + { + name: "my-tool", + title: "My Tool", + description: "What this tool does (shown to LLM)", + inputSchema: mySchema, + handler: async (args, context) => { + // Implementation + return createToolResponse("Success message"); + } + } + ]; +} +``` + +2. **Register in index**: Update `src/tools/index.ts` + +```typescript +import { getMyTools } from "./my-feature.tools"; + +export function getAllTools(): AnyToolDefinition[] { + return [ + ...getScaffoldingTools(), + ...getMyTools() // Add here + ]; +} +``` + +## Code Conventions + +### Imports + +- Use `@/` path alias for absolute imports from `src/` +- Prefer specific file imports over barrel exports when dealing with circular dependencies +- Group imports: node builtins → external packages → internal modules + +### Error Handling + +- Use `createErrorResponse()` for user-facing errors +- Log to `console.error` (not stdout) in STDIO mode +- Use `ProgressTracker` for long-running operations + +### Type Safety + +- All tool inputs must have Zod schemas +- Use `ToolContext` for MCP-provided context (notifications, progress) +- Avoid `any` except in `AnyToolDefinition` (required for heterogeneous tool arrays) + +## Testing + +Use MCP Inspector for interactive testing: + +```bash +# STDIO mode +npx @modelcontextprotocol/inspector node dist/index.js stdio + +# HTTP mode +pnpm start +npx @modelcontextprotocol/inspector +# Connect to http://localhost:3100/mcp +``` + +## Key Files Reference + +| File | Purpose | +| -------------------------- | -------------------------------------- | +| `config.ts` | All constants (ports, timeouts, paths) | +| `tools/types.ts` | MCP tool type definitions | +| `tools/utils/generator.ts` | Widget generator prompts and defaults | +| `server/session.ts` | HTTP session lifecycle management | + +## Common Patterns + +### Progress Notifications + +```typescript +const tracker = new ProgressTracker({ + context, + logger: "my-tool", + totalSteps: 5 +}); + +tracker.start("initializing"); +await tracker.progress(25, "Step 1 complete"); +await tracker.info("Detailed log message", { key: "value" }); +tracker.stop(); +``` + +### Long-Running Operations + +- Use `ProgressTracker` for heartbeat and stuck detection +- Set appropriate timeouts (see `SCAFFOLD_TIMEOUT_MS`) +- Call `tracker.markComplete()` before expected long waits (e.g., npm install) + +## Roadmap Context + +Current focus is widget scaffolding. Planned additions: + +- Widget property editing +- XML configuration management +- Build and deployment automation + +When adding features, maintain the existing patterns for tool registration, progress tracking, and transport-agnostic design. diff --git a/packages/pluggable-widgets-mcp/README.md b/packages/pluggable-widgets-mcp/README.md new file mode 100644 index 0000000000..d3c8baeded --- /dev/null +++ b/packages/pluggable-widgets-mcp/README.md @@ -0,0 +1,148 @@ +# Mendix Pluggable Widgets MCP Server + +> **Work in Progress** - This is an MVP focused on widget scaffolding. Widget editing capabilities coming soon. + +A Model Context Protocol (MCP) server that enables AI assistants to scaffold Mendix pluggable widgets programmatically. + +## Quick Start + +```bash +pnpm install +pnpm start # HTTP mode (default) +pnpm start:stdio # STDIO mode +``` + +## Transport Modes + +### HTTP Mode (default) + +Runs an HTTP server for web-based MCP clients. + +```bash +pnpm start +pnpm start:http +``` + +- Server runs on `http://localhost:3100` (override with `PORT` env var) +- Health check: `GET /health` +- MCP endpoint: `POST /mcp` + +### STDIO Mode + +Runs via stdin/stdout for CLI-based MCP clients (Claude Desktop, etc.). + +```bash +pnpm start:stdio +``` + +## MCP Client Configuration + +### HTTP + +```json +{ + "mcpServers": { + "pluggable-widgets-mcp": { + "url": "http://localhost:3100/mcp" + } + } +} +``` + +### STDIO + +**_Some client setups like Claude Desktop support STDIO only (for now)_** + +```json +{ + "mcpServers": { + "pluggable-widgets-mcp": { + "command": "node", + "args": ["/path/to/pluggable-widgets-mcp/dist/index.js", "stdio"] + } + } +} +``` + +## Available Tools + +### create-widget + +Scaffolds a new Mendix pluggable widget using `@mendix/generator-widget`. + +| Parameter | Required | Default | Description | +| --------------------- | -------- | ------------ | ------------------------------------ | +| `name` | Yes | - | Widget name (PascalCase recommended) | +| `description` | Yes | - | Brief description of the widget | +| `version` | No | `1.0.0` | Initial version (semver) | +| `author` | No | `Mendix` | Author name | +| `license` | No | `Apache-2.0` | License type | +| `organization` | No | `Mendix` | Organization namespace | +| `template` | No | `empty` | `full` (sample code) or `empty` | +| `programmingLanguage` | No | `typescript` | `typescript` or `javascript` | +| `unitTests` | No | `true` | Include unit test setup (Jest/TS) | +| `e2eTests` | No | `false` | Include E2E test setup (Playwright) | + +Generated widgets are placed in `generations/` directory within this package. + +## Development + +```bash +pnpm dev # Development mode with hot reload +pnpm build # Build for production +pnpm start # Build and run +``` + +## Testing with MCP Inspector + +The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive debugging tool for testing MCP servers. It provides a web UI to connect to your server, explore available tools, and execute them with custom inputs. + +### Quick Start + +```bash +# Run Inspector against this server (STDIO mode) +npx @modelcontextprotocol/inspector node dist/index.js stdio + +# Or for HTTP mode, start the server first then connect via Inspector +pnpm start +npx @modelcontextprotocol/inspector +# Then enter http://localhost:3100/mcp as the server URL +``` + +### Using the Inspector + +1. **Connect** - The Inspector will automatically connect to your MCP server +2. **Explore Tools** - View all registered tools (`create-widget`, etc.) with their schemas +3. **Execute Tools** - Fill in parameters and run tools to test behavior +4. **View Responses** - See JSON responses, progress notifications, and logs in real-time + +### Example: Testing `create-widget` + +1. Start the Inspector: `npx @modelcontextprotocol/inspector node dist/index.js stdio` +2. Select the `create-widget` tool from the tools list +3. Fill in required parameters: + ```json + { + "name": "TestWidget", + "description": "A test widget", + ... // Defaults for other optional values if not entered + } + ``` +4. Click "Execute" and watch progress notifications as the widget is scaffolded +5. Check `generations/testwidget/` for the created widget + +This is useful for verifying tool behavior without needing a full AI client integration. + +## Roadmap + +- [x] Widget scaffolding +- [x] HTTP transport +- [x] STDIO transport +- [x] Progress notifications +- [ ] Widget editing and modification +- [ ] Property management +- [ ] Build and deployment tools + +## License + +Apache-2.0 - Mendix Technology BV 2025 diff --git a/packages/pluggable-widgets-mcp/eslint.config.mjs b/packages/pluggable-widgets-mcp/eslint.config.mjs new file mode 100644 index 0000000000..ed68ae9e78 --- /dev/null +++ b/packages/pluggable-widgets-mcp/eslint.config.mjs @@ -0,0 +1,3 @@ +import config from "@mendix/eslint-config-web-widgets/widget-ts.mjs"; + +export default config; diff --git a/packages/pluggable-widgets-mcp/package-lock.json b/packages/pluggable-widgets-mcp/package-lock.json new file mode 100644 index 0000000000..4de60372f3 --- /dev/null +++ b/packages/pluggable-widgets-mcp/package-lock.json @@ -0,0 +1,2139 @@ +{ + "name": "pluggable-widgets-mcp", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pluggable-widgets-mcp", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.24.2", + "node-pty": "^1.0.0", + "tsx": "^4.21.0", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/node": "^24.10.1", + "tsc-alias": "^1.8.16", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", + "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", + "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", + "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", + "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", + "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", + "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", + "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", + "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", + "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", + "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", + "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", + "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", + "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", + "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", + "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", + "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", + "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", + "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", + "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", + "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", + "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", + "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", + "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", + "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", + "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", + "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.2.tgz", + "integrity": "sha512-hS/kzSfchqzvUeJUsdiDHi84/kNhLIZaZ6coGQVwbYIelOBbcAwUohUfaQTLa1MvFOK/jbTnGFzraHSFwB7pjQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", + "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.1", + "@esbuild/android-arm": "0.27.1", + "@esbuild/android-arm64": "0.27.1", + "@esbuild/android-x64": "0.27.1", + "@esbuild/darwin-arm64": "0.27.1", + "@esbuild/darwin-x64": "0.27.1", + "@esbuild/freebsd-arm64": "0.27.1", + "@esbuild/freebsd-x64": "0.27.1", + "@esbuild/linux-arm": "0.27.1", + "@esbuild/linux-arm64": "0.27.1", + "@esbuild/linux-ia32": "0.27.1", + "@esbuild/linux-loong64": "0.27.1", + "@esbuild/linux-mips64el": "0.27.1", + "@esbuild/linux-ppc64": "0.27.1", + "@esbuild/linux-riscv64": "0.27.1", + "@esbuild/linux-s390x": "0.27.1", + "@esbuild/linux-x64": "0.27.1", + "@esbuild/netbsd-arm64": "0.27.1", + "@esbuild/netbsd-x64": "0.27.1", + "@esbuild/openbsd-arm64": "0.27.1", + "@esbuild/openbsd-x64": "0.27.1", + "@esbuild/openharmony-arm64": "0.27.1", + "@esbuild/sunos-x64": "0.27.1", + "@esbuild/win32-arm64": "0.27.1", + "@esbuild/win32-ia32": "0.27.1", + "@esbuild/win32-x64": "0.27.1" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mylas": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.14.tgz", + "integrity": "sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/raouldeheer" + } + }, + "node_modules/nan": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", + "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-pty": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", + "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "nan": "^2.17.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "queue-lit": "^1.5.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsc-alias": { + "version": "1.8.16", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", + "integrity": "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "get-tsconfig": "^4.10.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" + }, + "bin": { + "tsc-alias": "dist/bin/index.js" + }, + "engines": { + "node": ">=16.20.2" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json new file mode 100644 index 0000000000..b23cdf64bc --- /dev/null +++ b/packages/pluggable-widgets-mcp/package.json @@ -0,0 +1,42 @@ +{ + "name": "pluggable-widgets-mcp", + "version": "0.1.0", + "description": "MCP server for Mendix Pluggable Widgets", + "copyright": "© Mendix Technology BV 2025. All rights reserved.", + "author": "Mendix", + "license": "Apache-2.0", + "type": "module", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc && tsc-alias -p tsconfig.json --resolve-full-paths", + "dev": "tsx watch src/index.ts", + "generate-source-map": "tsc --sourceMap --declaration --declarationMap", + "lint": "eslint src/ package.json", + "start": "npm run build && node dist/index.js", + "start:http": "npm run build && node dist/index.js http", + "start:stdio": "npm run build && node dist/index.js stdio" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.24.2", + "cors": "^2.8.5", + "express": "^5.1.0", + "node-pty": "^1.0.0", + "tsx": "^4.21.0", + "zod": "^4.1.13" + }, + "devDependencies": { + "@types/cors": "^2.8.19", + "@types/express": "^5.0.2", + "@types/node": "^24.10.1", + "tsc-alias": "^1.8.16", + "typescript": "^5.9.3" + }, + "keywords": [], + "packageManager": "pnpm@10.17.0", + "engines": { + "node": ">=22" + } +} diff --git a/packages/pluggable-widgets-mcp/src/api/handlers.ts b/packages/pluggable-widgets-mcp/src/api/handlers.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/pluggable-widgets-mcp/src/config.ts b/packages/pluggable-widgets-mcp/src/config.ts new file mode 100644 index 0000000000..6dceeb505d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/config.ts @@ -0,0 +1,25 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +// Server configuration +export const SERVER_NAME = "pluggable-widgets-mcp"; +export const SERVER_VERSION = "0.1.0"; +export const PORT = parseInt(process.env.PORT || "3100", 10); + +// Server metadata +export const SERVER_ICON = { + src: "https://avatars.githubusercontent.com/u/133443?s=200&v=4", + sizes: ["128x128"], + mimeType: "image/png" +}; +export const SERVER_WEBSITE_URL = "https://github.com/mendix/web-widgets"; +export const SERVER_INSTRUCTIONS = + "This is a MCP server for Mendix Pluggable Widgets. It allows you to create and edit widgets."; + +// Paths - use fileURLToPath for Node.js 18 compatibility (import.meta.dirname requires Node 20.11+) +const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)); +export const PACKAGE_ROOT = join(__dirname, "../"); +export const GENERATIONS_DIR = join(PACKAGE_ROOT, "generations"); + +// Timeouts +export const SCAFFOLD_TIMEOUT_MS = 300000; // 5 minutes diff --git a/packages/pluggable-widgets-mcp/src/index.ts b/packages/pluggable-widgets-mcp/src/index.ts new file mode 100644 index 0000000000..553f709399 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/index.ts @@ -0,0 +1,23 @@ +import { startHttpServer } from "@/server/http"; +import { startStdioServer } from "@/server/stdio"; + +type TransportMode = "http" | "stdio"; + +const mode = (process.argv[2] as TransportMode) || "http"; + +async function main(): Promise { + switch (mode) { + case "stdio": + await startStdioServer(); + break; + case "http": + default: + await startHttpServer(); + break; + } +} + +main().catch(err => { + console.error("Fatal error:", err); + process.exit(1); +}); diff --git a/packages/pluggable-widgets-mcp/src/server/http.ts b/packages/pluggable-widgets-mcp/src/server/http.ts new file mode 100644 index 0000000000..c1b5fed9c6 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/http.ts @@ -0,0 +1,35 @@ +import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; +import cors from "cors"; +import { PORT } from "@/config"; +import { setupRoutes } from "./routes"; +import { sessionManager } from "./session"; + +/** + * Starts the MCP server with HTTP/Streamable transport. + * Supports multiple concurrent sessions via Express. + */ +export async function startHttpServer(): Promise { + const app = createMcpExpressApp(); + app.use(cors()); + + setupRoutes(app); + + app.listen(PORT, () => { + console.log(`[HTTP] MCP Server started on port ${PORT}`); + console.log(`[HTTP] Health check: http://localhost:${PORT}/health`); + console.log(`[HTTP] MCP endpoint: http://localhost:${PORT}/mcp`); + }); + + setupGracefulShutdown(); +} + +function setupGracefulShutdown(): void { + const shutdown = async (): Promise => { + console.log("\n[HTTP] Shutting down server..."); + await sessionManager.closeAll(); + process.exit(0); + }; + + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); +} diff --git a/packages/pluggable-widgets-mcp/src/server/routes.ts b/packages/pluggable-widgets-mcp/src/server/routes.ts new file mode 100644 index 0000000000..9d888eb491 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/routes.ts @@ -0,0 +1,77 @@ +import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { Express, Request, Response } from "express"; +import { SERVER_NAME, SERVER_VERSION } from "@/config"; +import { createMcpServer } from "./server"; +import { sessionManager } from "./session"; + +/** + * Sets up all routes for the Express application. + */ +export function setupRoutes(app: Express): void { + setupHealthRoute(app); + setupMcpRoute(app); +} + +/** + * Health check endpoint for monitoring. + */ +function setupHealthRoute(app: Express): void { + app.get("/health", (_req: Request, res: Response) => { + res.json({ + status: "ok", + server: SERVER_NAME, + version: SERVER_VERSION, + sessions: sessionManager.sessionCount + }); + }); +} + +/** + * Main MCP endpoint handling session management and request routing. + */ +function setupMcpRoute(app: Express): void { + app.all("/mcp", async (req: Request, res: Response) => { + const sessionId = req.headers["mcp-session-id"] as string | undefined; + + try { + // Case 1: Existing session - reuse transport + if (sessionId && sessionManager.hasSession(sessionId)) { + const transport = sessionManager.getTransport(sessionId)!; + await transport.handleRequest(req, res, req.body); + return; + } + + // Case 2: New session - create transport and server + if (!sessionId && isInitializeRequest(req.body)) { + const transport = sessionManager.createTransport(); + const server = createMcpServer(); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + return; + } + + // Case 3: Invalid request + sendJsonRpcError(res, 400, "Bad Request: No valid session ID provided"); + } catch { + sendJsonRpcError( + res, + 400, + "Invalid session. Send an initialize request without session ID to start a new session." + ); + } + }); +} + +/** + * Sends a JSON-RPC error response. + */ +function sendJsonRpcError(res: Response, statusCode: number, message: string): void { + res.status(statusCode).json({ + jsonrpc: "2.0", + error: { + code: -32000, + message + }, + id: null + }); +} diff --git a/packages/pluggable-widgets-mcp/src/server/server.ts b/packages/pluggable-widgets-mcp/src/server/server.ts new file mode 100644 index 0000000000..42029e0d8a --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/server.ts @@ -0,0 +1,49 @@ +import { SERVER_ICON, SERVER_INSTRUCTIONS, SERVER_NAME, SERVER_VERSION, SERVER_WEBSITE_URL } from "@/config"; +import { getAllTools } from "@/tools"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; + +/** + * Creates and configures a new MCP server instance with all registered tools. + */ +export function createMcpServer(): McpServer { + const server = new McpServer( + { + name: SERVER_NAME, + version: SERVER_VERSION, + icons: [SERVER_ICON], + websiteUrl: SERVER_WEBSITE_URL + }, + { + capabilities: { + logging: {}, + prompts: {}, + resources: {}, + tools: {} + }, + instructions: SERVER_INSTRUCTIONS + } + ); + + registerTools(server); + + return server; +} + +/** + * Registers all available tools with the MCP server. + */ +function registerTools(server: McpServer): void { + const tools = getAllTools(); + + for (const tool of tools) { + server.registerTool( + tool.name, + { + title: tool.title, + description: tool.description, + inputSchema: tool.inputSchema + }, + tool.handler + ); + } +} diff --git a/packages/pluggable-widgets-mcp/src/server/session.ts b/packages/pluggable-widgets-mcp/src/server/session.ts new file mode 100644 index 0000000000..4015739e11 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/session.ts @@ -0,0 +1,77 @@ +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { randomUUID } from "node:crypto"; + +export interface Session { + transport: StreamableHTTPServerTransport; + createdAt: Date; +} + +/** + * Manages MCP sessions and their associated transports. + */ +export class SessionManager { + private sessions = new Map(); + + /** + * Creates a new transport with session lifecycle callbacks. + * The transport is added to sessions when initialized via the callback. + */ + createTransport(): StreamableHTTPServerTransport { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + onsessioninitialized: sessionId => { + this.sessions.set(sessionId, { + transport, + createdAt: new Date() + }); + console.log(`[MCP] Session initialized: ${sessionId}`); + }, + onsessionclosed: sessionId => { + this.sessions.delete(sessionId); + console.log(`[MCP] Session closed: ${sessionId}`); + } + }); + + return transport; + } + + /** + * Gets an existing session's transport by session ID. + */ + getTransport(sessionId: string): StreamableHTTPServerTransport | undefined { + return this.sessions.get(sessionId)?.transport; + } + + /** + * Checks if a session exists. + */ + hasSession(sessionId: string): boolean { + return this.sessions.has(sessionId); + } + + /** + * Gets the count of active sessions. + */ + get sessionCount(): number { + return this.sessions.size; + } + + /** + * Closes all sessions gracefully. + */ + async closeAll(): Promise { + const closePromises = Array.from(this.sessions.entries()).map(async ([sessionId, session]) => { + try { + console.log(`[MCP] Closing session: ${sessionId}`); + await session.transport.close(); + } catch (error) { + console.error(`[MCP] Error closing session ${sessionId}:`, error); + } + }); + + await Promise.all(closePromises); + this.sessions.clear(); + } +} + +export const sessionManager = new SessionManager(); diff --git a/packages/pluggable-widgets-mcp/src/server/stdio.ts b/packages/pluggable-widgets-mcp/src/server/stdio.ts new file mode 100644 index 0000000000..96039e6cd6 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/stdio.ts @@ -0,0 +1,31 @@ +import { createMcpServer } from "./server"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + +/** + * Starts the MCP server with STDIO transport. + * Communicates via stdin/stdout for CLI-based MCP clients. + */ +export async function startStdioServer(): Promise { + const server = createMcpServer(); + const transport = new StdioServerTransport(); + + // Log to stderr since stdout is used for MCP communication + console.error("[STDIO] Starting MCP server..."); + + await server.connect(transport); + + console.error("[STDIO] MCP server connected and ready"); + + setupGracefulShutdown(transport); +} + +function setupGracefulShutdown(transport: StdioServerTransport): void { + const shutdown = async (): Promise => { + console.error("\n[STDIO] Shutting down server..."); + await transport.close(); + process.exit(0); + }; + + process.on("SIGINT", shutdown); + process.on("SIGTERM", shutdown); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts new file mode 100644 index 0000000000..e80ee7c511 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -0,0 +1,13 @@ +import type { AnyToolDefinition } from "@/tools/types"; +import { getScaffoldingTools } from "./scaffolding.tools"; + +/** + * Gets all tool definitions for registration with the MCP server. + */ +export function getAllTools(): AnyToolDefinition[] { + const tools: AnyToolDefinition[] = []; + + tools.push(...getScaffoldingTools()); + + return tools; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts new file mode 100644 index 0000000000..962a8a7282 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -0,0 +1,157 @@ +import { mkdir } from "node:fs/promises"; +import { z } from "zod"; +import { GENERATIONS_DIR } from "@/config"; +import type { ToolContext, ToolDefinition, ToolResponse } from "@/tools/types"; +import { + buildWidgetOptions, + DEFAULT_WIDGET_OPTIONS, + GENERATOR_PROMPTS, + runWidgetGenerator, + SCAFFOLD_PROGRESS +} from "@/tools/utils/generator"; +import { ProgressTracker } from "@/tools/utils/progress-tracker"; +import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; + +const createWidgetSchema = z.object({ + name: z + .string() + .min(1) + .max(100) + .describe("[REQUIRED] The name of the widget in PascalCase (e.g., 'MyAwesomeWidget', 'DataChart')"), + description: z.string().min(1).max(200).describe("[REQUIRED] A brief description of what the widget does"), + version: z + .string() + .regex(/^\d+\.\d+\.\d+$/, "Version must be in semver format: x.y.z") + .optional() + .describe(`[OPTIONAL] Initial version in semver format. Default: "${DEFAULT_WIDGET_OPTIONS.version}"`), + author: z + .string() + .min(1) + .max(100) + .optional() + .describe(`[OPTIONAL] Author name. Default: "${DEFAULT_WIDGET_OPTIONS.author}"`), + license: z + .string() + .min(1) + .max(50) + .optional() + .describe(`[OPTIONAL] License type. Default: "${DEFAULT_WIDGET_OPTIONS.license}"`), + organization: z + .string() + .min(1) + .max(100) + .optional() + .describe( + `[OPTIONAL] Organization name for the widget namespace. Default: "${DEFAULT_WIDGET_OPTIONS.organization}"` + ), + template: z + .enum(["full", "empty"]) + .optional() + .describe( + `[OPTIONAL] Widget template: "full" includes sample code and examples, "empty" is minimal/blank. Default: "${DEFAULT_WIDGET_OPTIONS.template}"` + ), + programmingLanguage: z + .enum(["typescript", "javascript"]) + .optional() + .describe( + `[OPTIONAL] Programming language for the widget source code. Default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}"` + ), + unitTests: z + .boolean() + .optional() + .describe(`[OPTIONAL] Include unit test setup with Jest. Default: ${DEFAULT_WIDGET_OPTIONS.unitTests}`), + e2eTests: z + .boolean() + .optional() + .describe( + `[OPTIONAL] Include end-to-end test setup with Playwright. Default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}` + ) +}); + +type CreateWidgetInput = z.infer; + +const CREATE_WIDGET_DESCRIPTION = `Scaffolds a new Mendix pluggable widget using the official @mendix/generator-widget. + +BEFORE RUNNING: Please confirm all options with the user. Show them the full list of configurable parameters: + +REQUIRED: + • name: Widget name in PascalCase (e.g., "MyAwesomeWidget") + • description: Brief description of what the widget does + +OPTIONAL (with defaults): + • version: Initial version (default: "${DEFAULT_WIDGET_OPTIONS.version}") + • author: Author name (default: "${DEFAULT_WIDGET_OPTIONS.author}") + • license: License type (default: "${DEFAULT_WIDGET_OPTIONS.license}") + • organization: Namespace organization (default: "${DEFAULT_WIDGET_OPTIONS.organization}") + • template: "full" (with examples) or "empty" (minimal) (default: "${DEFAULT_WIDGET_OPTIONS.template}") + • programmingLanguage: "typescript" or "javascript" (default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}") + • unitTests: Include Jest test setup (default: ${DEFAULT_WIDGET_OPTIONS.unitTests}) + • e2eTests: Include Playwright E2E tests (default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}) + +Ask the user if they want to customize any options before proceeding.`; + +export function getScaffoldingTools(): Array> { + return [ + { + name: "create-widget", + title: "Create Widget", + description: CREATE_WIDGET_DESCRIPTION, + inputSchema: createWidgetSchema, + handler: handleCreateWidget + } + ]; +} + +async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext): Promise { + const options = buildWidgetOptions(args); + const tracker = new ProgressTracker({ + context, + logger: "scaffolding", + totalSteps: GENERATOR_PROMPTS.length + }); + + try { + console.error(`[create-widget] Starting widget scaffolding for "${options.name}"...`); + await tracker.progress(SCAFFOLD_PROGRESS.START, `Starting widget scaffolding for "${options.name}"...`); + await tracker.info(`Starting widget scaffolding for "${options.name}"...`, { + widgetName: options.name, + template: options.template, + organization: options.organization + }); + + // Ensure generations directory exists + await mkdir(GENERATIONS_DIR, { recursive: true }); + + const widgetFolder = await runWidgetGenerator(options, tracker); + const widgetPath = `${GENERATIONS_DIR}/${widgetFolder}`; + + console.error(`[create-widget] Widget created successfully at ${widgetPath}`); + await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget created successfully!"); + await tracker.info("Widget created successfully!", { + widgetName: options.name, + path: widgetPath + }); + + return createToolResponse( + [ + `Widget "${options.name}" created successfully!`, + "", + `Location: ${widgetPath}`, + "", + "Next steps:", + `1. cd ${widgetPath}`, + "2. pnpm install", + "3. pnpm start (to build and watch for changes)", + "", + "The widget will be available in Mendix Studio Pro after syncing the app directory." + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + await tracker.error(`Failed to create widget: ${message}`, { + widgetName: options.name, + error: message + }); + return createErrorResponse(`Failed to create widget: ${message}`); + } +} diff --git a/packages/pluggable-widgets-mcp/src/tools/types.ts b/packages/pluggable-widgets-mcp/src/tools/types.ts new file mode 100644 index 0000000000..65b1e252b8 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/types.ts @@ -0,0 +1,69 @@ +import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; +import type { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js"; +import type { ZodType } from "zod"; + +// ============================================================================= +// MCP Core Types +// ============================================================================= + +/** + * Standard response format for MCP tool handlers. + * Index signature required for MCP SDK compatibility. + */ +export interface ToolResponse { + [key: string]: unknown; + content: Array<{ type: "text"; text: string }>; +} + +/** + * Extra context provided to tool handlers by the MCP server. + */ +export type ToolContext = RequestHandlerExtra; + +/** + * Type for tool handler functions. + */ +export type ToolHandler = (args: T, context: ToolContext) => Promise; + +/** + * Definition for an MCP tool. + */ +export interface ToolDefinition { + name: string; + title: string; + description: string; + inputSchema: ZodType; + handler: ToolHandler; +} + +/** + * Type for collections of tools with heterogeneous input types. + * Uses 'any' because TypeScript's variance rules prevent using 'unknown' + * for handlers that only accept specific input types. + */ +export type AnyToolDefinition = ToolDefinition; + +/** + * Log levels supported by MCP logging notifications. + */ +export type LogLevel = "debug" | "info" | "notice" | "warning" | "error"; + +// ============================================================================= +// Widget Generator Types +// ============================================================================= + +/** + * Options for creating a new Mendix pluggable widget. + */ +export interface WidgetOptions { + name: string; + description: string; + version: string; + author: string; + license: string; + organization?: string; + template?: "full" | "empty"; + programmingLanguage?: "typescript" | "javascript"; + unitTests?: boolean; + e2eTests?: boolean; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts new file mode 100644 index 0000000000..6fc590b63c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -0,0 +1,250 @@ +import * as pty from "node-pty"; +import { GENERATIONS_DIR, SCAFFOLD_TIMEOUT_MS } from "@/config"; +import type { WidgetOptions } from "@/tools/types"; +import { ProgressTracker } from "./progress-tracker"; + +/** + * Generator prompt patterns in order - must match answers array. + */ +export const GENERATOR_PROMPTS = [ + "What is the name", + "Enter a description", + "organization", + "copyright", + "license", + "version", + "author", + "path", + "programming language", + "type of components", + "type of widget", + "template", + "unit tests", + "end-to-end" +] as const; + +/** + * Progress milestones for widget scaffolding. + */ +export const SCAFFOLD_PROGRESS = { + START: 0, + PROMPTS_START: 5, + PROMPTS_END: 70, + INSTALLING: 75, + COMPLETE: 100 +} as const; + +/** + * Default values for widget options. + */ +export const DEFAULT_WIDGET_OPTIONS = { + version: "1.0.0", + author: "Mendix", + license: "Apache-2.0", + organization: "Mendix", + template: "empty" as const, + programmingLanguage: "typescript" as const, + unitTests: true, + e2eTests: false +} as const; + +/** + * Local state for tracking generator process progress. + */ +interface GeneratorLocalState { + output: string; + answerIndex: number; + promptMatchedIndex: number; + allPromptsAnswered: boolean; +} + +/** + * Builds widget options from input arguments with defaults applied. + */ +export function buildWidgetOptions( + args: Partial & Pick +): WidgetOptions { + return { + name: args.name, + description: args.description, + version: args.version ?? DEFAULT_WIDGET_OPTIONS.version, + author: args.author ?? DEFAULT_WIDGET_OPTIONS.author, + license: args.license ?? DEFAULT_WIDGET_OPTIONS.license, + organization: args.organization ?? DEFAULT_WIDGET_OPTIONS.organization, + template: args.template ?? DEFAULT_WIDGET_OPTIONS.template, + programmingLanguage: DEFAULT_WIDGET_OPTIONS.programmingLanguage, + unitTests: args.unitTests ?? DEFAULT_WIDGET_OPTIONS.unitTests, + e2eTests: args.e2eTests ?? DEFAULT_WIDGET_OPTIONS.e2eTests + }; +} + +/** + * Builds the answers array for the generator prompts. + */ +export function buildGeneratorAnswers(options: WidgetOptions): string[] { + return [ + "", // Widget name - already passed as CLI arg + options.description, + options.organization ?? DEFAULT_WIDGET_OPTIONS.organization, + "© Mendix Technology BV 2025", // Copyright + options.license, + options.version, + options.author, + "../", // Project path (relative to widget folder inside generations/) + "", // Programming language - Enter for TypeScript (default) + "", // Component type - Enter for Function Components (default) + "", // Platform - Enter for web (default) + options.template ?? DEFAULT_WIDGET_OPTIONS.template, + options.unitTests !== false ? "yes" : "no", + options.e2eTests === true ? "yes" : "no" + ]; +} + +/** + * Calculates progress percentage for a given prompt index. + */ +export function calculatePromptProgress(promptIndex: number): number { + const progressRange = SCAFFOLD_PROGRESS.PROMPTS_END - SCAFFOLD_PROGRESS.PROMPTS_START; + const promptProgress = (promptIndex / GENERATOR_PROMPTS.length) * progressRange; + return Math.round(SCAFFOLD_PROGRESS.PROMPTS_START + promptProgress); +} + +/** + * Removes ANSI escape codes and spinner characters from terminal output. + */ +export function cleanTerminalOutput(data: string): string { + return ( + data + // eslint-disable-next-line no-control-regex -- Intentionally matching ANSI escape sequences + .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") + .replace(/[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/g, "") + .replace(/[\r\n]+/g, " ") + .replace(/\[[\dD\dC\dK\dG]+/g, "") + .trim() + ); +} + +/** + * Handles generator output and sends answers when prompts are detected. + */ +function handleGeneratorOutput( + state: GeneratorLocalState, + tracker: ProgressTracker, + sendNextAnswer: () => void, + onAllPromptsAnswered: () => void +): void { + if (state.answerIndex < GENERATOR_PROMPTS.length) { + // Skip if we've already matched this prompt + if (state.promptMatchedIndex >= state.answerIndex) { + return; + } + + const expectedPattern = GENERATOR_PROMPTS[state.answerIndex]; + const recentOutput = state.output.slice(-500).toLowerCase(); + + if (recentOutput.includes(expectedPattern.toLowerCase())) { + state.promptMatchedIndex = state.answerIndex; + tracker.updateStep(expectedPattern, state.answerIndex + 1); + + const progress = calculatePromptProgress(state.answerIndex + 1); + const message = `Configuring: ${expectedPattern}`; + + tracker.progress(progress, message).catch(() => undefined); + tracker + .info(message, { + step: expectedPattern, + promptIndex: state.answerIndex + 1, + totalPrompts: GENERATOR_PROMPTS.length + }) + .catch(() => undefined); + + setTimeout(sendNextAnswer, 150); + } + } else { + onAllPromptsAnswered(); + } +} + +/** + * Runs the Mendix widget generator using node-pty for terminal interaction. + */ +export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTracker): Promise { + const answers = buildGeneratorAnswers(options); + + return new Promise((resolve, reject) => { + const state: GeneratorLocalState = { + output: "", + answerIndex: 0, + promptMatchedIndex: -1, + allPromptsAnswered: false + }; + + tracker.start("initializing"); + + const ptyProcess = pty.spawn("npx", ["@mendix/generator-widget", options.name], { + name: "xterm-color", + cols: 120, + rows: 30, + cwd: GENERATIONS_DIR, + env: { ...process.env, FORCE_COLOR: "0" } + }); + + const sendNextAnswer = (): void => { + if (state.answerIndex < answers.length) { + const answer = answers[state.answerIndex]; + const displayAnswer = answer === "" ? "(Enter)" : `"${answer}"`; + const idx = state.answerIndex + 1; + console.error(`[create-widget] [${idx}/${answers.length}] Sending: ${displayAnswer}`); + state.answerIndex++; + ptyProcess.write(answer + "\r"); + } + }; + + ptyProcess.onData(data => { + state.output += data; + handleGeneratorOutput(state, tracker, sendNextAnswer, () => { + if (!state.allPromptsAnswered) { + state.allPromptsAnswered = true; + tracker.updateStep("installing", GENERATOR_PROMPTS.length); + tracker.markComplete(); + console.error("[create-widget] Installing dependencies..."); + tracker.progress(SCAFFOLD_PROGRESS.INSTALLING, "Installing dependencies...").catch(() => undefined); + tracker.info("Installing dependencies...").catch(() => undefined); + } + }); + }); + + ptyProcess.onExit(({ exitCode }) => { + tracker.stop(); + if (exitCode === 0) { + const widgetFolder = `${options.name.toLowerCase()}-web`; + console.error(`[create-widget] Widget scaffolded successfully: ${widgetFolder}`); + resolve(widgetFolder); + } else { + console.error(`[create-widget] Widget scaffold failed with exit code ${exitCode}`); + const cleanOutput = cleanTerminalOutput(state.output); + tracker + .error(`Scaffold failed with exit code ${exitCode}`, { + lastOutput: cleanOutput.slice(-500) + }) + .catch(() => undefined); + reject(new Error(`Generator exited with code ${exitCode}\nOutput: ${cleanOutput.slice(-2000)}`)); + } + }); + + const timeout = setTimeout(() => { + tracker.stop(); + console.error("[create-widget] Widget scaffold timed out after 5 minutes"); + tracker + .error("Widget scaffold timed out after 5 minutes", { + step: tracker.state.step, + stepIndex: tracker.state.stepIndex + }) + .catch(() => undefined); + ptyProcess.kill(); + reject(new Error("Widget scaffold timed out after 5 minutes")); + }, SCAFFOLD_TIMEOUT_MS); + + ptyProcess.onExit(() => clearTimeout(timeout)); + }); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts b/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts new file mode 100644 index 0000000000..f214154620 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts @@ -0,0 +1,36 @@ +import type { LogLevel, ToolContext } from "@/tools/types"; + +/** + * Sends a progress notification to the MCP client. + * Only sends if the client provided a progressToken in the request. + */ +export async function sendProgress(context: ToolContext, progress: number, message?: string): Promise { + const progressToken = context._meta?.progressToken; + if (progressToken) { + await context.sendNotification({ + method: "notifications/progress", + params: { progressToken, progress, total: 100, message } + }); + } +} + +/** + * Sends a logging message notification to the MCP client. + * Works independently of progressToken and provides detailed context. + */ +export async function sendLogMessage( + context: ToolContext, + level: LogLevel, + message: string, + data?: Record, + logger = "mcp-tools" +): Promise { + await context.sendNotification({ + method: "notifications/message", + params: { + level, + logger, + data: { message, ...data } + } + }); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/progress-tracker.ts b/packages/pluggable-widgets-mcp/src/tools/utils/progress-tracker.ts new file mode 100644 index 0000000000..87d74637db --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/progress-tracker.ts @@ -0,0 +1,237 @@ +import type { LogLevel, ToolContext } from "@/tools/types"; +import { sendLogMessage, sendProgress } from "./notifications"; + +/** + * Default timing constants for progress tracking. + */ +const DEFAULT_HEARTBEAT_INTERVAL_MS = 3000; +const DEFAULT_STUCK_WARNING_MS = 10000; + +/** + * Configuration options for the ProgressTracker. + */ +export interface ProgressTrackerOptions { + /** MCP tool context for sending notifications */ + context: ToolContext; + /** Logger name used in notifications/message (e.g., "scaffolding", "build") */ + logger: string; + /** Interval in ms between heartbeat logs (default: 3000) */ + heartbeatIntervalMs?: number; + /** Time in ms before sending a stuck warning (default: 10000) */ + stuckWarningMs?: number; + /** Total number of steps for progress calculation */ + totalSteps?: number; +} + +/** + * Current state snapshot from the tracker. + */ +export interface ProgressTrackerState { + step: string; + stepIndex: number; + elapsedSeconds: number; + isComplete: boolean; +} + +/** + * A reusable progress tracker for MCP tools. + * + * Provides: + * - Heartbeat logging at regular intervals + * - Stuck detection with warnings + * - Convenient logging methods (info, warning, error, debug) + * - Progress notification helpers + * - Step tracking with timing + * + * @example + * ```typescript + * const tracker = new ProgressTracker({ + * context, + * logger: "scaffolding", + * totalSteps: 10 + * }); + * + * tracker.start("initializing"); + * + * // Update step when progressing + * tracker.updateStep("configuring", 1); + * await tracker.info("Configuration started"); + * + * // On completion + * tracker.stop(); + * await tracker.info("Complete!"); + * ``` + */ +export class ProgressTracker { + private readonly context: ToolContext; + private readonly logger: string; + private readonly heartbeatIntervalMs: number; + private readonly stuckWarningMs: number; + private readonly totalSteps: number; + + private startTime: number = 0; + private lastStepTime: number = 0; + private currentStep: string = "idle"; + private currentStepIndex: number = 0; + private stuckWarningShown: boolean = false; + private isComplete: boolean = false; + private heartbeatInterval?: ReturnType; + + constructor(options: ProgressTrackerOptions) { + this.context = options.context; + this.logger = options.logger; + this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS; + this.stuckWarningMs = options.stuckWarningMs ?? DEFAULT_STUCK_WARNING_MS; + this.totalSteps = options.totalSteps ?? 0; + } + + /** + * Starts the progress tracker with optional initial step. + * Begins heartbeat interval for periodic status updates. + */ + start(initialStep = "starting"): void { + this.startTime = Date.now(); + this.lastStepTime = Date.now(); + this.currentStep = initialStep; + this.currentStepIndex = 0; + this.stuckWarningShown = false; + this.isComplete = false; + + this.heartbeatInterval = setInterval(() => { + this.sendHeartbeat(); + this.checkStuckWarning(); + }, this.heartbeatIntervalMs); + } + + /** + * Stops the progress tracker and cleans up intervals. + * Should be called when the operation completes or fails. + */ + stop(): void { + if (this.heartbeatInterval) { + clearInterval(this.heartbeatInterval); + this.heartbeatInterval = undefined; + } + } + + /** + * Updates the current step being tracked. + * Resets the stuck warning timer. + */ + updateStep(step: string, index?: number): void { + this.currentStep = step; + if (index !== undefined) { + this.currentStepIndex = index; + } + this.lastStepTime = Date.now(); + this.stuckWarningShown = false; + } + + /** + * Marks the tracker as complete. + * Subsequent stuck warnings will not be sent. + */ + markComplete(): void { + this.isComplete = true; + } + + /** + * Gets the elapsed time in seconds since start. + */ + get elapsedSeconds(): number { + if (this.startTime === 0) { + return 0; + } + return Math.round((Date.now() - this.startTime) / 1000); + } + + /** + * Gets the current state snapshot. + */ + get state(): ProgressTrackerState { + return { + step: this.currentStep, + stepIndex: this.currentStepIndex, + elapsedSeconds: this.elapsedSeconds, + isComplete: this.isComplete + }; + } + + /** + * Sends a log message with the specified level. + */ + async log(level: LogLevel, message: string, data?: Record): Promise { + await sendLogMessage(this.context, level, message, data, this.logger); + } + + /** + * Sends an info-level log message. + */ + async info(message: string, data?: Record): Promise { + await this.log("info", message, data); + } + + /** + * Sends a warning-level log message. + */ + async warning(message: string, data?: Record): Promise { + await this.log("warning", message, data); + } + + /** + * Sends an error-level log message. + */ + async error(message: string, data?: Record): Promise { + await this.log("error", message, data); + } + + /** + * Sends a debug-level log message. + */ + async debug(message: string, data?: Record): Promise { + await this.log("debug", message, data); + } + + /** + * Sends a progress notification to the client. + */ + async progress(value: number, message?: string): Promise { + await sendProgress(this.context, value, message); + } + + /** + * Sends a heartbeat debug log with current status. + */ + private sendHeartbeat(): void { + const elapsed = this.elapsedSeconds; + const stepInfo = this.totalSteps > 0 ? ` [${this.currentStepIndex}/${this.totalSteps}]` : ""; + + this.debug(`In progress...${stepInfo} (${elapsed}s elapsed)`, { + step: this.currentStep, + stepIndex: this.currentStepIndex, + totalSteps: this.totalSteps, + elapsedSeconds: elapsed + }).catch(() => undefined); + } + + /** + * Checks if the current step has exceeded the stuck warning threshold. + */ + private checkStuckWarning(): void { + if (this.isComplete || this.stuckWarningShown) { + return; + } + + const timeSinceLastStep = Date.now() - this.lastStepTime; + if (timeSinceLastStep > this.stuckWarningMs) { + this.stuckWarningShown = true; + const waitingSec = Math.round(timeSinceLastStep / 1000); + + this.warning(`Waiting for response (step: ${this.currentStep}, ${waitingSec}s elapsed)`, { + step: this.currentStep, + stepIndex: this.currentStepIndex, + waitingSeconds: waitingSec + }).catch(() => undefined); + } + } +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts new file mode 100644 index 0000000000..7ac521fc25 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts @@ -0,0 +1,19 @@ +import type { ToolResponse } from "@/tools/types"; + +/** + * Creates a successful tool response with text content. + */ +export function createToolResponse(text: string): ToolResponse { + return { + content: [{ type: "text", text }] + }; +} + +/** + * Creates an error tool response with a message. + */ +export function createErrorResponse(message: string): ToolResponse { + return { + content: [{ type: "text", text: message }] + }; +} diff --git a/packages/pluggable-widgets-mcp/tsconfig.json b/packages/pluggable-widgets-mcp/tsconfig.json new file mode 100644 index 0000000000..91fd5af0b7 --- /dev/null +++ b/packages/pluggable-widgets-mcp/tsconfig.json @@ -0,0 +1,34 @@ +{ + "include": ["./src", "config.ts"], + "compilerOptions": { + "lib": ["ES2022"], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "module": "esnext", + "moduleResolution": "node", + "target": "ES2022", + "outDir": "./dist", + "rootDir": "./src", + "types": ["node"], + "strict": true, + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedParameters": true, + "noUnusedLocals": true, + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "useUnknownInCatchVariables": false, + "exactOptionalPropertyTypes": false + }, + "exclude": ["node_modules", "dist"] +} diff --git a/packages/pluggable-widgets-mcp/tscpaths.json b/packages/pluggable-widgets-mcp/tscpaths.json new file mode 100644 index 0000000000..b8f0327956 --- /dev/null +++ b/packages/pluggable-widgets-mcp/tscpaths.json @@ -0,0 +1,4 @@ +{ + "resolveFullPaths": true, + "resolveFullExtension": ".js" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 608fc4ff4b..ec2485bd7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ importers: version: 8.0.3 turbo: specifier: ^2.5.4 - version: 2.9.18 + version: 2.5.8 automation/e2e-mcp: dependencies: @@ -124,25 +124,25 @@ importers: devDependencies: '@axe-core/playwright': specifier: ^4.11.1 - version: 4.11.3(playwright-core@1.61.0) + version: 4.12.1(playwright-core@1.62.1) '@eslint/js': specifier: ^9.39.4 - version: 9.39.4 + version: 9.39.5 '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../packages/shared/prettier-config-web-widgets '@playwright/test': specifier: ^1.60.0 - version: 1.61.0 + version: 1.62.1 '@types/node': specifier: ~24.12.0 version: 24.12.4 eslint-plugin-playwright: specifier: ^2.9.0 - version: 2.10.4(eslint@9.39.4(jiti@2.6.1)) + version: 2.11.0(eslint@9.39.3(jiti@2.6.1)) globals: specifier: ^17.4.0 - version: 17.6.0 + version: 17.9.0 playwright-ctrf-json-reporter: specifier: ^0.0.27 version: 0.0.27 @@ -151,7 +151,7 @@ importers: dependencies: '@commitlint/cli': specifier: ^21.2.1 - version: 21.2.1(@types/node@24.12.4)(conventional-commits-parser@7.1.0)(typescript@6.0.3) + version: 21.2.1(@types/node@24.12.4)(conventional-commits-parser@7.1.2)(typescript@5.9.3) '@commitlint/config-conventional': specifier: ^21.2.0 version: 21.2.0 @@ -160,7 +160,7 @@ importers: version: link:../../packages/shared/prettier-config-web-widgets pretty-quick: specifier: ^4.1.1 - version: 4.2.2(prettier@3.8.4) + version: 4.2.2(prettier@3.9.6) automation/snapshot-generator: dependencies: @@ -170,13 +170,13 @@ importers: devDependencies: '@eslint/js': specifier: ^9.32.0 - version: 9.39.4 + version: 9.37.0 '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../packages/shared/prettier-config-web-widgets globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.9.0 automation/utils: devDependencies: @@ -206,7 +206,7 @@ importers: version: 5.1.1 fast-xml-parser: specifier: ^4.1.3 - version: 4.5.6 + version: 4.5.3 glob: specifier: ^11.1.0 version: 11.1.0 @@ -224,7 +224,7 @@ importers: version: 0.8.5 ts-node: specifier: ^10.9.1 - version: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3) + version: 10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3) zod: specifier: ^3.25.76 version: 3.25.76 @@ -286,10 +286,10 @@ importers: version: link:../../shared/prettier-config-web-widgets '@rollup/plugin-node-resolve': specifier: ^15.3.1 - version: 15.3.1(rollup@4.62.0) + version: 15.3.1(rollup@3.29.5) '@rollup/plugin-terser': specifier: ^1.0.0 - version: 1.0.0(rollup@4.62.0) + version: 1.0.0(rollup@3.29.5) concurrently: specifier: ^6.5.1 version: 6.5.1 @@ -298,7 +298,7 @@ importers: version: 0.1.8 rollup: specifier: '*' - version: 4.62.0 + version: 3.29.5 xlsx: specifier: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz version: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz @@ -352,7 +352,7 @@ importers: devDependencies: '@eslint/js': specifier: ^9.32.0 - version: 9.39.4 + version: 9.37.0 '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -361,7 +361,7 @@ importers: version: link:../../shared/prettier-config-web-widgets globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.9.0 packages/pluggableWidgets/accessibility-helper-web: dependencies: @@ -377,7 +377,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -402,7 +402,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -429,11 +429,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -442,7 +442,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -470,7 +470,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -501,7 +501,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -528,11 +528,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -541,7 +541,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -559,7 +559,7 @@ importers: version: 2.5.1 jsbarcode: specifier: ^3.12.1 - version: 3.12.3 + version: 3.12.1 qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@18.3.1) @@ -572,7 +572,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -609,7 +609,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -627,7 +627,7 @@ importers: version: link:../../shared/widget-plugin-platform '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.2(rollup@4.62.4) packages/pluggableWidgets/bubble-chart-web: dependencies: @@ -642,11 +642,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -655,7 +655,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -676,10 +676,10 @@ importers: version: 2.5.1 date-fns: specifier: ^4.1.0 - version: 4.4.0 + version: 4.1.0 react-big-calendar: specifier: ^1.19.4 - version: 1.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -689,7 +689,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -725,7 +725,7 @@ importers: version: 2.5.1 swiper: specifier: ^12.1.2 - version: 12.2.0 + version: 12.1.2 devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -735,7 +735,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -778,7 +778,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -849,7 +849,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -895,7 +895,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -910,7 +910,7 @@ importers: version: link:../../shared/widget-plugin-platform '@types/react-color': specifier: ^2.17.6 - version: 2.17.12(@types/react@19.2.17) + version: 2.17.12(@types/react@19.2.2) cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -928,11 +928,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -941,7 +941,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -968,7 +968,7 @@ importers: version: 7.6.2(react@18.3.1) match-sorter: specifier: ^8.1.0 - version: 8.3.0 + version: 8.1.0 devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -978,7 +978,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1032,14 +1032,14 @@ importers: version: 4.0.7(patch_hash=47fd2d1b5c35554ddd4fa32fcaa928a16fda9f82dca0ff68bcdc1f7c3e5f9d1a)(mobx@6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1076,7 +1076,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1122,7 +1122,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1162,7 +1162,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1220,7 +1220,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1259,13 +1259,13 @@ importers: version: link:../../shared/widget-plugin-platform '@radix-ui/react-progress': specifier: ^1.1.7 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.1.7(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) brandi: specifier: ^5.0.0 - version: 5.1.0 + version: 5.0.0 brandi-react: specifier: ^5.0.0 - version: 5.1.0(brandi@5.1.0)(react@18.3.1) + version: 5.0.0(brandi@5.0.0)(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -1287,7 +1287,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1321,7 +1321,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1351,32 +1351,32 @@ importers: version: 4.8.69 react-pdf: specifier: ^9.2.1 - version: 9.2.1(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 9.2.1(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) xlsx: specifier: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz version: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz devDependencies: '@babel/plugin-transform-class-properties': specifier: ^7.27.1 - version: 7.29.7(@babel/core@7.29.7) + version: 7.27.1(@babel/core@7.29.7) '@babel/plugin-transform-private-methods': specifier: ^7.27.1 - version: 7.29.7(@babel/core@7.29.7) + version: 7.27.1(@babel/core@7.29.7) '@babel/plugin-transform-private-property-in-object': specifier: ^7.27.1 - version: 7.29.7(@babel/core@7.29.7) + version: 7.27.1(@babel/core@7.29.7) '@mendix/eslint-config-web-widgets': specifier: workspace:* version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/rollup-web-widgets': specifier: workspace:* version: link:../../shared/rollup-web-widgets '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.2(rollup@4.62.4) packages/pluggableWidgets/dropdown-sort-web: dependencies: @@ -1407,7 +1407,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1432,7 +1432,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1469,7 +1469,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1502,7 +1502,7 @@ importers: version: 4.0.7(patch_hash=47fd2d1b5c35554ddd4fa32fcaa928a16fda9f82dca0ff68bcdc1f7c3e5f9d1a)(mobx@6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dropzone: specifier: ^14.2.3 - version: 14.4.1(patch_hash=d30fd95f2a3d58218fd5d657104b52cad6924893c0ac0e173f51c8c2d8e179b6)(react@18.3.1) + version: 14.3.8(patch_hash=d30fd95f2a3d58218fd5d657104b52cad6924893c0ac0e173f51c8c2d8e179b6)(react@18.3.1) devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -1512,7 +1512,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1524,7 +1524,7 @@ importers: version: link:../../shared/widget-plugin-test-utils '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.62.0) + version: 6.1.0(rollup@4.62.4) '@types/big.js': specifier: ^6.2.2 version: 6.2.2 @@ -1554,10 +1554,10 @@ importers: version: link:../../shared/widget-plugin-sorting brandi: specifier: ^5.0.0 - version: 5.1.0 + version: 5.0.0 brandi-react: specifier: ^5.0.0 - version: 5.1.0(brandi@5.1.0)(react@18.3.1) + version: 5.0.0(brandi@5.0.0)(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -1576,7 +1576,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1613,7 +1613,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1634,11 +1634,11 @@ importers: version: 2.30.0 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -1647,7 +1647,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1668,7 +1668,7 @@ importers: version: link:../../shared/widget-plugin-component-kit dompurify: specifier: ^3.4.12 - version: 3.4.12 + version: 3.4.13 devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -1678,7 +1678,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1702,7 +1702,7 @@ importers: version: 4.0.7(patch_hash=47fd2d1b5c35554ddd4fa32fcaa928a16fda9f82dca0ff68bcdc1f7c3e5f9d1a)(mobx@6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-image-crop: specifier: ^11.0.10 - version: 11.0.10(react@18.3.1) + version: 11.1.2(react@18.3.1) devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -1712,7 +1712,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1749,7 +1749,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1779,11 +1779,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -1792,7 +1792,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1816,10 +1816,10 @@ importers: version: 0.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) brandi: specifier: ^5.0.0 - version: 5.1.0 + version: 5.0.0 brandi-react: specifier: ^5.0.0 - version: 5.1.0(brandi@5.1.0)(react@18.3.1) + version: 5.0.0(brandi@5.0.0)(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -1838,7 +1838,7 @@ importers: devDependencies: '@googlemaps/jest-mocks': specifier: ^2.10.0 - version: 2.22.8 + version: 2.22.6 '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -1847,7 +1847,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1896,7 +1896,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1935,11 +1935,11 @@ importers: version: 2.30.0 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -1948,7 +1948,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -1979,7 +1979,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2013,7 +2013,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2047,7 +2047,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2071,7 +2071,7 @@ importers: version: 2.5.1 pusher-js: specifier: ^8.5.0 - version: 8.5.0 + version: 8.6.0 devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -2081,7 +2081,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2114,10 +2114,10 @@ importers: version: link:../../shared/widget-plugin-platform '@rc-component/slider': specifier: ^1.0.1 - version: 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rc-component/tooltip': specifier: ^1.3.3 - version: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -2130,7 +2130,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2164,7 +2164,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2191,10 +2191,10 @@ importers: version: 6.4.11 '@codemirror/state': specifier: ^6.5.2 - version: 6.6.0 + version: 6.5.2 '@floating-ui/dom': specifier: ^1.7.4 - version: 1.7.6 + version: 1.7.4 '@floating-ui/react': specifier: ^0.26.27 version: 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2203,10 +2203,10 @@ importers: version: 0.25.0 '@uiw/codemirror-theme-github': specifier: ^4.23.13 - version: 4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) + version: 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6) '@uiw/react-codemirror': specifier: ^4.23.13 - version: 4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.1)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.25.2(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.6)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -2215,10 +2215,10 @@ importers: version: 2.0.3 katex: specifier: ^0.16.22 - version: 0.16.47 + version: 0.16.25 linkifyjs: specifier: ^4.3.2 - version: 4.3.3 + version: 4.3.2 lodash.merge: specifier: ^4.6.2 version: 4.6.2 @@ -2230,7 +2230,7 @@ importers: version: 2.0.3 quill-resize-module: specifier: ^2.0.4 - version: 2.1.3 + version: 2.0.8 devDependencies: '@mendix/automation-utils': specifier: workspace:* @@ -2240,7 +2240,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2264,22 +2264,22 @@ importers: version: link:../../shared/widget-plugin-test-utils '@rollup/plugin-alias': specifier: ^5.1.1 - version: 5.1.1(rollup@4.62.0) + version: 5.1.1(rollup@4.62.4) '@rollup/plugin-image': specifier: ^3.0.3 - version: 3.0.3(rollup@4.62.0) + version: 3.0.3(rollup@4.62.4) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.62.0) + version: 6.1.0(rollup@4.62.4) '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.2(rollup@4.62.4) '@types/js-beautify': specifier: ^1.14.3 version: 1.14.3 '@types/katex': specifier: ^0.16.7 - version: 0.16.8 + version: 0.16.7 '@types/sanitize-html': specifier: ^1.27.2 version: 1.27.2 @@ -2288,19 +2288,19 @@ importers: version: 7.0.3 postcss: specifier: ^8.5.6 - version: 8.5.15 + version: 8.5.6 postcss-import: specifier: ^16.1.1 - version: 16.1.1(postcss@8.5.15) + version: 16.1.1(postcss@8.5.6) postcss-url: specifier: ^10.1.3 - version: 10.1.4(postcss@8.5.15) + version: 10.1.3(postcss@8.5.6) rollup-plugin-postcss: specifier: ^4.0.2 - version: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) + version: 4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) rollup-preserve-directives: specifier: ^1.1.3 - version: 1.1.3(rollup@4.62.0) + version: 1.1.3(rollup@4.62.4) packages/pluggableWidgets/selection-helper-web: dependencies: @@ -2316,7 +2316,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2350,7 +2350,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2390,7 +2390,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2417,10 +2417,10 @@ importers: version: link:../../shared/widget-plugin-platform '@rc-component/slider': specifier: ^1.0.1 - version: 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rc-component/tooltip': specifier: ^1.3.3 - version: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -2433,7 +2433,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2461,7 +2461,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2494,11 +2494,11 @@ importers: version: 2.5.1 plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 devDependencies: '@happy-dom/jest-environment': specifier: ^19.0.2 - version: 19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) + version: 19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1) '@mendix/automation-utils': specifier: workspace:* version: link:../../../automation/utils @@ -2507,7 +2507,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2538,7 +2538,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2572,7 +2572,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2603,7 +2603,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2631,7 +2631,7 @@ importers: version: link:../../shared/eslint-config-web-widgets '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../../shared/prettier-config-web-widgets @@ -2655,10 +2655,10 @@ importers: version: 6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9) plotly.js-dist-min: specifier: ^3.0.0 - version: 3.6.0 + version: 3.1.1 react-plotly.js: specifier: ^2.6.0 - version: 2.6.0(plotly.js@3.6.0(mapbox-gl@1.13.3))(react@18.3.1) + version: 2.6.0(plotly.js@3.1.1(mapbox-gl@1.13.3))(react@18.3.1) devDependencies: '@mendix/eslint-config-web-widgets': specifier: workspace:* @@ -2686,16 +2686,16 @@ importers: version: link:../widget-plugin-test-utils '@rollup/plugin-commonjs': specifier: ^29.0.3 - version: 29.0.3(rollup@4.62.0) + version: 29.0.3(rollup@3.29.5) '@rollup/plugin-node-resolve': specifier: ^15.3.1 - version: 15.3.1(rollup@4.62.0) + version: 15.3.1(rollup@3.29.5) '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.3(rollup@4.62.0) + version: 6.0.2(rollup@3.29.5) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.62.0) + version: 0.4.4(rollup@3.29.5) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -2704,7 +2704,7 @@ importers: version: 2.3.4 '@types/react-plotly.js': specifier: ^2.6.3 - version: 2.6.4 + version: 2.6.3 copy-and-watch: specifier: ^0.1.6 version: 0.1.8 @@ -2713,61 +2713,61 @@ importers: version: 4.4.1 rollup: specifier: '*' - version: 4.62.0 + version: 3.29.5 rollup-plugin-copy: specifier: ^3.5.0 version: 3.5.0 rollup-plugin-license: specifier: ^3.6.0 - version: 3.7.1(picomatch@4.0.4)(rollup@4.62.0) + version: 3.6.0(picomatch@4.0.3)(rollup@3.29.5) packages/shared/eslint-config-web-widgets: dependencies: '@eslint/js': specifier: ^9.39.3 - version: 9.39.4 + version: 9.39.5 '@mendix/prettier-config-web-widgets': specifier: workspace:* version: link:../prettier-config-web-widgets eslint: specifier: ^9.39.3 - version: 9.39.4(jiti@2.6.1) + version: 9.39.3(jiti@2.6.1) eslint-config-prettier: specifier: ^9.0.0 - version: 9.1.2(eslint@9.39.4(jiti@2.6.1)) + version: 9.1.2(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-cypress: specifier: ^5.1.1 - version: 5.4.0(eslint@9.39.4(jiti@2.6.1)) + version: 5.2.0(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1)) + version: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-jest: specifier: ^29.15.0 - version: 29.15.2(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@6.0.3) + version: 29.16.0(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@5.9.3) eslint-plugin-package-json: specifier: ^0.89.1 - version: 0.89.4(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1))(jsonc-eslint-parser@3.1.0) + version: 0.89.4(@types/estree@1.0.9)(eslint@9.39.3(jiti@2.6.1))(jsonc-eslint-parser@2.4.1) eslint-plugin-prettier: specifier: ^5.5.5 - version: 5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.8.4) + version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-promise: specifier: ^7.2.1 - version: 7.3.0(eslint@9.39.4(jiti@2.6.1)) + version: 7.2.1(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-react: specifier: ~7.37.5 - version: 7.37.5(eslint@9.39.4(jiti@2.6.1)) + version: 7.37.5(eslint@9.39.3(jiti@2.6.1)) eslint-plugin-react-hooks: specifier: 7.0.1 - version: 7.0.1(eslint@9.39.4(jiti@2.6.1)) + version: 7.0.1(eslint@9.39.3(jiti@2.6.1)) globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.9.0 prettier: specifier: ^3.8.1 - version: 3.8.4 + version: 3.9.6 typescript-eslint: specifier: ^8.57.0 - version: 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + version: 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) packages/shared/filter-commons: dependencies: @@ -2795,34 +2795,34 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 packages/shared/prettier-config-web-widgets: dependencies: '@eslint/js': specifier: ^9.34.0 - version: 9.39.4 + version: 9.37.0 '@prettier/plugin-xml': specifier: '>=3.4.1' - version: 3.4.2(prettier@3.8.4) + version: 3.4.2(prettier@3.9.6) eslint: specifier: ^9.39.3 - version: 9.39.4(jiti@2.6.1) + version: 9.39.3(jiti@2.6.1) globals: specifier: ^17.3.0 - version: 17.6.0 + version: 17.9.0 prettier: specifier: ^3.8.1 - version: 3.8.4 + version: 3.9.6 prettier-plugin-packagejson: specifier: ^2.5.19 - version: 2.5.22(prettier@3.8.4) + version: 2.5.19(prettier@3.9.6) packages/shared/rollup-web-widgets: devDependencies: '@mendix/pluggable-widgets-tools': specifier: 11.11.0 - version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) rollup-plugin-copy: specifier: ^3.5.0 version: 3.5.0 @@ -2842,16 +2842,16 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) classnames: specifier: ^2.5.1 version: 2.5.1 jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-dropdown-filter: dependencies: @@ -2872,7 +2872,7 @@ importers: version: link:../widget-plugin-mobx-kit downshift: specifier: ^9.0.9 - version: 9.3.6(react@18.3.1) + version: 9.0.10(react@18.3.1) mendix: specifier: ^10.24.75382 version: 10.24.75382 @@ -2910,13 +2910,13 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-filtering: dependencies: @@ -2925,7 +2925,7 @@ importers: version: 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@floating-ui/react-dom': specifier: ^2.1.2 - version: 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mendix/filter-commons': specifier: workspace:* version: link:../filter-commons @@ -2946,7 +2946,7 @@ importers: version: link:../widget-plugin-platform downshift: specifier: ^9.0.8 - version: 9.3.6(react@18.3.1) + version: 9.0.10(react@18.3.1) mendix: specifier: 10.24.75382 version: 10.24.75382 @@ -2971,16 +2971,16 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) date-fns: specifier: ^3.6.0 version: 3.6.0 jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-grid: dependencies: @@ -3008,16 +3008,16 @@ importers: version: link:../widget-plugin-test-utils '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) classnames: specifier: ^2.5.1 version: 2.5.1 jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-hooks: devDependencies: @@ -3035,16 +3035,16 @@ importers: version: link:../widget-plugin-platform '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) classnames: specifier: ^2.5.1 version: 2.5.1 jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-mobx-kit: dependencies: @@ -3069,10 +3069,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) optionalDependencies: react: specifier: '>=18.0.0 <19.0.0' @@ -3091,10 +3091,10 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) big.js: specifier: ^6.2.1 version: 6.2.2 @@ -3103,7 +3103,7 @@ importers: version: 2.5.1 jest-environment-jsdom: specifier: ^30.3.0 - version: 30.3.0(canvas@3.2.3) + version: 30.4.1(canvas@3.2.0) packages/shared/widget-plugin-sorting: dependencies: @@ -3156,34 +3156,42 @@ importers: version: link:../tsconfig-web-widgets '@swc/core': specifier: ^1.7.26 - version: 1.15.41 + version: 1.13.5 '@swc/jest': specifier: ^0.2.36 - version: 0.2.39(@swc/core@1.15.41) + version: 0.2.39(@swc/core@1.13.5) big.js: specifier: ^6.2.2 version: 6.2.2 packages: - '@adobe/css-tools@4.5.0': - resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@altano/repository-tools@2.0.3': - resolution: {integrity: sha512-cSR/ZYDF6Wp9OeAJMyLYYN1GenAAhV17W+w38ELP+3c5Ltsy9jkkCymi33nz/qnXyef3n6Fbr1h2yt3dvUN5sQ==} + '@altano/repository-tools@2.0.1': + resolution: {integrity: sha512-YE/52CkFtb+YtHPgbWPai7oo5N9AKnMuP5LM+i2AG7G1H2jdYBCO1iDnkDE3dZ3C1MIgckaF+d5PNRulgt0bdw==} '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@axe-core/playwright@4.11.3': - resolution: {integrity: sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==} + '@axe-core/playwright@4.12.1': + resolution: {integrity: sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw==} peerDependencies: playwright-core: '>= 1.0.0' + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} @@ -3199,89 +3207,176 @@ packages: '@babel/core': 7.29.7 eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - '@babel/generator@7.29.7': - resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.29.7': resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin@7.29.7': resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin@7.27.1': + resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin@7.29.7': resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider@0.6.8': resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.29.7': resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.29.7': resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.29.7': resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms@7.29.7': resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + '@babel/helper-optimise-call-expression@7.29.7': resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.29.7': resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-remap-async-to-generator@7.29.7': resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/helper-replace-supers@7.29.7': resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + engines: {node: '>=6.9.0'} + '@babel/helper-wrap-function@7.29.7': resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} engines: {node: '>=6.9.0'} @@ -3290,8 +3385,13 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.7': - resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} engines: {node: '>=6.0.0'} hasBin: true @@ -3331,8 +3431,8 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-proposal-export-default-from@7.29.7': - resolution: {integrity: sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==} + '@babel/plugin-proposal-export-default-from@7.27.1': + resolution: {integrity: sha512-hjlsMBl1aJc5lp8MoCDEZCiYzlgdRAShOjAfRw6X+GlpLpUPU7c3XNLsKFZbQk/1cRzBlJ7CXg3xJAJMrFa1Uw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3369,8 +3469,14 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-syntax-export-default-from@7.29.7': - resolution: {integrity: sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==} + '@babel/plugin-syntax-export-default-from@7.27.1': + resolution: {integrity: sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-syntax-flow@7.27.1': + resolution: {integrity: sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3387,6 +3493,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-import-attributes@7.29.7': resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} engines: {node: '>=6.9.0'} @@ -3403,6 +3515,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-jsx@7.29.7': resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} @@ -3451,6 +3569,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript@7.29.7': resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} @@ -3463,18 +3587,36 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-arrow-functions@7.29.7': resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-async-generator-functions@7.29.7': resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-async-to-generator@7.29.7': resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} engines: {node: '>=6.9.0'} @@ -3487,12 +3629,24 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-block-scoping@7.28.4': + resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-block-scoping@7.29.7': resolution: {integrity: sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-class-properties@7.29.7': resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} engines: {node: '>=6.9.0'} @@ -3505,18 +3659,36 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-classes@7.29.7': resolution: {integrity: sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-computed-properties@7.29.7': resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-destructuring@7.28.0': + resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-destructuring@7.29.7': resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} engines: {node: '>=6.9.0'} @@ -3565,18 +3737,36 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-flow-strip-types@7.27.1': + resolution: {integrity: sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-flow-strip-types@7.29.7': resolution: {integrity: sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-for-of@7.29.7': resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-function-name@7.29.7': resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} engines: {node: '>=6.9.0'} @@ -3589,12 +3779,24 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-literals@7.29.7': resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-logical-assignment-operators@7.27.1': + resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-logical-assignment-operators@7.29.7': resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} engines: {node: '>=6.9.0'} @@ -3613,14 +3815,20 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-modules-commonjs@7.29.7': resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-modules-systemjs@7.29.7': - resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} + '@babel/plugin-transform-modules-systemjs@7.29.8': + resolution: {integrity: sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3631,6 +3839,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} engines: {node: '>=6.9.0'} @@ -3643,18 +3857,36 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-numeric-separator@7.29.7': resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-object-rest-spread@7.29.7': resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} engines: {node: '>=6.9.0'} @@ -3667,30 +3899,60 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-optional-catch-binding@7.29.7': resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-optional-chaining@7.27.1': + resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-optional-chaining@7.29.7': resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-parameters@7.29.7': resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-private-methods@7.29.7': resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-private-property-in-object@7.29.7': resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} engines: {node: '>=6.9.0'} @@ -3703,6 +3965,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-display-name@7.29.7': resolution: {integrity: sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==} engines: {node: '>=6.9.0'} @@ -3715,14 +3983,14 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3739,8 +4007,14 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.7': - resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.8': + resolution: {integrity: sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3757,8 +4031,14 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-runtime@7.29.7': - resolution: {integrity: sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==} + '@babel/plugin-transform-runtime@7.28.3': + resolution: {integrity: sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3769,8 +4049,20 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/plugin-transform-spread@7.29.7': - resolution: {integrity: sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==} + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-transform-spread@7.29.8': + resolution: {integrity: sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': 7.29.7 @@ -3793,6 +4085,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-typescript@7.29.7': resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} engines: {node: '>=6.9.0'} @@ -3811,6 +4109,12 @@ packages: peerDependencies: '@babel/core': 7.29.7 + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-unicode-regex@7.29.7': resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} engines: {node: '>=6.9.0'} @@ -3858,20 +4162,32 @@ packages: peerDependencies: '@babel/core': 7.29.7 - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.7': - resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -3881,11 +4197,11 @@ packages: resolution: {integrity: sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==} hasBin: true - '@codemirror/autocomplete@6.20.3': - resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} + '@codemirror/autocomplete@6.19.0': + resolution: {integrity: sha512-61Hfv3cF07XvUxNeC3E7jhG8XNi1Yom1G0lRC936oLnlF+jrbrv8rc/J98XlYzcsAoTVupfsf5fLej1aI8kyIg==} - '@codemirror/commands@6.10.3': - resolution: {integrity: sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==} + '@codemirror/commands@6.9.0': + resolution: {integrity: sha512-454TVgjhO6cMufsyyGN70rGIfJxJEjcqjBG2x2Y03Y/+Fm99d3O/Kv1QDYWuG6hvxsgmjXmBuATikIIYvERX+w==} '@codemirror/lang-css@6.3.1': resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} @@ -3893,26 +4209,26 @@ packages: '@codemirror/lang-html@6.4.11': resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} - '@codemirror/lang-javascript@6.2.5': - resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} + '@codemirror/lang-javascript@6.2.4': + resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} - '@codemirror/language@6.12.3': - resolution: {integrity: sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==} + '@codemirror/language@6.11.3': + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} - '@codemirror/lint@6.9.7': - resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} + '@codemirror/lint@6.9.0': + resolution: {integrity: sha512-wZxW+9XDytH3SKvS8cQzMyQCaaazH8XL1EMHleHe00wVzsv7NBQKVW2yzEHrRhmM7ZOhVdItPbvlRBvMp9ej7A==} - '@codemirror/search@6.7.0': - resolution: {integrity: sha512-ZvGm99wc/s2cITtMT15LFdn8aH/aS+V+DqyGq/N5ZlV5vWtH+nILvC2nw0zX7ByNoHHDZ2IxxdW38O0tc5nVHg==} + '@codemirror/search@6.5.11': + resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} - '@codemirror/state@6.6.0': - resolution: {integrity: sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==} + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} '@codemirror/theme-one-dark@6.1.3': resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - '@codemirror/view@6.43.1': - resolution: {integrity: sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==} + '@codemirror/view@6.38.6': + resolution: {integrity: sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==} '@commitlint/cli@21.2.1': resolution: {integrity: sha512-blsZGe29hJ72VGEFVl72IVYX+1vsfINpjA9yWQA6i7OKD/McGEOXg08sKIRKjFk4JvzhV/9n0l3i6NooPLTNfg==} @@ -3983,12 +4299,12 @@ packages: resolution: {integrity: sha512-7zVFCDB2reMvJH5dmbKnOQPjZEvjdJTH8jc0U/PIPU1r3/+vf5pD1HlfitV2MWsWXrvu7u39iY1lyLUPOaN0Gw==} engines: {node: '>=22.12.0'} - '@conventional-changelog/git-client@3.1.0': - resolution: {integrity: sha512-Tqa/gHco2WJWa740NRjOrfKVvzIqxkZpecb8bemaQ8sKM5PXb1UK4uTyTb/1wIqNuOVaDOFxyBdhTIQZn6gdjQ==} + '@conventional-changelog/git-client@3.1.1': + resolution: {integrity: sha512-w/q+UIVdWQMgXlziPIYfPlyDud+H8kcvSCzDQQoc/gid7yZzb6eNfAkNN4UlEcS2cVVh924jevsOIb36d0In2g==} engines: {node: '>=22'} peerDependencies: conventional-commits-filter: ^6.0.1 - conventional-commits-parser: ^7.0.1 + conventional-commits-parser: ^7.1.2 peerDependenciesMeta: conventional-commits-filter: optional: true @@ -4040,18 +4356,28 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.2': - resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': @@ -4062,12 +4388,20 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + '@eslint/js@9.39.3': + resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -4078,14 +4412,26 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=18.0.0 <19.0.0' + react-dom: '>=18.0.0 <19.0.0' - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' @@ -4096,17 +4442,20 @@ packages: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@floating-ui/react@0.27.19': - resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} + '@floating-ui/react@0.27.20': + resolution: {integrity: sha512-CMqMy7OaXl9W0eq1Uy7L7i2Y/anPvHmFmESd2CEw0t5YvZhcVCeo4MBevAmswRllX7Y2dEidA4ozGPunLSTQpw==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@googlemaps/jest-mocks@2.22.8': - resolution: {integrity: sha512-r0Gh5F/KpDWVgnyQQYTkFbldxY9XUU4FPxv6Gs8nulvbEPR1fvnbTUXEzJp2O1h0RyK2VJLh1jk0mDwhUneFjQ==} + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@googlemaps/jest-mocks@2.22.6': + resolution: {integrity: sha512-t3n0l03OdGPEUCfWVC1a4xGgcE21+58tGdNsIjGWsTbsaMZBOfCxwTHvzmAx/H0dyPeKZ4uWmtahsyUQIcGInA==} '@happy-dom/jest-environment@19.0.2': resolution: {integrity: sha512-dRX5Xuiwevif8mPQK9EYDxub/Nz6JvPKzIwNv4cIDz8+dwUrAKxJzLmfsKeKImjLPad0zpo+6orUfgadoJCwFQ==} @@ -4118,22 +4467,18 @@ packages: jest-mock: '>=25.0.0' jest-util: '>=25.0.0' - '@hono/node-server@1.19.17': - resolution: {integrity: sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==} - engines: {node: '>=18.14.1'} + '@hono/node-server@2.1.0': + resolution: {integrity: sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==} + engines: {node: '>=20'} peerDependencies: hono: ^4 - '@humanfs/core@0.19.2': - resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.8': - resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/types@0.15.0': - resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': @@ -4153,16 +4498,12 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@isaacs/cliui@9.0.0': - resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} - engines: {node: '>=18'} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.6': - resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} '@jest/console@30.3.0': @@ -4178,8 +4519,8 @@ packages: node-notifier: optional: true - '@jest/create-cache-key-function@30.4.1': - resolution: {integrity: sha512-R+xGEtzA95NIsvpXJSROG4t01956dDOt17KpamguY4XOnGvdHNFFXE7Er0C1OAsRjOwiIxpKqOvGlznIGZIQlQ==} + '@jest/create-cache-key-function@30.2.0': + resolution: {integrity: sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/diff-sequences@30.3.0': @@ -4200,10 +4541,24 @@ packages: canvas: optional: true + '@jest/environment-jsdom-abstract@30.4.1': + resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + jsdom: '*' + peerDependenciesMeta: + canvas: + optional: true + '@jest/environment@30.3.0': resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/environment@30.4.1': + resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/expect-utils@30.3.0': resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4220,6 +4575,10 @@ packages: resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/fake-timers@30.4.1': + resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/get-type@30.1.0': resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4273,6 +4632,14 @@ packages: resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/transform@30.4.1': + resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.2.0': + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/types@30.3.0': resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4303,23 +4670,23 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@lezer/common@1.5.2': - resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} - '@lezer/css@1.3.3': - resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} + '@lezer/css@1.3.0': + resolution: {integrity: sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==} - '@lezer/highlight@1.2.3': - resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} - '@lezer/html@1.3.13': - resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} + '@lezer/html@1.3.12': + resolution: {integrity: sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==} '@lezer/javascript@1.5.4': resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - '@lezer/lr@1.4.10': - resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} '@mapbox/geojson-rewind@0.5.2': resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} @@ -4343,8 +4710,8 @@ packages: '@mapbox/tiny-sdf@1.2.5': resolution: {integrity: sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw==} - '@mapbox/tiny-sdf@2.2.0': - resolution: {integrity: sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==} + '@mapbox/tiny-sdf@2.0.7': + resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==} '@mapbox/unitbezier@0.0.0': resolution: {integrity: sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==} @@ -4384,11 +4751,19 @@ packages: '@cfworker/json-schema': optional: true - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/wasm-runtime@1.2.2': + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} @@ -4408,109 +4783,109 @@ packages: '@one-ini/wasm@0.2.1': resolution: {integrity: sha512-TUqERXGNTifZ9y2g3wPxQrw3HpHv/02DsW3D90T9x0hhonrL1ZqpSmNrU2XkoIq0fP1N6gZfVQzy2Fw1ZvGBNg==} - '@parcel/watcher-android-arm64@2.5.6': - resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] - '@parcel/watcher-darwin-arm64@2.5.6': - resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] - '@parcel/watcher-darwin-x64@2.5.6': - resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] - '@parcel/watcher-freebsd-x64@2.5.6': - resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] - '@parcel/watcher-linux-arm-glibc@2.5.6': - resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [glibc] - '@parcel/watcher-linux-arm-musl@2.5.6': - resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [musl] - '@parcel/watcher-linux-arm64-glibc@2.5.6': - resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@parcel/watcher-linux-arm64-musl@2.5.6': - resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [musl] - '@parcel/watcher-linux-x64-glibc@2.5.6': - resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [glibc] - '@parcel/watcher-linux-x64-musl@2.5.6': - resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [musl] - '@parcel/watcher-win32-arm64@2.5.6': - resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - '@parcel/watcher-win32-ia32@2.5.6': - resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] - '@parcel/watcher-win32-x64@2.5.6': - resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - '@parcel/watcher@2.5.6': - resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} engines: {node: '>= 10.0.0'} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.10': - resolution: {integrity: sha512-x6fFWCeak8aCGfqZfe6CXYt5xVjxe9Os1cIPmVRcToInKLjhJkRVXvJ/L3/1KxFkjDQdbZV/YsuLKqa8t/xKpA==} + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@pkgr/core@0.3.6': resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} - '@playwright/test@1.61.0': - resolution: {integrity: sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==} - engines: {node: '>=18'} + '@playwright/test@1.62.1': + resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==} + engines: {node: '>=20'} hasBin: true '@plotly/d3-sankey-circular@0.33.1': @@ -4540,8 +4915,8 @@ packages: peerDependencies: prettier: ^3.0.0 - '@radix-ui/react-compose-refs@1.1.3': - resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': '>=18.2.36' react: '>=18.0.0 <19.0.0' @@ -4549,8 +4924,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.4': - resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': '>=18.2.36' react: '>=18.0.0 <19.0.0' @@ -4558,8 +4933,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-primitive@2.1.6': - resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: '@types/react': '>=18.2.36' '@types/react-dom': '*' @@ -4571,8 +4946,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.10': - resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==} + '@radix-ui/react-progress@1.1.7': + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} peerDependencies: '@types/react': '>=18.2.36' '@types/react-dom': '*' @@ -4584,8 +4959,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.3.0': - resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: '@types/react': '>=18.2.36' react: '>=18.0.0 <19.0.0' @@ -4593,47 +4968,47 @@ packages: '@types/react': optional: true - '@rc-component/motion@1.3.3': - resolution: {integrity: sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==} + '@rc-component/motion@1.1.4': + resolution: {integrity: sha512-rz3+kqQ05xEgIAB9/UKQZKCg5CO/ivGNU78QWYKVfptmbjJKynZO4KXJ7pJD3oMxE9aW94LD/N3eppXWeysTjw==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/portal@2.2.1': - resolution: {integrity: sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==} + '@rc-component/portal@2.0.0': + resolution: {integrity: sha512-337ADhBfgH02S8OujUl33OT+8zVJ67eyuUq11j/dE71rXKYNihMsggW8R2VfI2aL3SciDp8gAFsmPVoPkxLUGw==} engines: {node: '>=12.x'} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/resize-observer@1.1.2': - resolution: {integrity: sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==} + '@rc-component/resize-observer@1.0.0': + resolution: {integrity: sha512-inR8Ka87OOwtrDJzdVp2VuEVlc5nK20lHolvkwFUnXwV50p+nLhKny1NvNTCKvBmS/pi/rTn/1Hvsw10sRRnXA==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/slider@1.1.1': - resolution: {integrity: sha512-LSzgWGYDgeCDgR4r1XlU29gbYws6HpLnvJd/uMhLeW/vQgxldeR+Wb4uzHDCHiYEbr1bnEHWdjkPxjJRHxuiig==} + '@rc-component/slider@1.0.1': + resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} engines: {node: '>=8.x'} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/tooltip@1.4.0': - resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} + '@rc-component/tooltip@1.3.3': + resolution: {integrity: sha512-6wNDh60lh+RZFGJYm5vwNqB/S7YxkioZYF4Vj57tWIlKScxJWW5I2qXOc7gv99CXTDGclutVwcefZFbq9JANFQ==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/trigger@3.9.1': - resolution: {integrity: sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q==} + '@rc-component/trigger@3.6.15': + resolution: {integrity: sha512-agmLUpfYbgWhVBrXyQGiupc+YoQ9NaUyt1cf+LcyRi3waq1PDj6Q+D/bA3UlvcTr53Xg9592u3zmZ3yodRvBbA==} engines: {node: '>=8.x'} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@rc-component/util@1.11.1': - resolution: {integrity: sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==} + '@rc-component/util@1.3.0': + resolution: {integrity: sha512-hfXE04CVsxI/slmWKeSh6du7sSKpbvVdVEZCa8A+2QWDlL97EsCYme2c3ZWLn1uC9FR21JoewlrhUPWO4QgO8w==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' @@ -4717,8 +5092,8 @@ packages: rollup: optional: true - '@rollup/plugin-replace@6.0.3': - resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} + '@rollup/plugin-replace@6.0.2': + resolution: {integrity: sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -4744,8 +5119,8 @@ packages: rollup: optional: true - '@rollup/plugin-typescript@12.3.0': - resolution: {integrity: sha512-7DP0/p7y3t67+NabT9f8oTBFE6gGkto4SA6Np2oudYmZE/m1dt8RB0SjL1msMxFpLo631qjRCcBlAbq1ml/Big==} + '@rollup/plugin-typescript@12.1.4': + resolution: {integrity: sha512-s5Hx+EtN60LMlDBvl5f04bEiFZmAepk27Q+mr85L/00zPDn1jtzlTV6FWn81MaIwqfWzKxmOJrBWHU6vtQyedQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.14.0||^3.0.0||^4.0.0 @@ -4766,8 +5141,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@5.4.0': - resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -4775,141 +5150,141 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.62.0': - resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} + '@rollup/rollup-android-arm-eabi@4.62.4': + resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.62.0': - resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + '@rollup/rollup-android-arm64@4.62.4': + resolution: {integrity: sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.62.0': - resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + '@rollup/rollup-darwin-arm64@4.62.4': + resolution: {integrity: sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.0': - resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + '@rollup/rollup-darwin-x64@4.62.4': + resolution: {integrity: sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.62.0': - resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} + '@rollup/rollup-freebsd-arm64@4.62.4': + resolution: {integrity: sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.0': - resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + '@rollup/rollup-freebsd-x64@4.62.4': + resolution: {integrity: sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': - resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': + resolution: {integrity: sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.62.0': - resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.4': + resolution: {integrity: sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.62.0': - resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + '@rollup/rollup-linux-arm64-gnu@4.62.4': + resolution: {integrity: sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.62.0': - resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + '@rollup/rollup-linux-arm64-musl@4.62.4': + resolution: {integrity: sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.62.0': - resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} + '@rollup/rollup-linux-loong64-gnu@4.62.4': + resolution: {integrity: sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.62.0': - resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} + '@rollup/rollup-linux-loong64-musl@4.62.4': + resolution: {integrity: sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.62.0': - resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} + '@rollup/rollup-linux-ppc64-gnu@4.62.4': + resolution: {integrity: sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.62.0': - resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + '@rollup/rollup-linux-ppc64-musl@4.62.4': + resolution: {integrity: sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.62.0': - resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.4': + resolution: {integrity: sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.62.0': - resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} + '@rollup/rollup-linux-riscv64-musl@4.62.4': + resolution: {integrity: sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.62.0': - resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + '@rollup/rollup-linux-s390x-gnu@4.62.4': + resolution: {integrity: sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.0': - resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + '@rollup/rollup-linux-x64-gnu@4.62.4': + resolution: {integrity: sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.62.0': - resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + '@rollup/rollup-linux-x64-musl@4.62.4': + resolution: {integrity: sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.62.0': - resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} + '@rollup/rollup-openbsd-x64@4.62.4': + resolution: {integrity: sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.62.0': - resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + '@rollup/rollup-openharmony-arm64@4.62.4': + resolution: {integrity: sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.62.0': - resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} + '@rollup/rollup-win32-arm64-msvc@4.62.4': + resolution: {integrity: sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.0': - resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} + '@rollup/rollup-win32-ia32-msvc@4.62.4': + resolution: {integrity: sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.0': - resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + '@rollup/rollup-win32-x64-gnu@4.62.4': + resolution: {integrity: sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.0': - resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} + '@rollup/rollup-win32-x64-msvc@4.62.4': + resolution: {integrity: sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==} cpu: [x64] os: [win32] @@ -4924,8 +5299,8 @@ packages: resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==} engines: {node: '>=22'} - '@sinclair/typebox@0.34.49': - resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==} + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -4933,86 +5308,72 @@ packages: '@sinonjs/fake-timers@15.4.0': resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} - '@swc/core-darwin-arm64@1.15.41': - resolution: {integrity: sha512-kREh6J5paQFvP3i7f/4FbqRNOJREutVFVOkder4GVyCBQ39YmER55cW/y1NNjwrchzFqgYswFn0mMDCqbqKzrw==} + '@swc/core-darwin-arm64@1.13.5': + resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.41': - resolution: {integrity: sha512-N8B56ESFazZAWZyIkecADSPCwlLEinW7QLMEeotCpv4J7VXwfH+OLkmRL8o96UZ+1355fwHxDTS6/wK7yucvkA==} + '@swc/core-darwin-x64@1.13.5': + resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.41': - resolution: {integrity: sha512-6XrId2fyle0mS5xxON8rU84mPd2Cq1kDJRj+4BnQKTd7u+2kSA6Ww+JkOP0iTNqOqt9OXhPOEAjBHAuonWcdCg==} + '@swc/core-linux-arm-gnueabihf@1.13.5': + resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.41': - resolution: {integrity: sha512-ynLIarxlkVnqHn1D0fKOVht6mNU5ks6lrH+MY3kkS+XFaGGgDxFZVjWKJlkYTKm3RCvBTfA8Ng5fLufXheMRKQ==} + '@swc/core-linux-arm64-gnu@1.13.5': + resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - '@swc/core-linux-arm64-musl@1.15.41': - resolution: {integrity: sha512-dXu/5vd4gh8symyhRF+4G7gOPkjmb4pONhh7sl+6GSiW0LOKZlfu5kXmyFbTz9smOT7jgr002qY9b1nujjXt2A==} + '@swc/core-linux-arm64-musl@1.13.5': + resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - '@swc/core-linux-ppc64-gnu@1.15.41': - resolution: {integrity: sha512-XGO6zVPXoPE0gf/XnI4jBbafNT13AYgoh6ns0JCSdOetI/kqVf0vhpz7NuNgAzZrMVCsmieqjPoTwViDgh4mOQ==} + '@swc/core-linux-x64-gnu@1.13.5': + resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} engines: {node: '>=10'} - cpu: [ppc64] + cpu: [x64] os: [linux] libc: [glibc] - '@swc/core-linux-s390x-gnu@1.15.41': - resolution: {integrity: sha512-0WUglRwyZtW+iMi7J3iFdrCxreZZIKf4egTwEQfIYRsqFax69A0OrFj+NIoFSE03xBT/IFRrg+S8K6f9Ky+4hA==} + '@swc/core-linux-x64-musl@1.13.5': + resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} engines: {node: '>=10'} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@swc/core-linux-x64-gnu@1.15.41': - resolution: {integrity: sha512-VxkuQK59c0tHm6uJZCUrS3cyA2JhGGfdU6e41SZz0x/JS+4Sm7C1mIc97In14vkZJopEt7yXA2TouCqZDSygEA==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@swc/core-linux-x64-musl@1.15.41': - resolution: {integrity: sha512-/0qXIu1ZxggLuovLb22vFfKHq2AA4n6Whw5UwmVCHk4pkw7KWnPIQpMCEqUMPsNkFJig7PPp/TSYFu8ZEb2rtQ==} - engines: {node: '>=10'} - cpu: [x64] + cpu: [x64] os: [linux] libc: [musl] - '@swc/core-win32-arm64-msvc@1.15.41': - resolution: {integrity: sha512-Y481sMNZM6rECh9VO4+y26N1lWEDAyxnBZskUf37fl90uHE946VHfmiVQWT0uMFOhyJJFovGTRuF4W82dwewUg==} + '@swc/core-win32-arm64-msvc@1.13.5': + resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.41': - resolution: {integrity: sha512-BAchBD5qeUzy3hiPSLJtaaoSm4blCLyYffOF1bGE4ETcV+OisqjUAwDQMJj++4bTpvMCDzwC+Bj3PmQyBCtscw==} + '@swc/core-win32-ia32-msvc@1.13.5': + resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.41': - resolution: {integrity: sha512-WOkA+fJ/ViVBQDsSV9JC52NACTe5PhlurA6viASDZGb7HR3KS01ZG7RZ+Bg6SVQFIoq3gSbTsskQVe6EbHFAYw==} + '@swc/core-win32-x64-msvc@1.13.5': + resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.41': - resolution: {integrity: sha512-03nQq/082QRJJiOvp3FGbgxTGyyxMxohPTjhk/W9bD2J0tk4ukITI7goOhOO2WbaHn/lsPmo/zf8+DIXhwpgYQ==} + '@swc/core@1.13.5': + resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -5029,8 +5390,8 @@ packages: peerDependencies: '@swc/core': '*' - '@swc/types@0.1.27': - resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==} + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -5061,8 +5422,12 @@ packages: peerDependencies: '@testing-library/dom': ^10.4.1 - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -5073,53 +5438,23 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@turbo/darwin-64@2.9.18': - resolution: {integrity: sha512-9f27peFu16ur8c0v9nUFUEyBnbKuuFsUTjHFWfmwGfzySBXbHwzU44QhZon6Mznz0cHsIr3984NQj/bVrnGSRw==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.18': - resolution: {integrity: sha512-9A6TMRq/Ib+QnbhLlgkhOm+624wO4pzSQ/yQviQfWHOlFvaYxdnIAYmu2H6TS6y7kSVL0DvzNe04NbESTOzFVQ==} - cpu: [arm64] - os: [darwin] - - '@turbo/linux-64@2.9.18': - resolution: {integrity: sha512-zCdIDtz69AnbYh913elJRRoF3QY5aa2HNnf+4rAkc7bQ+tWujiDkCNV7stazOUPggaDvhKIf2Z87qHftTeXSkw==} - cpu: [x64] - os: [linux] - - '@turbo/linux-arm64@2.9.18': - resolution: {integrity: sha512-Va1kXI04naMgYwqv/5Dfa36dTDx8015U7oaQAjrXa45ua9OoFjSV4OmvkML4EmXvUclQHCiBRbY8bvd0jV7eAg==} - cpu: [arm64] - os: [linux] - - '@turbo/windows-64@2.9.18': - resolution: {integrity: sha512-m0kDhZANxSNz9ck1ybogFscHabriAsp4eDFNrN/1H5WrgTF7b3VlcPZnhuO3v2+E2KnCbeAc+UUT10BZZHdDKw==} - cpu: [x64] - os: [win32] - - '@turbo/windows-arm64@2.9.18': - resolution: {integrity: sha512-nUdR8WqoomUys9iIQmG45TMiizJ+5BV8egSeLLZba/AWblyp3fVBcIH1kSE58OtK4g2YzbMJEth6Ttv9w5rqMA==} - cpu: [arm64] - os: [win32] - - '@turf/area@7.3.5': - resolution: {integrity: sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q==} + '@turf/area@7.2.0': + resolution: {integrity: sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA==} - '@turf/bbox@7.3.5': - resolution: {integrity: sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw==} + '@turf/bbox@7.2.0': + resolution: {integrity: sha512-wzHEjCXlYZiDludDbXkpBSmv8Zu6tPGLmJ1sXQ6qDwpLE1Ew3mcWqt8AaxfTP5QwDNQa3sf2vvgTEzNbPQkCiA==} - '@turf/centroid@7.3.5': - resolution: {integrity: sha512-hkWaqwGFdOn6Tf0EWfn2yn1XZ1FWE1h2C5ZWstDMu/FxYO5DB+YjlmOFPl4K6SmSOEgdV07eK2vDCyPeTHqKGA==} + '@turf/centroid@7.2.0': + resolution: {integrity: sha512-yJqDSw25T7P48au5KjvYqbDVZ7qVnipziVfZ9aSo7P2/jTE7d4BP21w0/XLi3T/9bry/t9PR1GDDDQljN4KfDw==} - '@turf/helpers@7.3.5': - resolution: {integrity: sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg==} + '@turf/helpers@7.2.0': + resolution: {integrity: sha512-cXo7bKNZoa7aC7ydLmUR02oB3IgDe7MxiPuRz3cCtYQHn+BJ6h1tihmamYDWWUlPHgSNF0i3ATc4WmDECZafKw==} - '@turf/meta@7.3.5': - resolution: {integrity: sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg==} + '@turf/meta@7.2.0': + resolution: {integrity: sha512-igzTdHsQc8TV1RhPuOLVo74Px/hyPrVgVOTgjWQZzt3J9BVseCdpfY/0cJBdlSRI4S/yTmmHl7gAqjhpYH5Yaw==} - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -5148,6 +5483,12 @@ packages: '@types/deep-equal@1.0.4': resolution: {integrity: sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==} + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -5163,8 +5504,8 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - '@types/google.maps@3.65.1': - resolution: {integrity: sha512-O9monmoXfyWsuyR4Wz3TZU26qai9y7jUV7DSRySluae6O5tQt3Obw5ETt0HKfNsjctnlF/yx/Tfn3WQNmKRXZA==} + '@types/google.maps@3.58.1': + resolution: {integrity: sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==} '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -5190,8 +5531,8 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/katex@0.16.8': - resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} + '@types/katex@0.16.7': + resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} '@types/leaflet@1.9.21': resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} @@ -5229,8 +5570,8 @@ packages: '@types/plotly.js-dist-min@2.3.4': resolution: {integrity: sha512-ISwLFV6Zs/v3DkaRFLyk2rvYAfVdnYP2VVVy7h+fBDWw52sn7sMUzytkWiN4M75uxr1uz1uiBioePTDpAfoFIg==} - '@types/plotly.js@3.0.10': - resolution: {integrity: sha512-q+MgO4aajC2HrO7FllTYWzrpdfbTjboSMfjkz/aXKjg1v7HNo1zMEFfAW7quKfk6SL+bH74A5ThBEps/7hZxOA==} + '@types/plotly.js@3.0.7': + resolution: {integrity: sha512-oFgNQsBpVOuQ2jYl3qRO9uyuixT+jbeMXGbbAHTV7AM9Bi6N9B2ZFYxO64hBwXu65Khb7w8a6d4EJnrLC30Vlw==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -5252,16 +5593,16 @@ packages: '@types/react-datepicker@6.2.0': resolution: {integrity: sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==} - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} peerDependencies: '@types/react': '>=18.2.36' - '@types/react-plotly.js@2.6.4': - resolution: {integrity: sha512-AU6w1u3qEGM0NmBA69PaOgNc0KPFA/+qkH6Uu9EBTJ45/WYOUoXi9AF5O15PRM2klpHSiHAAs4WnlI+OZAFmUA==} + '@types/react-plotly.js@2.6.3': + resolution: {integrity: sha512-HBQwyGuu/dGXDsWhnQrhH+xcJSsHvjkwfSRjP+YpOsCCWryIuXF78ZCBjpfgO3sCc0Jo8sYp4NOGtqT7Cn3epQ==} - '@types/react@19.2.17': - resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} '@types/reactcss@1.2.13': resolution: {integrity: sha512-gi3S+aUi6kpkF5vdhUsnkwbiSEIU/BEJyD7kBy2SudWBUuKmJk8AQKE0OVcQQeEy40Azh0lV6uynxlikYIJuwg==} @@ -5292,8 +5633,8 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/warning@3.0.4': - resolution: {integrity: sha512-CqN8MnISMwQbLJXO3doBAV4Yw9hx9/Pyr2rZ78+NfaCnhyRA/nKrpyk6E7mKw17ZOaQdLpK9GiUjrqLzBlN3sg==} + '@types/warning@3.0.3': + resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} @@ -5301,70 +5642,107 @@ packages: '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.61.1': - resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + '@typescript-eslint/eslint-plugin@8.66.0': + resolution: {integrity: sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.61.1 + '@typescript-eslint/parser': ^8.66.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/parser@8.61.1': - resolution: {integrity: sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==} + '@typescript-eslint/parser@8.66.0': + resolution: {integrity: sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/project-service@8.61.1': - resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + '@typescript-eslint/project-service@8.46.1': + resolution: {integrity: sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/scope-manager@8.61.1': - resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + '@typescript-eslint/project-service@8.66.0': + resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>5.8.0 <6.0.0' + + '@typescript-eslint/scope-manager@8.46.1': + resolution: {integrity: sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.66.0': + resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.46.1': + resolution: {integrity: sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.61.1': - resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + '@typescript-eslint/tsconfig-utils@8.66.0': + resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/type-utils@8.61.1': - resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} + '@typescript-eslint/type-utils@8.66.0': + resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/types@8.61.1': - resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + '@typescript-eslint/types@8.46.1': + resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.66.0': + resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.46.1': + resolution: {integrity: sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>5.8.0 <6.0.0' + + '@typescript-eslint/typescript-estree@8.66.0': + resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/typescript-estree@8.61.1': - resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + '@typescript-eslint/utils@8.46.1': + resolution: {integrity: sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: + eslint: ^8.57.0 || ^9.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/utils@8.61.1': - resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + '@typescript-eslint/utils@8.66.0': + resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>5.8.0 <6.0.0' - '@typescript-eslint/visitor-keys@8.61.1': - resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + '@typescript-eslint/visitor-keys@8.46.1': + resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/codemirror-extensions-basic-setup@4.25.10': - resolution: {integrity: sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==} + '@typescript-eslint/visitor-keys@8.66.0': + resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@uiw/codemirror-extensions-basic-setup@4.25.2': + resolution: {integrity: sha512-s2fbpdXrSMWEc86moll/d007ZFhu6jzwNu5cWv/2o7egymvLeZO52LWkewgbr+BUCGWGPsoJVWeaejbsb/hLcw==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' @@ -5374,18 +5752,18 @@ packages: '@codemirror/state': ^6.5.2 '@codemirror/view': ^6.38.1 - '@uiw/codemirror-theme-github@4.25.10': - resolution: {integrity: sha512-iMM2QT4FaebJMO4W7lXmxNkRPIjKzgY26wL0QG0Ugy0gzsnxoNz4zgNeFIblPA8rvrN3vOIhNNh4nk9UOlFKxA==} + '@uiw/codemirror-theme-github@4.25.2': + resolution: {integrity: sha512-9g3ujmYCNU2VQCp0+XzI1NS5hSZGgXRtH+5yWli5faiPvHGYZUVke+5Pnzdn/1tkgW6NpTQ7U/JHsyQkgbnZ/w==} - '@uiw/codemirror-themes@4.25.10': - resolution: {integrity: sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==} + '@uiw/codemirror-themes@4.25.2': + resolution: {integrity: sha512-WFYxW3OlCkMomXQBlQdGj1JZ011UNCa7xYdmgYqywVc4E8f5VgIzRwCZSBNVjpWGGDBOjc+Z6F65l7gttP16pg==} peerDependencies: '@codemirror/language': '>=6.0.0' '@codemirror/state': ^6.5.2 '@codemirror/view': ^6.38.1 - '@uiw/react-codemirror@4.25.10': - resolution: {integrity: sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==} + '@uiw/react-codemirror@4.25.2': + resolution: {integrity: sha512-XP3R1xyE0CP6Q0iR0xf3ed+cJzJnfmbLelgJR6osVVtMStGGZP3pGQjjwDRYptmjGHfEELUyyBLdY25h0BQg7w==} peerDependencies: '@babel/runtime': '>=7.11.0' '@codemirror/state': ^6.5.2 @@ -5395,8 +5773,8 @@ packages: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - '@ungap/structured-clone@1.3.1': - resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} @@ -5550,8 +5928,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} acorn@7.4.1: @@ -5559,8 +5937,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -5576,11 +5954,11 @@ packages: ajv: optional: true - ajv@6.15.0: - resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} @@ -5659,6 +6037,9 @@ packages: array-range@1.0.1: resolution: {integrity: sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA==} + array-rearrange@2.2.2: + resolution: {integrity: sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w==} + array-union@1.0.2: resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==} engines: {node: '>=0.10.0'} @@ -5721,12 +6102,12 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.4: - resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} + axe-core@4.12.1: + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} engines: {node: '>=4'} - babel-jest@30.3.0: - resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} + babel-jest@30.4.1: + resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': 7.29.7 @@ -5735,10 +6116,15 @@ packages: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} - babel-plugin-jest-hoist@30.3.0: - resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} + babel-plugin-jest-hoist@30.4.0: + resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + peerDependencies: + '@babel/core': 7.29.7 + babel-plugin-polyfill-corejs2@0.4.17: resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} peerDependencies: @@ -5754,6 +6140,11 @@ packages: peerDependencies: '@babel/core': 7.29.7 + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + peerDependencies: + '@babel/core': 7.29.7 + babel-plugin-polyfill-regenerator@0.6.8: resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} peerDependencies: @@ -5770,8 +6161,8 @@ packages: peerDependencies: '@babel/core': 7.29.7 - babel-preset-jest@30.3.0: - resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} + babel-preset-jest@30.4.0: + resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': 7.29.7 @@ -5790,11 +6181,15 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.37: - resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==} + baseline-browser-mapping@2.11.13: + resolution: {integrity: sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==} engines: {node: '>=6.0.0'} hasBin: true + baseline-browser-mapping@2.8.16: + resolution: {integrity: sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw==} + hasBin: true + big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} @@ -5824,31 +6219,36 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.16: - resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.1.3: - resolution: {integrity: sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@5.0.8: - resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - brandi-react@5.1.0: - resolution: {integrity: sha512-eOceMj/GwLTo501X/fgL8HAYmUrjkwZwCHXaS/uPHYa0+i5vbg0WV/98nYYVa7qHwRC5c9iw0sn+fAuPofZbHg==} + brandi-react@5.0.0: + resolution: {integrity: sha512-EnJXip83QYe7uS8e1J6Yeng1eGeI1MCshPqUJ5/c8ujffDeH633I8t1UluPLR4ZtC3UWCdTh6yOz4UDkyPSPfA==} peerDependencies: brandi: ^3 || ^4 || ^5 react: '>=18.0.0 <19.0.0' - brandi@5.1.0: - resolution: {integrity: sha512-wGAIaC/pj/SMRCc7RdEhawT83YcbuxSViRAWp0d5cWOCjpGqAzCJo8NeiL/5rUbAPL4zlQ45ciz5eMnARMGygA==} + brandi@5.0.0: + resolution: {integrity: sha512-oztvITQgvuFb2K+NWdHLx0mMH8TGO3ASrQ43FZzmfiq5rCj0DRlsuZ6Efi/yeu3hyGx/Y+Z1xLGp2qzDWpiNYA==} + + browserslist@4.26.3: + resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -5873,8 +6273,8 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.9: - resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} call-bound@1.0.4: @@ -5896,11 +6296,17 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001750: + resolution: {integrity: sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ==} + + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + + canvas-fit@1.5.0: + resolution: {integrity: sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ==} - canvas@3.2.3: - resolution: {integrity: sha512-PzE5nJZPz72YUAfo8oTp0u3fqqY7IzlTubneAihqDYAUcBk7ryeCmBbdJBEdaH0bptSOe2VT2Zwcb3UaFyaSWw==} + canvas@3.2.0: + resolution: {integrity: sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==} engines: {node: ^18.12.0 || >= 20.9.0} chalk@4.1.2: @@ -5935,8 +6341,8 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} cjs-module-lexer@2.2.0: @@ -5948,9 +6354,6 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cldrjs@0.5.5: - resolution: {integrity: sha512-KDwzwbmLIPfCgd8JERVDpQKrUUM1U4KpFJJg2IROv89rF172lLufoJnqJ/Wea6fXL5bO6WjuLMzY8V52UWPvkA==} - cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -5989,11 +6392,11 @@ packages: codemirror@6.0.2: resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-alpha@1.1.3: - resolution: {integrity: sha512-krPYBO1RSO5LH4AGb/b6z70O1Ip2o0F0+0cVFN5FN99jfQtZFT08rQyg+9oOBNJYAn3SRwJIFC8jUEOKz7PisA==} + color-alpha@1.0.4: + resolution: {integrity: sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -6005,18 +6408,14 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-name@2.1.0: - resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} - engines: {node: '>=12.20'} - color-normalize@1.5.0: resolution: {integrity: sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==} color-parse@1.4.3: resolution: {integrity: sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A==} - color-parse@2.0.2: - resolution: {integrity: sha512-eCtOz5w5ttWIUcaKLiktF+DxZO1R9KLNY/xhbV6CkhM7sR3GhVghmt6X6yOnzeaM24po+Z9/S1apbXMwA3Iepw==} + color-parse@2.0.0: + resolution: {integrity: sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg==} color-rgba@2.4.0: resolution: {integrity: sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==} @@ -6106,8 +6505,8 @@ packages: resolution: {integrity: sha512-n4Kr1HFMTf3iMbES0TMxKIcYtUUv4rKqyQQp2JwfOEfFCOfGT3Tq4mCyJ8S9/YPyWhydjfKrrvnyl+gCjA+mJQ==} engines: {node: '>=22'} - conventional-commits-parser@7.1.0: - resolution: {integrity: sha512-DPp6hkUjvwIivxbkrTiLXeRswNv1A/4GFA2X6scXma0AMa9632V3TwxmrlkUIEtUktiM3Ln+RrSH2xlP3/jUTw==} + conventional-commits-parser@7.1.2: + resolution: {integrity: sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==} engines: {node: '>=22'} hasBin: true @@ -6127,11 +6526,15 @@ packages: engines: {node: '>=10'} hasBin: true - core-js-compat@3.49.0: - resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} + core-js-compat@3.46.0: + resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} + + core-js-compat@3.50.0: + resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} + engines: {node: '>=6.4.0'} - core-js@3.49.0: - resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==} + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -6140,8 +6543,8 @@ packages: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} - cosmiconfig-typescript-loader@6.3.0: - resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} engines: {node: '>=v18'} peerDependencies: '@types/node': ~24.12.0 @@ -6261,8 +6664,8 @@ packages: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} cuint@0.2.2: resolution: {integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==} @@ -6352,11 +6755,11 @@ packages: date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - date-fns@4.4.0: - resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dayjs@1.11.21: - resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + dayjs@1.11.18: + resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -6390,8 +6793,8 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.7.2: - resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -6446,6 +6849,11 @@ packages: detect-kerning@2.1.2: resolution: {integrity: sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw==} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -6458,8 +6866,8 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dir-glob@3.0.1: @@ -6482,9 +6890,6 @@ packages: dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dom-helpers@6.0.1: - resolution: {integrity: sha512-IKySryuFwseGkrCA/pIqlwUPOD50w1Lj/B2Yief3vBOP18k5y4t+hTqKh55gULDVeJMRitcozve+g/wVFf4sFQ==} - dom-serializer@1.4.1: resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} @@ -6499,8 +6904,8 @@ packages: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} - dompurify@3.4.12: - resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} domutils@2.8.0: resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} @@ -6514,8 +6919,8 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - downshift@9.3.6: - resolution: {integrity: sha512-xEKP1vbt/k7Siu481TKibmj8EyL6iyBwaRJgb6gCsFyLeiyZ1KEJnApS9R1U71hTdK5ym0R99AOYRROcTz1PeQ==} + downshift@9.0.10: + resolution: {integrity: sha512-TP/iqV6bBok6eGD5tZ8boM8Xt7/+DZvnVNr8cNIhbAm2oUBd79Tudiccs2hbcV9p7xAgS/ozE7Hxy3a9QqS6Mw==} peerDependencies: react: '>=18.0.0 <19.0.0' @@ -6553,8 +6958,14 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.373: - resolution: {integrity: sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA==} + electron-to-chromium@1.5.237: + resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} + + electron-to-chromium@1.5.403: + resolution: {integrity: sha512-MQsYmdaLzvaCX5j+ZZBr5Fm6uCCnPQcRtlvmvRlWqrXy+BH2O4ffXIAScF+JQznQWB9brWp4lSD9Z4yNmaf2BA==} + + element-size@1.1.1: + resolution: {integrity: sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ==} elementary-circuits-directed-graph@1.3.1: resolution: {integrity: sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ==} @@ -6605,12 +7016,8 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract-get@1.0.0: - resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} - engines: {node: '>= 0.4'} - - es-abstract@1.24.2: - resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -6624,12 +7031,12 @@ packages: es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-iterator-helpers@1.3.3: - resolution: {integrity: sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==} + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.2: - resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: @@ -6640,12 +7047,12 @@ packages: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} - es-to-primitive@1.3.1: - resolution: {integrity: sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==} + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.49.0: - resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} es5-ext@0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} @@ -6687,8 +7094,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-fix-utils@0.4.2: - resolution: {integrity: sha512-n7ZTcwwkP5scedlhvWMcqxED+O1NzXcj5Rxn/0kJQMP88k02vRcBfQ1qsk/JHb6Aw8bajFoetFCCBiNIcNCsvA==} + eslint-fix-utils@0.4.3: + resolution: {integrity: sha512-EKzNhsNavV5DdkHVltHBM9TqfsonCmiUhOm1Ra97zo03M9IAx3wtM1eC27eWGMj/D3LrALmHHNe1QlQH1P7Nxw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@types/estree': '>=1' @@ -6700,8 +7107,8 @@ packages: eslint-import-resolver-node@0.3.10: resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} - eslint-module-utils@2.13.0: - resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==} + eslint-module-utils@2.14.0: + resolution: {integrity: sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -6721,9 +7128,8 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-cypress@5.4.0: - resolution: {integrity: sha512-XAQYuzMpLWJdFRQorPO3GDx4XHqI362qr1/XIp0N6SNTAa8lyzmpTA26qNRc99I53NnqX9l0SHwbHXX7TAKIkg==} - deprecated: 'deprecate: accidentally includes breaking changes from 6.0.0' + eslint-plugin-cypress@5.2.0: + resolution: {integrity: sha512-vuCUBQloUSILxtJrUWV39vNIQPlbg0L7cTunEAzvaUzv9LFZZym+KFLH18n9j2cZuFPdlxOqTubCvg5se0DyGw==} peerDependencies: eslint: '>=9' @@ -6737,8 +7143,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jest@29.15.2: - resolution: {integrity: sha512-kEN4r9RZl1xcsb4arGq89LrcVdOUFII/JSCwtTPJyv16mDwmPrcuEQwpxqZHeINvcsd7oK5O/rhdGlxFRaZwvQ==} + eslint-plugin-jest@29.16.0: + resolution: {integrity: sha512-0WFBxDHlT2ratGQfnFQEVIsgQJ5cfd+0IV8Kc6U3X2onB8ATLG23voD2Ch5G9fCkEpCPmCMuzW0tbS0kYb8biw==} engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0 @@ -6760,8 +7166,8 @@ packages: eslint: '>=8.0.0' jsonc-eslint-parser: '>=2.0.0' - eslint-plugin-playwright@2.10.4: - resolution: {integrity: sha512-l0V/VxyqfFbtqCTxj5AdRn3Q6S/hIW4nKBnKZVleVbZ24N2My6Usj//ytX3dKKqAoSbvKck9YtSytfdZ5qjLuA==} + eslint-plugin-playwright@2.11.0: + resolution: {integrity: sha512-zOIEYyv1TSn2izXkyg/yj0LXc4YBQI6pl3xIFWwMCcXt/dgQCz8dBqXiXFMoM3xc/GqZThAtaGYo8aPu/vBd+Q==} engines: {node: '>=16.9.0'} peerDependencies: eslint: '>=8.40.0' @@ -6780,11 +7186,11 @@ packages: eslint-config-prettier: optional: true - eslint-plugin-promise@7.3.0: - resolution: {integrity: sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==} + eslint-plugin-promise@7.2.1: + resolution: {integrity: sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 eslint-plugin-react-hooks@7.0.1: resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} @@ -6822,8 +7228,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@9.39.4: - resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + eslint@9.39.3: + resolution: {integrity: sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -6840,13 +7246,17 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -6881,8 +7291,8 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} @@ -6920,8 +7330,8 @@ packages: resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - express-rate-limit@8.5.2: - resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} + express-rate-limit@8.6.2: + resolution: {integrity: sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -6959,12 +7369,12 @@ packages: fast-uri@3.1.4: resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} - fast-xml-parser@4.5.6: - resolution: {integrity: sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A==} + fast-xml-parser@4.5.3: + resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} hasBin: true - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -7021,14 +7431,18 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} flatten-vertex-data@1.0.2: resolution: {integrity: sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==} - flow-parser@0.318.0: - resolution: {integrity: sha512-66vPPqpjOcroUke2vbiyRNS87lbTi7ib80CM9lsn45qymGHPL4nrdY9FKo0TvtrFFqHQErfB/BJeqVkEnWeK/g==} + flow-estree@0.326.0: + resolution: {integrity: sha512-43Qv+Ei9qfabhLx8JGEJku4frHGD5zI2EuL73Hs9HrWqPMbTT/DS18Az7bcodGwdQEv1DVIEI2WowkEbMIk4BQ==} + engines: {node: '>=18'} + + flow-parser@0.326.0: + resolution: {integrity: sha512-H/Wqt2SDkQ8GH8wpyAj434zN40zomipZWBbKYlAwQYyNdMIQMKqpXTx82FMnITt2iDQ5zrV80NvSPAgIAafwGA==} engines: {node: '>=0.4.0'} font-atlas@2.1.0: @@ -7045,8 +7459,8 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.6: - resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} formdata-polyfill@4.0.10: @@ -7067,8 +7481,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.5: - resolution: {integrity: sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==} + fs-extra@11.4.0: + resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} engines: {node: '>=14.14'} fs-extra@8.1.0: @@ -7091,8 +7505,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.2.0: - resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: @@ -7112,8 +7526,8 @@ packages: geojson-vt@3.2.1: resolution: {integrity: sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==} - geojson-vt@4.0.3: - resolution: {integrity: sha512-jR1MwkLaZGa8Zftct9ZFruyWFrdl9ZyD2OliXNy9Qq5bBPeg5wHVpBQF9p5GjnicSDQqvBVpysxTPKmWdsfWMA==} + geojson-vt@4.0.2: + resolution: {integrity: sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==} get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -7122,8 +7536,8 @@ packages: get-canvas-context@1.0.2: resolution: {integrity: sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==} - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -7150,8 +7564,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - git-hooks-list@4.2.1: - resolution: {integrity: sha512-WNvqJjOxxs/8ZP9+DWdwWJ7cDsd60NHf39XnD82pDVrKO5q7xfPqpkK6hwEAmBa/ZSEE4IOoR75EzbbIuwGlMw==} + git-hooks-list@4.1.1: + resolution: {integrity: sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==} github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -7208,15 +7622,19 @@ packages: resolution: {integrity: sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==} engines: {node: '>=16'} - globalize@1.7.1: - resolution: {integrity: sha512-PFymRL0PtitFOlSniuwwwNfkooi3cLQJo9Uke1+j1DsGfUkkHkwneImqVtGcqKI0TuzhAlHt7hAcgK324902HA==} + globalize@0.1.1: + resolution: {integrity: sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==} globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@17.6.0: - resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globals@17.9.0: + resolution: {integrity: sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==} engines: {node: '>=18'} globalthis@1.0.4: @@ -7324,6 +7742,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} @@ -7338,8 +7760,8 @@ packages: resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} engines: {node: '>=12.0.0'} - hono@4.12.27: - resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + hono@4.13.1: + resolution: {integrity: sha512-kdJoFVv2xmayw6cY09H7AbMJMt8Jn5jdlEdXsP7AGBdF2DIptVlKlOLKXP41yPip4/a3yQPv9gVcJYI8YY04dw==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -7356,6 +7778,10 @@ packages: htmlparser2@4.1.0: resolution: {integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -7385,8 +7811,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} icss-replace-symbols@1.1.0: @@ -7416,8 +7842,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@5.1.6: - resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} import-cwd@3.0.0: resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==} @@ -7473,8 +7899,8 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ip-address@10.4.0: - resolution: {integrity: sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==} + ip-address@10.5.0: + resolution: {integrity: sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==} engines: {node: '>= 12'} ip@2.0.1: @@ -7518,6 +7944,10 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + is-core-module@2.16.2: resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} @@ -7530,10 +7960,6 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-document.all@1.0.0: - resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -7566,6 +7992,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-iexplorer@1.0.0: + resolution: {integrity: sha512-YeLzceuwg3K6O0MLM3UyUUjKAlyULetwryFp1mHy1I5PfArK0AEqlfa+MR4gkJjcbuJXoDJCvXbyqZVf5CR2Sg==} + engines: {node: '>=0.10.0'} + is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -7690,9 +8120,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.5: - resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} - engines: {node: '>=18'} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} @@ -7725,8 +8155,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.2.3: - resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} jasmine-core@3.99.1: @@ -7797,6 +8227,15 @@ packages: canvas: optional: true + jest-environment-jsdom@30.4.1: + resolution: {integrity: sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jest-environment-node@30.3.0: resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7805,6 +8244,10 @@ packages: resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-haste-map@30.4.1: + resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-jasmine2@30.3.0: resolution: {integrity: sha512-oNNTvA5UBmxQuimsK7G3l1wLIXaUlQ81/v8zaTC1Y9thJkjbOV6v7mBPFiruk98QpplYkS65S2u1ihvW66kxLg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7898,6 +8341,10 @@ packages: resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-worker@30.4.1: + resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest@30.3.0: resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7912,8 +8359,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.3: - resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + jose@6.2.8: + resolution: {integrity: sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==} js-beautify@2.0.3: resolution: {integrity: sha512-cyFbh3tkPhknnTD/0bLf0T0yy2ZIbqL05mttzbt4y1Zfr7NxqXQZ62dkBLKs3oHH/lpjmDRAnciJiSUyOy8XwQ==} @@ -7926,19 +8373,19 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.15.0: - resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true - js-yaml@4.3.0: - resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsbarcode@3.12.3: - resolution: {integrity: sha512-CuHU9hC6dPsHF5oVFMo8NW76uQVjH4L22CsP4hW+dNnGywJHC/B0ThA1CTDVLnxKLrrpYdicBLnd2xsgTfRnvg==} + jsbarcode@3.12.1: + resolution: {integrity: sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==} - jscodeshift@17.3.0: - resolution: {integrity: sha512-LjFrGOIORqXBU+jwfC9nbkjmQfFldtMIoS6d9z2LG/lkmyNXsJAySPT+2SWXJEoE68/bCWcxKpXH37npftgmow==} + jscodeshift@17.4.0: + resolution: {integrity: sha512-i3ESKiiTsGynxzTg5BhsZViD0ai72/6SsI1efDZxG6/5KCoElsmquxtyhXK5lpEgoO7MTNpYkjFEdEL97SkBNg==} engines: {node: '>=16'} hasBin: true peerDependencies: @@ -7991,9 +8438,9 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-eslint-parser@3.1.0: - resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} + jsonc-eslint-parser@2.4.1: + resolution: {integrity: sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -8001,8 +8448,8 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.2.1: - resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} @@ -8015,15 +8462,15 @@ packages: resolution: {integrity: sha512-3KF80UaaSSxo8jVnRYtMKNGFOoVPBdkkVPsw+Ad0y4oxKXPduS6G6iHkrf69yJVff/VAaYXkV42rtZ7daJxU3w==} engines: {node: '>=0.10.0'} - katex@0.16.47: - resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + katex@0.16.25: + resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} hasBin: true kdbush@3.0.0: resolution: {integrity: sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==} - kdbush@4.1.0: - resolution: {integrity: sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==} + kdbush@4.0.2: + resolution: {integrity: sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -8056,8 +8503,8 @@ packages: linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} - linkifyjs@4.3.3: - resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + linkifyjs@4.3.2: + resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} livereload-js@3.4.1: resolution: {integrity: sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==} @@ -8083,8 +8530,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.18.1: - resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -8108,8 +8555,8 @@ packages: lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -8122,8 +8569,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -8144,8 +8591,8 @@ packages: magic-string@0.16.0: resolution: {integrity: sha512-c4BEos3y6G2qO0B9X7K0FVLOPT9uGrjYwYRLFmDqyl5YMboUviyecnXWp94fJTSMwPw2/sf+CEYt5AGpmklkkQ==} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} make-cancellable-promise@1.3.2: resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==} @@ -8190,8 +8637,8 @@ packages: resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} hasBin: true - match-sorter@8.3.0: - resolution: {integrity: sha512-8Py1GbZi5zsclYSFcPAH4H5xfTbeD0bOREA7qP/t8bW4MbOSlPl8sbqHOedEV7O+Bxyvxm6xs/v6BXJGe+JDNA==} + match-sorter@8.1.0: + resolution: {integrity: sha512-0HX3BHPixkbECX+Vt7nS1vJ6P2twPgGTU3PMXjWrl1eyVCL24tFHeyYN1FN5RKLzve0TyzNI9qntqQGbebnfPQ==} material-colors@1.2.6: resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} @@ -8214,8 +8661,8 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + media-typer@1.1.1: + resolution: {integrity: sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==} engines: {node: '>= 0.8'} memoize-one@6.0.0: @@ -8224,8 +8671,8 @@ packages: mendix@10.24.75382: resolution: {integrity: sha512-ICMxqkWUejsc3KeFD9BJYvC+T4soi/NB2iapwWPC7oN0lCrFx36upzwI4rU77oMdRHsrVSsFVYLBy7sJJOABHw==} - mendix@11.10.0: - resolution: {integrity: sha512-OsLdgNJfhwG4/TcOIMMk0gu0ewHq3Xlv2CNW1YJy2ujqD19ngFvBVB9YTjYxmf+RMn1EG0FQYrn6zCSFzmbL4A==} + mendix@11.13.0: + resolution: {integrity: sha512-E9nacFc2Pd61EBCZD8OcEGmc2q+t+c7rktJ0iDSbJaSnsq4j4EqW7YrJluvn2B7BKFBiYFVmWvZunuXFfF6fhw==} merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} @@ -8297,19 +8744,22 @@ packages: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} - minimatch@8.0.7: - resolution: {integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==} + minimatch@3.1.4: + resolution: {integrity: sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==} + + minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -8319,6 +8769,10 @@ packages: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} @@ -8363,9 +8817,18 @@ packages: moo-color@1.0.3: resolution: {integrity: sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==} + mouse-change@1.4.0: + resolution: {integrity: sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ==} + mouse-event-offset@3.0.2: resolution: {integrity: sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w==} + mouse-event@1.0.5: + resolution: {integrity: sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw==} + + mouse-wheel@1.2.0: + resolution: {integrity: sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -8383,8 +8846,13 @@ packages: resolution: {integrity: sha512-Jd0fILWG44a9luj8v5kED4WI+zfkkgwKyRQKItTtlPfEsh7Lznfi1kr8/iZ+XAIss4Qq5GqRB0qtWbaz9ceO/A==} engines: {node: ^18.0.0 || >=20.0.0} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -8420,8 +8888,8 @@ packages: nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - node-abi@3.92.0: - resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} + node-abi@3.78.0: + resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} engines: {node: '>=10'} node-addon-api@7.1.1: @@ -8432,8 +8900,8 @@ packages: engines: {node: '>=10.5.0'} deprecated: Use your platform's native DOMException instead - node-exports-info@1.6.0: - resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + node-exports-info@1.6.2: + resolution: {integrity: sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==} engines: {node: '>= 0.4'} node-fetch@2.7.0: @@ -8452,8 +8920,11 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.47: - resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + node-releases@2.0.23: + resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} engines: {node: '>=18'} nopt@10.0.1: @@ -8497,8 +8968,8 @@ packages: resolution: {integrity: sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg==} engines: {node: '>=0.10.0'} - nwsapi@2.2.24: - resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -8604,8 +9075,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-json-validator@1.5.2: - resolution: {integrity: sha512-eHXskJQU4aCiSfjhRfTVtCJ+22/EzLHgYgZv5Gj3teb3NJrnTMzq5BnKAWKvR+PLpknCO1PmOCImDuO+dX4Vaw==} + package-json-validator@1.6.0: + resolution: {integrity: sha512-pd5HlhSA4oymER74A8ODqcOHz3sQAQjmTysx1oiJrIMRtIWl9pqxX5L3aUqRb5CBFpXroA8j6fhJmaFKhVUuxQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} package-name-regex@2.0.6: @@ -8672,6 +9143,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -8709,12 +9184,16 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pify@2.3.0: @@ -8745,24 +9224,24 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.61.0: - resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} - engines: {node: '>=18'} + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} hasBin: true playwright-ctrf-json-reporter@0.0.27: resolution: {integrity: sha512-FZ8KadoHJc7xhf5XM0R9F8XBsTSm4vywa5/fhmeo2nZhN31UmapYwRfxaBsGk6AbsvGmft5G+MVmkBjTJZic/Q==} - playwright@1.61.0: - resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} - engines: {node: '>=18'} + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} hasBin: true - plotly.js-dist-min@3.6.0: - resolution: {integrity: sha512-VR9jO2YdcEwbzVwtRyPE0eAieXFv1x5q6M9nnIgUS8FggahPrjiID6kzpnTYABwLX0gZkgEc0zxS6gQgVmgHzw==} + plotly.js-dist-min@3.1.1: + resolution: {integrity: sha512-eyuiESylUXW4kaF+v9J2gy9eZ+YT2uSVLILM4w1Afxnuv9u4UX9OnZnHR1OdF9ybq4x7+9chAzWUUbQ6HvBb3g==} - plotly.js@3.6.0: - resolution: {integrity: sha512-Fu5IaetcuxaeQPULk4wfIik0MnvIsEb5ynOsPAMfhAnjkPOEDFG7eSb/3ZZq1DW5MwYvZFXaTFHpal4U1Q5Yig==} + plotly.js@3.1.1: + resolution: {integrity: sha512-s4XPAXAZajmdpHoyPOyeL6jwPHW+tZtmbVBii9IDJbzbn7Jkp2Y9dAivJPhmh4djnWSgNE6zmd5e+Jw1f+DvBQ==} engines: {node: '>=18.0.0'} point-in-polygon@1.1.0: @@ -8977,12 +9456,12 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-selector-parser@6.1.4: - resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.1.4: - resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} engines: {node: '>=4'} postcss-svgo@5.1.0: @@ -8997,8 +9476,8 @@ packages: peerDependencies: postcss: ^8.2.15 - postcss-url@10.1.4: - resolution: {integrity: sha512-/oBzyLOHQvXvVr/7bzZOFD5lYTy1nomVE4aMA9eY5KQsHfWLDIzb86q8XoUsmrj2xKoGYMAvd884EzJEQzuXIw==} + postcss-url@10.1.3: + resolution: {integrity: sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==} engines: {node: '>=10'} peerDependencies: postcss: ^8.0.0 @@ -9006,8 +9485,12 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} potpack@1.0.2: @@ -9019,7 +9502,6 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} - deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -9030,16 +9512,16 @@ packages: resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} - prettier-plugin-packagejson@2.5.22: - resolution: {integrity: sha512-G6WalmoUssKF8ZXkni0+n4324K+gG143KPysSQNW+FrR0XyNb3BdRxchGC/Q1FE/F702p7/6KU7r4mv0WSWbzA==} + prettier-plugin-packagejson@2.5.19: + resolution: {integrity: sha512-Qsqp4+jsZbKMpEGZB1UP1pxeAT8sCzne2IwnKkr+QhUe665EXUo3BAvTf1kAPCqyMv9kg3ZmO0+7eOni/C6Uag==} peerDependencies: prettier: '>= 1.16.0' peerDependenciesMeta: prettier: optional: true - prettier@3.8.4: - resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} engines: {node: '>=14'} hasBin: true @@ -9062,8 +9544,8 @@ packages: peerDependencies: prettier: ^3.0.0 - probe-image-size@7.3.0: - resolution: {integrity: sha512-7CaDeBwiAbh6ohXsvLbAZhO7wzsZAmaevfxe39qvCwRh8LyaZfDlBGGLU1CCTgrTLtCOdwBBhjOrIHaIIimHfQ==} + probe-image-size@7.2.3: + resolution: {integrity: sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w==} proc-log@6.1.0: resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} @@ -9085,8 +9567,8 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protocol-buffers-schema@3.6.1: - resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==} + protocol-buffers-schema@3.6.0: + resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} @@ -9095,8 +9577,8 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} @@ -9109,16 +9591,16 @@ packages: pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - pusher-js@8.5.0: - resolution: {integrity: sha512-V7uzGi9bqOOOyM/6IkJdpFyjGZj7llz1v0oWnYkZKcYLvbz6VcHVLmzKqkvegjuMumpfIEKGLmWHwFb39XFCpw==} + pusher-js@8.6.0: + resolution: {integrity: sha512-wShJPfCS/kYkCBVzVW67wa9cnQIgHTszEK2XHNrFkOgGruuGw081aERAxfRjfdFU+WcIt8x6dvbwkTW4iZuQ8Q==} qrcode.react@4.2.0: resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} peerDependencies: react: '>=18.0.0 <19.0.0' - qs@6.15.2: - resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -9134,8 +9616,8 @@ packages: resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} engines: {node: '>= 12.0.0'} - quill-resize-module@2.1.3: - resolution: {integrity: sha512-Hrs/pwKqmnEkY8Z5zbp4LxdLSHmQBXGAR5yzEFDeSfWTZIMpi6yII/dqIeXfwQBKAxWAVSxEeWqY7DMZ2GLTaQ==} + quill-resize-module@2.0.8: + resolution: {integrity: sha512-FBEQl+We+HR94OkV43nRd2QnDO6xJYYxfRpc5uJwcuO3hiDPFTRVcPYTKGZGU0nb9ZcLKwxrNHXeAmdd9Zl0Ug==} quill@2.0.3: resolution: {integrity: sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==} @@ -9159,8 +9641,8 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-big-calendar@1.20.0: - resolution: {integrity: sha512-Lp1mvG34l/9xtb/2LsBb4UAF3iPcUkmDqbmpsvDHzp/n8GA5gtn3+nf8BsULWw08opKDgv38nQi75dQlOOqzkg==} + react-big-calendar@1.19.4: + resolution: {integrity: sha512-FrvbDx2LF6JAWFD96LU1jjloppC5OgIvMYUYIPzAw5Aq+ArYFPxAjLqXc4DyxfsQDN0TJTMuS/BIbcSB7Pg0YA==} peerDependencies: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' @@ -9187,14 +9669,14 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - react-dropzone@14.4.1: - resolution: {integrity: sha512-QDuV76v3uKbHiH34SpwifZ+gOLi1+RdsCO1kl5vxMT4wW8R82+sthjvBw4th3NHF/XX6FBsqDYZVNN+pnhaw0g==} + react-dropzone@14.3.8: + resolution: {integrity: sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==} engines: {node: '>= 10.13'} peerDependencies: react: '>=18.0.0 <19.0.0' - react-image-crop@11.0.10: - resolution: {integrity: sha512-+5FfDXUgYLLqBh1Y/uQhIycpHCbXkI50a+nbfkB1C0xXXUTwkisHDo2QCB1SQJyHCqIuia4FeyReqXuMDKWQTQ==} + react-image-crop@11.1.2: + resolution: {integrity: sha512-+0Pc2fxpwKL4u4oLmdKBw8XSwUceFbXbKEHvFOlsl/MGB1OVNic4uBlAPmEHGXYgoJIq+b63xHbc/aJMG0AVkA==} peerDependencies: react: '>=18.0.0 <19.0.0' @@ -9204,11 +9686,14 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.7: - resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + react-is@19.2.8: + resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} react-lifecycles-compat@3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} @@ -9251,8 +9736,8 @@ packages: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' - react-test-renderer@19.2.7: - resolution: {integrity: sha512-U4TyPDJ9MsC8rFimXuJum8w40aPc9kbOZYO8Pc2/4A884i8hwJsMNA/JNyuOc/f2/37wHvk7HjpVl1V4re7Dig==} + react-test-renderer@19.2.8: + resolution: {integrity: sha512-GHKPaDRaNYU24PHTLG8Bx8VMY9t+qNfxQbt/Yjp7aMWBkKU6766SR0n6TnYu7P5I1MfEuAMUadqiyDHyI4Yy9Q==} peerDependencies: react: '>=18.0.0 <19.0.0' @@ -9282,13 +9767,13 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@5.0.0: - resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} - recast@0.23.11: - resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} - engines: {node: '>= 4'} + recast@0.23.20: + resolution: {integrity: sha512-VtSf75pThDqsIUpdaYrTdQvkw10/+yP0i7+Cax7h+K9SRvYjrJURZcRlcHMrH6TMzV275Q8a2A8+G7y7W9zqsg==} + engines: {node: '>= 22'} rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} @@ -9326,8 +9811,8 @@ packages: regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.13.2: - resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true regl-error2d@2.0.12: @@ -9336,8 +9821,8 @@ packages: regl-line2d@3.1.3: resolution: {integrity: sha512-fkgzW+tTn4QUQLpFKsUIE0sgWdCmXAM3ctXcCgoGBZTSX5FE2A0M7aynz7nrZT5baaftLrk9te54B+MEq4QcSA==} - regl-scatter2d@3.4.0: - resolution: {integrity: sha512-DavKQlHsI+iHZuLgOL+yGkg+sPd94CS+7FCBWkcQ6s/TbaNfUsF9eN591fjjSWIoKrGNfb/SEGhsXR5lXjqZ2w==} + regl-scatter2d@3.3.1: + resolution: {integrity: sha512-seOmMIVwaCwemSYz/y4WE0dbSO9svNFSqtTh5RE57I7PjGo3tcUYKtH0MTSoshcAsreoqN8HoCtnn8wfHXXfKQ==} regl-splom@1.0.14: resolution: {integrity: sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw==} @@ -9374,11 +9859,20 @@ packages: resolve@0.6.3: resolution: {integrity: sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@1.22.12: resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + resolve@2.0.0-next.7: resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} engines: {node: '>= 0.4'} @@ -9392,6 +9886,9 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + right-now@1.0.0: + resolution: {integrity: sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg==} + rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -9412,6 +9909,12 @@ packages: resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==} engines: {node: '>=8.3'} + rollup-plugin-license@3.6.0: + resolution: {integrity: sha512-1ieLxTCaigI5xokIfszVDRoy6c/Wmlot1fDEnea7Q/WXSR8AqOjYljHDLObAx7nFxHC2mbxT3QnTSPhaic2IYw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 + rollup-plugin-license@3.7.1: resolution: {integrity: sha512-FcGXUbAmPvRSLxjVdjp/r/MUtKBlttVQd+ApUyvKfREnsoAfAZA6Ic2fE1Tz4RL0f9XqEQU9UIRNUMdtQtliDw==} engines: {node: '>=14.0.0'} @@ -9439,8 +9942,13 @@ packages: peerDependencies: rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@4.62.0: - resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.62.4: + resolution: {integrity: sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -9461,8 +9969,8 @@ packages: resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} engines: {npm: '>=2.0.0'} - safe-array-concat@1.1.4: - resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} safe-buffer@5.1.2: @@ -9485,14 +9993,13 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.101.0: - resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + sass@1.102.0: + resolution: {integrity: sha512-NSOyTnaQF7rTAEOtI2fwb386vL+akyiQLBZu8Na7hXCb+umJy0GAqlcMIaqACZ6Z1VgTBS4K9PG6B3IdjHGJsw==} engines: {node: '>=20.19.0'} hasBin: true - sax@1.6.0: - resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} - engines: {node: '>=11.0.0'} + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -9512,8 +10019,13 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -9524,8 +10036,8 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serialize-javascript@7.0.5: - resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} + serialize-javascript@7.1.0: + resolution: {integrity: sha512-RNEqWOyhhUQYN9V1GfHwu9AR/g+NTciH6Z5u3/no6X3/w+04J2lVDL+svFQVXgXrEGBMG2puMVN3gq2SNGuTGw==} engines: {node: '>=20.0.0'} serve-static@2.2.1: @@ -9595,6 +10107,10 @@ packages: engines: {node: '>=18'} hasBin: true + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -9607,6 +10123,10 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + side-channel@1.1.1: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} @@ -9621,6 +10141,9 @@ packages: signature_pad@5.1.3: resolution: {integrity: sha512-zyxW5vuJVnQdGcU+kAj9FYl7WaAunY3kA5S7mPg0xJiujL9+sPAWfSQHS5tXaJXDUa4FuZeKhfdCDQ6K3wfkpQ==} + signum@1.0.0: + resolution: {integrity: sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -9635,20 +10158,17 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - smob@1.6.2: - resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} - engines: {node: '>=20.0.0'} + smob@1.5.0: + resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + + sort-object-keys@1.1.3: + resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} sort-object-keys@2.1.0: resolution: {integrity: sha512-SOiEnthkJKPv2L6ec6HMwhUcN0/lppkeYuN1x63PbyPRrgSPIuBJCiYxYyvWRTtjMlOi14vQUCGUJqS6PLVm8g==} - sort-package-json@3.6.0: - resolution: {integrity: sha512-fyJsPLhWvY7u2KsKPZn1PixbXp+1m7V8NWqU8CvgFRbMEX41Ffw1kD8n0CfJiGoaSfoAvbrqRRl/DcHO8omQOQ==} - engines: {node: '>=20'} - hasBin: true - - sort-package-json@3.7.1: - resolution: {integrity: sha512-ssk1HG7whF8N/T1IsNAQrtHG5Cbdi0rAgRJZXYBr9hF5xaHnBNzUx/W6LcthEW7FhOwvZssbESZuO+GxssqAyA==} + sort-package-json@3.4.0: + resolution: {integrity: sha512-97oFRRMM2/Js4oEA9LJhjyMlde+2ewpZQf53pgue27UkbEXfHJnDzHlUxQ/DWUkzqmp7DFwJp8D+wi/TYeQhpA==} engines: {node: '>=20'} hasBin: true @@ -9684,8 +10204,8 @@ packages: spdx-expression-validate@2.0.0: resolution: {integrity: sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==} - spdx-license-ids@3.0.23: - resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} spdx-ranges@2.1.1: resolution: {integrity: sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==} @@ -9710,6 +10230,10 @@ packages: static-eval@2.1.1: resolution: {integrity: sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==} + 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'} @@ -9753,12 +10277,12 @@ packages: string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.11: - resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.10: - resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: @@ -9778,8 +10302,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@3.0.0: @@ -9819,8 +10343,8 @@ packages: style-inject@0.3.0: resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} - style-mod@4.1.3: - resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} stylehacks@5.1.1: resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} @@ -9834,6 +10358,9 @@ packages: supercluster@8.0.1: resolution: {integrity: sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==} + superscript-text@1.0.0: + resolution: {integrity: sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -9855,24 +10382,28 @@ packages: svg-path-sdf@1.1.3: resolution: {integrity: sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg==} - svgo@2.8.2: - resolution: {integrity: sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==} + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} engines: {node: '>=10.13.0'} hasBin: true - swiper@12.2.0: - resolution: {integrity: sha512-K8uXsBZU6ME97Ia3xbBge8IRCnR1lOmIILzvY/jGVic7dSTQ530s3uO8RvXbPUtkkXLWIwmZLRPbtDxRWVAFdg==} + swiper@12.1.2: + resolution: {integrity: sha512-4gILrI3vXZqoZh71I1PALqukCFgk+gpOwe1tOvz5uE9kHtl2gTDzmYflYCwWvR4LOvCrJi6UEEU+gnuW5BtkgQ==} engines: {node: '>= 4.7.0'} symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + synckit@0.11.13: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} tar-fs@2.1.4: resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} @@ -9881,8 +10412,8 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} - terser@5.48.0: - resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} hasBin: true @@ -9905,9 +10436,12 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} - engines: {node: '>=18'} + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} @@ -9936,8 +10470,8 @@ packages: to-float32@1.1.0: resolution: {integrity: sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg==} - to-px@1.1.0: - resolution: {integrity: sha512-bfg3GLYrGoEzrGoE05TAL/Uw+H/qrf2ptr9V3W7U0lkjjyYnIfgxmVLUfhQ1hZpIQwin81uxhDjvUkDYsC0xWw==} + to-px@1.0.1: + resolution: {integrity: sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw==} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -9966,6 +10500,12 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>5.8.0 <6.0.0' + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -9976,8 +10516,8 @@ packages: resolution: {integrity: sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==} engines: {node: '>=14.0.0'} - ts-jest@29.4.11: - resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==} + ts-jest@29.4.12: + resolution: {integrity: sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -10029,8 +10569,38 @@ packages: tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - turbo@2.9.18: - resolution: {integrity: sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg==} + turbo-darwin-64@2.5.8: + resolution: {integrity: sha512-Dh5bCACiHO8rUXZLpKw+m3FiHtAp2CkanSyJre+SInEvEr5kIxjGvCK/8MFX8SFRjQuhjtvpIvYYZJB4AGCxNQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.5.8: + resolution: {integrity: sha512-f1H/tQC9px7+hmXn6Kx/w8Jd/FneIUnvLlcI/7RGHunxfOkKJKvsoiNzySkoHQ8uq1pJnhJ0xNGTlYM48ZaJOQ==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.5.8: + resolution: {integrity: sha512-hMyvc7w7yadBlZBGl/bnR6O+dJTx3XkTeyTTH4zEjERO6ChEs0SrN8jTFj1lueNXKIHh1SnALmy6VctKMGnWfw==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.5.8: + resolution: {integrity: sha512-LQELGa7bAqV2f+3rTMRPnj5G/OHAe2U+0N9BwsZvfMvHSUbsQ3bBMWdSQaYNicok7wOZcHjz2TkESn1hYK6xIQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.5.8: + resolution: {integrity: sha512-3YdcaW34TrN1AWwqgYL9gUqmZsMT4T7g8Y5Azz+uwwEJW+4sgcJkIi9pYFyU4ZBSjBvkfuPZkGgfStir5BBDJQ==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.5.8: + resolution: {integrity: sha512-eFC5XzLmgXJfnAK3UMTmVECCwuBcORrWdewoiXBnUm934DY6QN8YowC/srhNnROMpaKaqNeRpoB5FxCww3eteQ==} + cpu: [arm64] + os: [win32] + + turbo@2.5.8: + resolution: {integrity: sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w==} hasBin: true tweetnacl@1.0.3: @@ -10071,8 +10641,8 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.8: - resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} typedarray-pool@1.2.0: @@ -10081,8 +10651,8 @@ packages: typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript-eslint@8.61.1: - resolution: {integrity: sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==} + typescript-eslint@8.66.0: + resolution: {integrity: sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -10093,11 +10663,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -10152,8 +10717,14 @@ packages: unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-browserslist-db@1.3.0: + resolution: {integrity: sha512-x/M6q3w4Ybp91CNaS4S69UnliqR3BzRpOT6LWbksjth0S/+jhfaPJsWjt/TewpT8j9eLIojUf5jr29WextHroA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -10172,8 +10743,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -10265,8 +10836,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.22: - resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} which@1.3.1: @@ -10312,8 +10883,8 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@7.5.11: - resolution: {integrity: sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 @@ -10324,8 +10895,8 @@ packages: utf-8-validate: optional: true - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -10378,8 +10949,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@1.10.3: - resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} yargs-parser@20.2.9: @@ -10414,8 +10985,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zip-a-folder@6.1.1: - resolution: {integrity: sha512-8hjtUn4YQpj8HZvDwtGHhol27oDf+D1x70ldKwA3Bwru6gup62fDVrBTd+BC90/8REgjdCa5ep7EsBiGHudSdA==} + zip-a-folder@6.1.4: + resolution: {integrity: sha512-6zRF/xi0zRxKOCTBVWLwWyyp+zuAKyBhM3Q2ddgEg8uvjDFSTsKulc476jUAaNXnw3lI8pGRuffRNPbxNUld+g==} hasBin: true zod-to-json-schema@3.25.2: @@ -10434,9 +11005,9 @@ packages: snapshots: - '@adobe/css-tools@4.5.0': {} + '@adobe/css-tools@4.4.4': {} - '@altano/repository-tools@2.0.3': {} + '@altano/repository-tools@2.0.1': {} '@asamuzakjp/css-color@3.2.0': dependencies: @@ -10446,10 +11017,16 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@axe-core/playwright@4.11.3(playwright-core@1.61.0)': + '@axe-core/playwright@4.12.1(playwright-core@1.62.1)': dependencies: - axe-core: 4.11.4 - playwright-core: 1.61.0 + axe-core: 4.12.1 + playwright-core: 1.62.1 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 '@babel/code-frame@7.29.7': dependencies: @@ -10457,19 +11034,21 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.29.7': {} '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -10479,34 +11058,67 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.29.7(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.6.1))': + '@babel/eslint-parser@7.29.7(@babel/core@7.29.7)(eslint@9.39.3(jiti@2.6.1))': dependencies: '@babel/core': 7.29.7 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) eslint-visitor-keys: 2.1.0 semver: 6.3.1 - '@babel/generator@7.29.7': + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/generator@7.29.8': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 + '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.26.3 + lru-cache: 5.1.1 + semver: 6.3.1 '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.2 + browserslist: 4.26.3 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.4 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10515,11 +11127,18 @@ snapshots: '@babel/helper-optimise-call-expression': 7.29.7 '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 semver: 6.3.1 transitivePeerDependencies: - supports-color + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10527,6 +11146,17 @@ snapshots: regexpu-core: 6.4.0 semver: 6.3.1 + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10538,19 +11168,44 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/helper-member-expression-to-functions@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -10559,69 +11214,118 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.4 + '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-plugin-utils@7.29.7': {} + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-wrap-function': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 - '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color + '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} + '@babel/helper-wrap-function@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/helper-wrap-function@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 - '@babel/parser@7.29.7': + '@babel/parser@7.29.8': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -10656,14 +11360,14 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-export-default-from@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)': dependencies: @@ -10672,32 +11376,37 @@ snapshots: '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-export-default-from@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-export-default-from@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-flow@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-flow@7.29.7(@babel/core@7.29.7)': dependencies: @@ -10709,6 +11418,11 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10717,12 +11431,17 @@ snapshots: '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: @@ -10732,42 +11451,47 @@ snapshots: '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': dependencies: @@ -10777,20 +11501,43 @@ snapshots: '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.7) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.7) transitivePeerDependencies: - supports-color @@ -10808,11 +11555,24 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10829,6 +11589,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.7) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10837,21 +11609,35 @@ snapshots: '@babel/helper-globals': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/template': 7.29.7 + '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -10895,12 +11681,26 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-flow-strip-types@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10909,12 +11709,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -10923,11 +11732,21 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10946,6 +11765,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10954,13 +11781,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-systemjs@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -10972,6 +11799,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -10983,16 +11816,37 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.7) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11000,7 +11854,7 @@ snapshots: '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -11012,11 +11866,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11025,11 +11892,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11038,6 +11918,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11052,6 +11941,11 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11064,15 +11958,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)': dependencies: @@ -11081,7 +11975,7 @@ snapshots: '@babel/helper-module-imports': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -11091,7 +11985,12 @@ snapshots: '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 @@ -11107,24 +12006,37 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-runtime@7.28.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.29.7) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.7) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.29.7) semver: 6.3.1 transitivePeerDependencies: - supports-color + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-spread@7.29.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 @@ -11132,6 +12044,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11147,6 +12064,17 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.7) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11169,6 +12097,12 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.29.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -11223,7 +12157,7 @@ snapshots: '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-systemjs': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) @@ -11237,11 +12171,11 @@ snapshots: '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.29.8(@babel/core@7.29.7) '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) @@ -11253,7 +12187,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) - core-js-compat: 3.49.0 + core-js-compat: 3.50.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -11269,7 +12203,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.28.4 esutils: 2.0.3 '@babel/preset-react@7.29.7(@babel/core@7.29.7)': @@ -11304,27 +12238,50 @@ snapshots: pirates: 4.0.7 source-map-support: 0.5.21 - '@babel/runtime@7.29.7': {} + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 - '@babel/traverse@7.29.7': + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.29.8': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.7': + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 @@ -11335,98 +12292,98 @@ snapshots: dependencies: commander: 2.20.3 - '@codemirror/autocomplete@6.20.3': + '@codemirror/autocomplete@6.19.0': dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/common': 1.2.3 - '@codemirror/commands@6.10.3': + '@codemirror/commands@6.9.0': dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/common': 1.2.3 '@codemirror/lang-css@6.3.1': dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 + '@codemirror/autocomplete': 6.19.0 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.0 '@codemirror/lang-html@6.4.11': dependencies: - '@codemirror/autocomplete': 6.20.3 + '@codemirror/autocomplete': 6.19.0 '@codemirror/lang-css': 6.3.1 - '@codemirror/lang-javascript': 6.2.5 - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 - '@lezer/html': 1.3.13 - - '@codemirror/lang-javascript@6.2.5': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 + '@codemirror/lang-javascript': 6.2.4 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/common': 1.2.3 + '@lezer/css': 1.3.0 + '@lezer/html': 1.3.12 + + '@codemirror/lang-javascript@6.2.4': + dependencies: + '@codemirror/autocomplete': 6.19.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/common': 1.2.3 '@lezer/javascript': 1.5.4 - '@codemirror/language@6.12.3': + '@codemirror/language@6.11.3': dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - style-mod: 4.1.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 - '@codemirror/lint@6.9.7': + '@codemirror/lint@6.9.0': dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 crelt: 1.0.6 - '@codemirror/search@6.7.0': + '@codemirror/search@6.5.11': dependencies: - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 crelt: 1.0.6 - '@codemirror/state@6.6.0': + '@codemirror/state@6.5.2': dependencies: '@marijn/find-cluster-break': 1.0.2 '@codemirror/theme-one-dark@6.1.3': dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 - '@lezer/highlight': 1.2.3 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 + '@lezer/highlight': 1.2.1 - '@codemirror/view@6.43.1': + '@codemirror/view@6.38.6': dependencies: - '@codemirror/state': 6.6.0 + '@codemirror/state': 6.5.2 crelt: 1.0.6 - style-mod: 4.1.3 + style-mod: 4.1.2 w3c-keyname: 2.2.8 - '@commitlint/cli@21.2.1(@types/node@24.12.4)(conventional-commits-parser@7.1.0)(typescript@6.0.3)': + '@commitlint/cli@21.2.1(@types/node@24.12.4)(conventional-commits-parser@7.1.2)(typescript@5.9.3)': dependencies: '@commitlint/config-conventional': 21.2.0 '@commitlint/format': 21.2.0 '@commitlint/lint': 21.2.0 - '@commitlint/load': 21.2.0(@types/node@24.12.4)(typescript@6.0.3) - '@commitlint/read': 21.2.1(conventional-commits-parser@7.1.0) + '@commitlint/load': 21.2.0(@types/node@24.12.4)(typescript@5.9.3) + '@commitlint/read': 21.2.1(conventional-commits-parser@7.1.2) '@commitlint/types': 21.2.0 - tinyexec: 1.2.4 + tinyexec: 1.0.1 yargs: 18.0.0 transitivePeerDependencies: - '@types/node' @@ -11442,12 +12399,12 @@ snapshots: '@commitlint/config-validator@21.2.0': dependencies: '@commitlint/types': 21.2.0 - ajv: 8.20.0 + ajv: 8.17.1 '@commitlint/ensure@21.2.0': dependencies: '@commitlint/types': 21.2.0 - es-toolkit: 1.49.0 + es-toolkit: 1.50.0 '@commitlint/execute-rule@21.0.1': {} @@ -11459,7 +12416,7 @@ snapshots: '@commitlint/is-ignored@21.2.0': dependencies: '@commitlint/types': 21.2.0 - semver: 7.8.4 + semver: 7.7.3 '@commitlint/lint@21.2.0': dependencies: @@ -11468,15 +12425,15 @@ snapshots: '@commitlint/rules': 21.2.0 '@commitlint/types': 21.2.0 - '@commitlint/load@21.2.0(@types/node@24.12.4)(typescript@6.0.3)': + '@commitlint/load@21.2.0(@types/node@24.12.4)(typescript@5.9.3)': dependencies: '@commitlint/config-validator': 21.2.0 '@commitlint/execute-rule': 21.0.1 '@commitlint/resolve-extends': 21.2.0 '@commitlint/types': 21.2.0 - cosmiconfig: 9.0.2(typescript@6.0.3) - cosmiconfig-typescript-loader: 6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3) - es-toolkit: 1.49.0 + cosmiconfig: 9.0.2(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3) + es-toolkit: 1.50.0 is-plain-obj: 4.1.0 picocolors: 1.1.1 transitivePeerDependencies: @@ -11489,14 +12446,14 @@ snapshots: dependencies: '@commitlint/types': 21.2.0 conventional-changelog-angular: 9.2.1 - conventional-commits-parser: 7.1.0 + conventional-commits-parser: 7.1.2 - '@commitlint/read@21.2.1(conventional-commits-parser@7.1.0)': + '@commitlint/read@21.2.1(conventional-commits-parser@7.1.2)': dependencies: '@commitlint/top-level': 21.2.0 '@commitlint/types': 21.2.0 - '@conventional-changelog/git-client': 3.1.0(conventional-commits-parser@7.1.0) - tinyexec: 1.2.4 + '@conventional-changelog/git-client': 3.1.1(conventional-commits-parser@7.1.2) + tinyexec: 1.0.1 transitivePeerDependencies: - conventional-commits-filter - conventional-commits-parser @@ -11505,7 +12462,7 @@ snapshots: dependencies: '@commitlint/config-validator': 21.2.0 '@commitlint/types': 21.2.0 - es-toolkit: 1.49.0 + es-toolkit: 1.50.0 global-directory: 5.0.0 resolve-from: 5.0.0 @@ -11524,16 +12481,16 @@ snapshots: '@commitlint/types@21.2.0': dependencies: - conventional-commits-parser: 7.1.0 + conventional-commits-parser: 7.1.2 picocolors: 1.1.1 - '@conventional-changelog/git-client@3.1.0(conventional-commits-parser@7.1.0)': + '@conventional-changelog/git-client@3.1.1(conventional-commits-parser@7.1.2)': dependencies: '@simple-libs/child-process-utils': 2.0.0 '@simple-libs/stream-utils': 2.0.0 - semver: 7.8.4 + semver: 7.7.3 optionalDependencies: - conventional-commits-parser: 7.1.0 + conventional-commits-parser: 7.1.2 '@conventional-changelog/template@1.2.1': {} @@ -11577,18 +12534,25 @@ snapshots: tslib: 2.8.1 optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.3(jiti@2.6.1))': dependencies: - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.3(jiti@2.6.1))': + dependencies: + eslint: 9.39.3(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 3.1.5 + minimatch: 3.1.4 transitivePeerDependencies: - supports-color @@ -11600,21 +12564,25 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.1': dependencies: - ajv: 6.15.0 + ajv: 6.12.6 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.3.0 - minimatch: 3.1.5 + js-yaml: 4.1.1 + minimatch: 3.1.4 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.39.4': {} + '@eslint/js@9.37.0': {} + + '@eslint/js@9.39.3': {} + + '@eslint/js@9.39.5': {} '@eslint/object-schema@2.1.7': {} @@ -11623,66 +12591,78 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@floating-ui/core@1.7.5': + '@floating-ui/core@1.7.3': dependencies: - '@floating-ui/utils': 0.2.11 + '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.6': + '@floating-ui/core@1.8.0': dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 + '@floating-ui/utils': 0.2.12 - '@floating-ui/react-dom@2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/dom@1.7.4': dependencies: - '@floating-ui/dom': 1.7.6 + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/react-dom@2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.8.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) '@floating-ui/react@0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@floating-ui/utils': 0.2.11 + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.10 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.4.0 + tabbable: 6.2.0 - '@floating-ui/react@0.27.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react@0.27.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@floating-ui/utils': 0.2.11 + '@floating-ui/react-dom': 2.1.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/utils': 0.2.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.4.0 + tabbable: 6.2.0 + + '@floating-ui/utils@0.2.10': {} - '@floating-ui/utils@0.2.11': {} + '@floating-ui/utils@0.2.12': {} - '@googlemaps/jest-mocks@2.22.8': {} + '@googlemaps/jest-mocks@2.22.6': {} - '@happy-dom/jest-environment@19.0.2(@jest/environment@30.3.0)(@jest/fake-timers@30.3.0)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1)': + '@happy-dom/jest-environment@19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1)': dependencies: - '@jest/environment': 30.3.0 - '@jest/fake-timers': 30.3.0 + '@jest/environment': 30.4.1 + '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 happy-dom: 19.0.2 jest-mock: 30.4.1 jest-util: 30.4.1 - '@hono/node-server@1.19.17(hono@4.12.27)': + '@hono/node-server@2.1.0(hono@4.13.1)': dependencies: - hono: 4.12.27 + hono: 4.13.1 - '@humanfs/core@0.19.2': - dependencies: - '@humanfs/types': 0.15.0 + '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.8': + '@humanfs/node@0.16.7': dependencies: - '@humanfs/core': 0.19.2 - '@humanfs/types': 0.15.0 + '@humanfs/core': 0.19.1 '@humanwhocodes/retry': 0.4.3 - '@humanfs/types@0.15.0': {} - '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} @@ -11695,22 +12675,20 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@isaacs/cliui@9.0.0': {} - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.15.0 + js-yaml: 3.14.2 resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.6': {} + '@istanbuljs/schema@0.1.3': {} '@jest/console@30.3.0': dependencies: @@ -11732,7 +12710,7 @@ snapshots: '@types/node': 24.12.4 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.3.0 @@ -11757,7 +12735,7 @@ snapshots: - ts-node optional: true - '@jest/core@30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3))': + '@jest/core@30.3.0(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3))': dependencies: '@jest/console': 30.3.0 '@jest/pattern': 30.0.1 @@ -11768,11 +12746,11 @@ snapshots: '@types/node': 24.12.4 ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) jest-haste-map: 30.3.0 jest-message-util: 30.3.0 jest-regex-util: 30.0.1 @@ -11792,15 +12770,15 @@ snapshots: - supports-color - ts-node - '@jest/create-cache-key-function@30.4.1': + '@jest/create-cache-key-function@30.2.0': dependencies: - '@jest/types': 30.4.1 + '@jest/types': 30.2.0 '@jest/diff-sequences@30.3.0': {} '@jest/diff-sequences@30.4.0': {} - '@jest/environment-jsdom-abstract@30.3.0(canvas@3.2.3)(jsdom@26.1.0(canvas@3.2.3))': + '@jest/environment-jsdom-abstract@30.3.0(canvas@3.2.0)(jsdom@26.1.0(canvas@3.2.0))': dependencies: '@jest/environment': 30.3.0 '@jest/fake-timers': 30.3.0 @@ -11809,9 +12787,22 @@ snapshots: '@types/node': 24.12.4 jest-mock: 30.3.0 jest-util: 30.3.0 - jsdom: 26.1.0(canvas@3.2.3) + jsdom: 26.1.0(canvas@3.2.0) + optionalDependencies: + canvas: 3.2.0 + + '@jest/environment-jsdom-abstract@30.4.1(canvas@3.2.0)(jsdom@26.1.0(canvas@3.2.0))': + dependencies: + '@jest/environment': 30.4.1 + '@jest/fake-timers': 30.4.1 + '@jest/types': 30.4.1 + '@types/jsdom': 21.1.7 + '@types/node': 24.12.4 + jest-mock: 30.4.1 + jest-util: 30.4.1 + jsdom: 26.1.0(canvas@3.2.0) optionalDependencies: - canvas: 3.2.3 + canvas: 3.2.0 '@jest/environment@30.3.0': dependencies: @@ -11820,6 +12811,13 @@ snapshots: '@types/node': 24.12.4 jest-mock: 30.3.0 + '@jest/environment@30.4.1': + dependencies: + '@jest/fake-timers': 30.4.1 + '@jest/types': 30.4.1 + '@types/node': 24.12.4 + jest-mock: 30.4.1 + '@jest/expect-utils@30.3.0': dependencies: '@jest/get-type': 30.1.0 @@ -11844,6 +12842,15 @@ snapshots: jest-mock: 30.3.0 jest-util: 30.3.0 + '@jest/fake-timers@30.4.1': + dependencies: + '@jest/types': 30.4.1 + '@sinonjs/fake-timers': 15.4.0 + '@types/node': 24.12.4 + jest-message-util: 30.4.1 + jest-mock: 30.4.1 + jest-util: 30.4.1 + '@jest/get-type@30.1.0': {} '@jest/globals@30.3.0': @@ -11875,7 +12882,7 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 '@types/node': 24.12.4 chalk: 4.1.2 - collect-v8-coverage: 1.0.3 + collect-v8-coverage: 1.0.2 exit-x: 0.2.2 glob: 10.5.0 graceful-fs: 4.2.11 @@ -11895,11 +12902,11 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.49 + '@sinclair/typebox': 0.34.41 '@jest/schemas@30.4.1': dependencies: - '@sinclair/typebox': 0.34.49 + '@sinclair/typebox': 0.34.41 '@jest/snapshot-utils@30.3.0': dependencies: @@ -11919,7 +12926,7 @@ snapshots: '@jest/console': 30.3.0 '@jest/types': 30.3.0 '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 + collect-v8-coverage: 1.0.2 '@jest/test-sequencer@30.3.0': dependencies: @@ -11947,6 +12954,35 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/transform@30.4.1': + dependencies: + '@babel/core': 7.29.7 + '@jest/types': 30.4.1 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.4.1 + jest-regex-util: 30.4.0 + jest-util: 30.4.1 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/types@30.2.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.12.4 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + '@jest/types@30.3.0': dependencies: '@jest/pattern': 30.0.1 @@ -11954,7 +12990,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 '@types/node': 24.12.4 - '@types/yargs': 17.0.35 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jest/types@30.4.1': @@ -11964,7 +13000,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 '@types/node': 24.12.4 - '@types/yargs': 17.0.35 + '@types/yargs': 17.0.33 chalk: 4.1.2 '@jridgewell/gen-mapping@0.3.13': @@ -11996,240 +13032,147 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@lezer/common@1.5.2': {} - - '@lezer/css@1.3.3': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - - '@lezer/highlight@1.2.3': - dependencies: - '@lezer/common': 1.5.2 - - '@lezer/html@1.3.13': - dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 + '@lezer/common@1.2.3': {} - '@lezer/javascript@1.5.4': + '@lezer/css@1.3.0': dependencies: - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 - '@lezer/lr@1.4.10': + '@lezer/highlight@1.2.1': dependencies: - '@lezer/common': 1.5.2 + '@lezer/common': 1.2.3 - '@mapbox/geojson-rewind@0.5.2': + '@lezer/html@1.3.12': dependencies: - get-stream: 6.0.1 - minimist: 1.2.8 - - '@mapbox/geojson-types@1.0.2': {} - - '@mapbox/jsonlint-lines-primitives@2.0.2': {} - - '@mapbox/mapbox-gl-supported@1.5.0(mapbox-gl@1.13.3)': - dependencies: - mapbox-gl: 1.13.3 - - '@mapbox/point-geometry@0.1.0': {} - - '@mapbox/tiny-sdf@1.2.5': {} - - '@mapbox/tiny-sdf@2.2.0': {} - - '@mapbox/unitbezier@0.0.0': {} - - '@mapbox/unitbezier@0.0.1': {} - - '@mapbox/vector-tile@1.3.1': - dependencies: - '@mapbox/point-geometry': 0.1.0 - - '@mapbox/whoots-js@3.1.0': {} - - '@maplibre/maplibre-gl-style-spec@20.4.0': - dependencies: - '@mapbox/jsonlint-lines-primitives': 2.0.2 - '@mapbox/unitbezier': 0.0.1 - json-stringify-pretty-compact: 4.0.0 - minimist: 1.2.8 - quickselect: 2.0.0 - rw: 1.3.3 - tinyqueue: 3.0.0 - - '@marijn/find-cluster-break@1.0.2': {} - - '@melloware/coloris@0.25.0': {} - - '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.4)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': - dependencies: - '@babel/core': 7.29.7 - '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.6.1)) - '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/preset-env': 7.29.7(@babel/core@7.29.7) - '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@prettier/plugin-xml': 3.4.2(prettier@3.8.4) - '@react-native/babel-preset': 0.77.3(@babel/core@7.29.7)(@babel/preset-env@7.29.7(@babel/core@7.29.7)) - '@rollup/plugin-alias': 5.1.1(rollup@4.62.0) - '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0) - '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.0) - '@rollup/plugin-image': 3.0.3(rollup@4.62.0) - '@rollup/plugin-json': 6.1.0(rollup@4.62.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.0) - '@rollup/plugin-terser': 1.0.0(rollup@4.62.0) - '@rollup/plugin-typescript': 12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3) - '@rollup/plugin-url': 8.0.2(rollup@4.62.0) - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) - '@testing-library/dom': 10.4.1 - '@testing-library/jest-dom': 6.9.1 - '@testing-library/react': 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@types/semver': 7.7.1 - '@types/testing-library__jest-dom': 5.14.9 - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - ansi-colors: 4.1.3 - babel-jest: 30.3.0(@babel/core@7.29.7) - big.js: 6.2.2 - core-js: 3.49.0 - dotenv: 17.4.2 - fast-glob: 3.3.3 - fs-extra: 11.3.5 - identity-obj-proxy: 3.0.0 - jasmine: 3.99.0 - jasmine-core: 3.99.1 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) - jest-environment-jsdom: 30.3.0(canvas@3.2.3) - jest-jasmine2: 30.3.0 - jest-junit: 17.0.0 - make-dir: 5.1.0 - mendix: 11.10.0 - mime: 4.1.0 - postcss: 8.5.15 - postcss-import: 14.1.0(postcss@8.5.15) - postcss-url: 10.1.4(postcss@8.5.15) - react-test-renderer: 19.2.7(react@18.3.1) - recursive-copy: 2.0.14 - resolve: 1.22.12 - rollup: 4.62.0 - rollup-plugin-clear: 2.0.7 - rollup-plugin-command: 1.1.3 - rollup-plugin-license: 3.7.1(picomatch@4.0.4)(rollup@4.62.0) - rollup-plugin-livereload: 2.0.5 - rollup-plugin-postcss: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) - rollup-plugin-re: 1.0.7 - sass: 1.101.0 - semver: 7.8.4 - shelljs: 0.10.0 - shx: 0.4.0 - ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@30.3.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) - typescript: 5.9.3 - xml2js: 0.6.2 - zip-a-folder: 6.1.1 - transitivePeerDependencies: - - '@jest/transform' - - '@jest/types' - - '@swc/core' - - '@swc/wasm' - - '@types/babel__core' - - '@types/node' - - babel-plugin-macros - - bufferutil - - canvas - - esbuild - - esbuild-register - - eslint - - jest-util - - node-notifier - - picomatch - - prettier - - react - - react-dom - - supports-color - - tslib - - utf-8-validate + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 - '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.3.0)(@jest/types@30.4.1)(@swc/core@1.15.41)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.3)(eslint@9.39.4(jiti@2.6.1))(jest-util@30.4.1)(prettier@3.8.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': + '@lezer/javascript@1.5.4': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + + '@mapbox/geojson-rewind@0.5.2': + dependencies: + get-stream: 6.0.1 + minimist: 1.2.8 + + '@mapbox/geojson-types@1.0.2': {} + + '@mapbox/jsonlint-lines-primitives@2.0.2': {} + + '@mapbox/mapbox-gl-supported@1.5.0(mapbox-gl@1.13.3)': + dependencies: + mapbox-gl: 1.13.3 + + '@mapbox/point-geometry@0.1.0': {} + + '@mapbox/tiny-sdf@1.2.5': {} + + '@mapbox/tiny-sdf@2.0.7': {} + + '@mapbox/unitbezier@0.0.0': {} + + '@mapbox/unitbezier@0.0.1': {} + + '@mapbox/vector-tile@1.3.1': + dependencies: + '@mapbox/point-geometry': 0.1.0 + + '@mapbox/whoots-js@3.1.0': {} + + '@maplibre/maplibre-gl-style-spec@20.4.0': + dependencies: + '@mapbox/jsonlint-lines-primitives': 2.0.2 + '@mapbox/unitbezier': 0.0.1 + json-stringify-pretty-compact: 4.0.0 + minimist: 1.2.8 + quickselect: 2.0.0 + rw: 1.3.3 + tinyqueue: 3.0.0 + + '@marijn/find-cluster-break@1.0.2': {} + + '@melloware/coloris@0.25.0': {} + + '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': dependencies: '@babel/core': 7.29.7 - '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.6.1)) + '@babel/eslint-parser': 7.29.7(@babel/core@7.29.7)(eslint@9.39.3(jiti@2.6.1)) '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) '@babel/preset-env': 7.29.7(@babel/core@7.29.7) '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@prettier/plugin-xml': 3.4.2(prettier@3.8.4) + '@prettier/plugin-xml': 3.4.2(prettier@3.9.6) '@react-native/babel-preset': 0.77.3(@babel/core@7.29.7)(@babel/preset-env@7.29.7(@babel/core@7.29.7)) - '@rollup/plugin-alias': 5.1.1(rollup@4.62.0) - '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0) - '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.0) - '@rollup/plugin-image': 3.0.3(rollup@4.62.0) - '@rollup/plugin-json': 6.1.0(rollup@4.62.0) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.0) - '@rollup/plugin-terser': 1.0.0(rollup@4.62.0) - '@rollup/plugin-typescript': 12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3) - '@rollup/plugin-url': 8.0.2(rollup@4.62.0) - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/plugin-alias': 5.1.1(rollup@4.62.4) + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.4) + '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.4) + '@rollup/plugin-image': 3.0.3(rollup@4.62.4) + '@rollup/plugin-json': 6.1.0(rollup@4.62.4) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.62.4) + '@rollup/plugin-terser': 1.0.0(rollup@4.62.4) + '@rollup/plugin-typescript': 12.1.4(rollup@4.62.4)(tslib@2.8.1)(typescript@5.9.3) + '@rollup/plugin-url': 8.0.2(rollup@4.62.4) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) '@testing-library/dom': 10.4.1 '@testing-library/jest-dom': 6.9.1 - '@testing-library/react': 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/react': 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.2 + '@types/react-dom': 19.2.4(@types/react@19.2.2) '@types/semver': 7.7.1 '@types/testing-library__jest-dom': 5.14.9 - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) ansi-colors: 4.1.3 - babel-jest: 30.3.0(@babel/core@7.29.7) + babel-jest: 30.4.1(@babel/core@7.29.7) big.js: 6.2.2 - core-js: 3.49.0 + core-js: 3.46.0 dotenv: 17.4.2 fast-glob: 3.3.3 - fs-extra: 11.3.5 + fs-extra: 11.4.0 identity-obj-proxy: 3.0.0 jasmine: 3.99.0 jasmine-core: 3.99.1 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) - jest-environment-jsdom: 30.3.0(canvas@3.2.3) + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) + jest-environment-jsdom: 30.3.0(canvas@3.2.0) jest-jasmine2: 30.3.0 jest-junit: 17.0.0 make-dir: 5.1.0 - mendix: 11.10.0 + mendix: 11.13.0 mime: 4.1.0 - postcss: 8.5.15 - postcss-import: 14.1.0(postcss@8.5.15) - postcss-url: 10.1.4(postcss@8.5.15) - react-test-renderer: 19.2.7(react@18.3.1) + postcss: 8.5.26 + postcss-import: 14.1.0(postcss@8.5.26) + postcss-url: 10.1.3(postcss@8.5.26) + react-test-renderer: 19.2.8(react@18.3.1) recursive-copy: 2.0.14 resolve: 1.22.12 - rollup: 4.62.0 + rollup: 4.62.4 rollup-plugin-clear: 2.0.7 rollup-plugin-command: 1.1.3 - rollup-plugin-license: 3.7.1(rollup@4.62.0) + rollup-plugin-license: 3.7.1(picomatch@4.0.5)(rollup@4.62.4) rollup-plugin-livereload: 2.0.5 - rollup-plugin-postcss: 4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + rollup-plugin-postcss: 4.0.2(postcss@8.5.26)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) rollup-plugin-re: 1.0.7 - sass: 1.101.0 - semver: 7.8.4 + sass: 1.102.0 + semver: 7.7.3 shelljs: 0.10.0 shx: 0.4.0 - ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@30.3.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + ts-jest: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3) typescript: 5.9.3 xml2js: 0.6.2 - zip-a-folder: 6.1.1 + zip-a-folder: 6.1.4 transitivePeerDependencies: - '@jest/transform' - '@jest/types' @@ -12255,18 +13198,18 @@ snapshots: '@modelcontextprotocol/sdk@1.30.0(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.17(hono@4.12.27) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) + '@hono/node-server': 2.1.0(hono@4.13.1) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.1.0 express: 5.2.1 - express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.27 - jose: 6.2.3 + express-rate-limit: 8.6.2(express@5.2.1) + hono: 4.13.1 + jose: 6.2.8 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 @@ -12275,11 +13218,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 + '@tybys/wasm-util': 0.10.3 optional: true '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': @@ -12296,81 +13242,81 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + fastq: 1.19.1 '@one-ini/wasm@0.2.1': {} - '@parcel/watcher-android-arm64@2.5.6': + '@parcel/watcher-android-arm64@2.5.1': optional: true - '@parcel/watcher-darwin-arm64@2.5.6': + '@parcel/watcher-darwin-arm64@2.5.1': optional: true - '@parcel/watcher-darwin-x64@2.5.6': + '@parcel/watcher-darwin-x64@2.5.1': optional: true - '@parcel/watcher-freebsd-x64@2.5.6': + '@parcel/watcher-freebsd-x64@2.5.1': optional: true - '@parcel/watcher-linux-arm-glibc@2.5.6': + '@parcel/watcher-linux-arm-glibc@2.5.1': optional: true - '@parcel/watcher-linux-arm-musl@2.5.6': + '@parcel/watcher-linux-arm-musl@2.5.1': optional: true - '@parcel/watcher-linux-arm64-glibc@2.5.6': + '@parcel/watcher-linux-arm64-glibc@2.5.1': optional: true - '@parcel/watcher-linux-arm64-musl@2.5.6': + '@parcel/watcher-linux-arm64-musl@2.5.1': optional: true - '@parcel/watcher-linux-x64-glibc@2.5.6': + '@parcel/watcher-linux-x64-glibc@2.5.1': optional: true - '@parcel/watcher-linux-x64-musl@2.5.6': + '@parcel/watcher-linux-x64-musl@2.5.1': optional: true - '@parcel/watcher-win32-arm64@2.5.6': + '@parcel/watcher-win32-arm64@2.5.1': optional: true - '@parcel/watcher-win32-ia32@2.5.6': + '@parcel/watcher-win32-ia32@2.5.1': optional: true - '@parcel/watcher-win32-x64@2.5.6': + '@parcel/watcher-win32-x64@2.5.1': optional: true - '@parcel/watcher@2.5.6': + '@parcel/watcher@2.5.1': dependencies: - detect-libc: 2.1.2 + detect-libc: 1.0.3 is-glob: 4.0.3 + micromatch: 4.0.8 node-addon-api: 7.1.1 - picomatch: 4.0.4 optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.6 - '@parcel/watcher-darwin-arm64': 2.5.6 - '@parcel/watcher-darwin-x64': 2.5.6 - '@parcel/watcher-freebsd-x64': 2.5.6 - '@parcel/watcher-linux-arm-glibc': 2.5.6 - '@parcel/watcher-linux-arm-musl': 2.5.6 - '@parcel/watcher-linux-arm64-glibc': 2.5.6 - '@parcel/watcher-linux-arm64-musl': 2.5.6 - '@parcel/watcher-linux-x64-glibc': 2.5.6 - '@parcel/watcher-linux-x64-musl': 2.5.6 - '@parcel/watcher-win32-arm64': 2.5.6 - '@parcel/watcher-win32-ia32': 2.5.6 - '@parcel/watcher-win32-x64': 2.5.6 + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 optional: true '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.10': {} + '@pkgr/core@0.2.9': {} '@pkgr/core@0.3.6': {} - '@playwright/test@1.61.0': + '@playwright/test@1.62.1': dependencies: - playwright: 1.61.0 + playwright: 1.62.1 '@plotly/d3-sankey-circular@0.33.1': dependencies: @@ -12431,95 +13377,96 @@ snapshots: '@popperjs/core@2.11.8': {} - '@prettier/plugin-xml@3.4.2(prettier@3.8.4)': + '@prettier/plugin-xml@3.4.2(prettier@3.9.6)': dependencies: '@xml-tools/parser': 1.0.11 - prettier: 3.8.4 + prettier: 3.9.6 - '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@18.3.1)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 - '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@18.3.1)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 - '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.2 + '@types/react-dom': 19.2.4(@types/react@19.2.2) - '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.2 + '@types/react-dom': 19.2.4(@types/react@19.2.2) - '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@18.3.1)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@18.3.1)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@18.3.1) react: 18.3.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 - '@rc-component/motion@1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/motion@1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.1 + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/portal@2.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/portal@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.1 + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/resize-observer@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/resize-observer@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/slider@1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/slider@1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/tooltip@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/tooltip@1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/trigger': 3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.1 + '@rc-component/trigger': 3.6.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/trigger@3.9.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/trigger@3.6.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@rc-component/motion': 1.3.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/portal': 2.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/resize-observer': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@rc-component/util': 1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - clsx: 2.1.1 + '@rc-component/motion': 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/portal': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/resize-observer': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/util': 1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@rc-component/util@1.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@rc-component/util@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: is-mobile: 5.0.0 react: 18.3.1 @@ -12528,7 +13475,7 @@ snapshots: '@react-native/babel-plugin-codegen@0.77.3(@babel/preset-env@7.29.7(@babel/core@7.29.7))': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.28.4 '@react-native/codegen': 0.77.3(@babel/preset-env@7.29.7(@babel/core@7.29.7)) transitivePeerDependencies: - '@babel/preset-env' @@ -12537,46 +13484,46 @@ snapshots: '@react-native/babel-preset@0.77.3(@babel/core@7.29.7)(@babel/preset-env@7.29.7(@babel/core@7.29.7))': dependencies: '@babel/core': 7.29.7 - '@babel/plugin-proposal-export-default-from': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.7) '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-syntax-export-default-from': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-export-default-from': 7.27.1(@babel/core@7.29.7) '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7) - '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.29.7) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.29.7) '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-flow-strip-types': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.29.7) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.29.7) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.29.7) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.7) '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) - '@babel/template': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.29.7) + '@babel/plugin-transform-runtime': 7.28.3(@babel/core@7.29.7) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.29.7) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.7) + '@babel/template': 7.27.2 '@react-native/babel-plugin-codegen': 0.77.3(@babel/preset-env@7.29.7(@babel/core@7.29.7)) babel-plugin-syntax-hermes-parser: 0.25.1 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.7) @@ -12587,12 +13534,12 @@ snapshots: '@react-native/codegen@0.77.3(@babel/preset-env@7.29.7(@babel/core@7.29.7))': dependencies: - '@babel/parser': 7.29.7 + '@babel/parser': 7.28.4 '@babel/preset-env': 7.29.7(@babel/core@7.29.7) glob: 7.2.3 hermes-parser: 0.25.1 invariant: 2.2.4 - jscodeshift: 17.3.0(@babel/preset-env@7.29.7(@babel/core@7.29.7)) + jscodeshift: 17.4.0(@babel/preset-env@7.29.7(@babel/core@7.29.7)) nullthrows: 1.1.1 yargs: 17.7.2 transitivePeerDependencies: @@ -12603,177 +13550,222 @@ snapshots: dequal: 2.0.3 react: 18.3.1 - '@rollup/plugin-alias@5.1.1(rollup@4.62.0)': + '@rollup/plugin-alias@5.1.1(rollup@4.62.4)': optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.0)': + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7)(@types/babel__core@7.20.5)(rollup@4.62.4)': dependencies: '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@babel/helper-module-imports': 7.27.1 + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) optionalDependencies: '@types/babel__core': 7.20.5 - rollup: 4.62.0 + rollup: 4.62.4 transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@29.0.3(rollup@4.62.0)': + '@rollup/plugin-commonjs@29.0.3(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.19 + picomatch: 4.0.3 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-commonjs@29.0.3(rollup@4.62.4)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.4) + fdir: 6.5.0(picomatch@4.0.3) is-reference: 1.2.1 - magic-string: 0.30.21 - picomatch: 4.0.4 + magic-string: 0.30.19 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-image@3.0.3(rollup@4.62.0)': + '@rollup/plugin-image@3.0.3(rollup@4.62.4)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) mini-svg-data-uri: 1.4.4 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-json@6.1.0(rollup@4.62.0)': + '@rollup/plugin-json@6.1.0(rollup@4.62.4)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.0)': + '@rollup/plugin-node-resolve@15.3.1(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@3.29.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.12 + resolve: 1.22.10 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-replace@6.0.3(rollup@4.62.0)': + '@rollup/plugin-replace@6.0.2(rollup@3.29.5)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) - magic-string: 0.30.21 + '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + magic-string: 0.30.19 optionalDependencies: - rollup: 4.62.0 + rollup: 3.29.5 - '@rollup/plugin-terser@0.4.4(rollup@4.62.0)': + '@rollup/plugin-replace@6.0.2(rollup@4.62.4)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) + magic-string: 0.30.19 + optionalDependencies: + rollup: 4.62.4 + + '@rollup/plugin-terser@0.4.4(rollup@3.29.5)': dependencies: serialize-javascript: 6.0.2 - smob: 1.6.2 - terser: 5.48.0 + smob: 1.5.0 + terser: 5.44.0 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-terser@1.0.0(rollup@3.29.5)': + dependencies: + serialize-javascript: 7.1.0 + smob: 1.5.0 + terser: 5.44.0 optionalDependencies: - rollup: 4.62.0 + rollup: 3.29.5 - '@rollup/plugin-terser@1.0.0(rollup@4.62.0)': + '@rollup/plugin-terser@1.0.0(rollup@4.62.4)': dependencies: - serialize-javascript: 7.0.5 - smob: 1.6.2 - terser: 5.48.0 + serialize-javascript: 7.1.0 + smob: 1.5.0 + terser: 5.44.0 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/plugin-typescript@12.3.0(rollup@4.62.0)(tslib@2.8.1)(typescript@5.9.3)': + '@rollup/plugin-typescript@12.1.4(rollup@4.62.4)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) resolve: 1.22.12 typescript: 5.9.3 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 tslib: 2.8.1 - '@rollup/plugin-url@8.0.2(rollup@4.62.0)': + '@rollup/plugin-url@8.0.2(rollup@4.62.4)': dependencies: - '@rollup/pluginutils': 5.4.0(rollup@4.62.0) + '@rollup/pluginutils': 5.3.0(rollup@4.62.4) make-dir: 3.1.0 mime: 3.0.0 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/pluginutils@5.4.0(rollup@4.62.0)': + '@rollup/pluginutils@5.3.0(rollup@3.29.5)': dependencies: - '@types/estree': 1.0.9 + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@5.3.0(rollup@4.62.4)': + dependencies: + '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.62.0 + rollup: 4.62.4 - '@rollup/rollup-android-arm-eabi@4.62.0': + '@rollup/rollup-android-arm-eabi@4.62.4': optional: true - '@rollup/rollup-android-arm64@4.62.0': + '@rollup/rollup-android-arm64@4.62.4': optional: true - '@rollup/rollup-darwin-arm64@4.62.0': + '@rollup/rollup-darwin-arm64@4.62.4': optional: true - '@rollup/rollup-darwin-x64@4.62.0': + '@rollup/rollup-darwin-x64@4.62.4': optional: true - '@rollup/rollup-freebsd-arm64@4.62.0': + '@rollup/rollup-freebsd-arm64@4.62.4': optional: true - '@rollup/rollup-freebsd-x64@4.62.0': + '@rollup/rollup-freebsd-x64@4.62.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.0': + '@rollup/rollup-linux-arm64-gnu@4.62.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.0': + '@rollup/rollup-linux-arm64-musl@4.62.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.0': + '@rollup/rollup-linux-loong64-gnu@4.62.4': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.0': + '@rollup/rollup-linux-loong64-musl@4.62.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.4': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.0': + '@rollup/rollup-linux-ppc64-musl@4.62.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.0': + '@rollup/rollup-linux-riscv64-musl@4.62.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.0': + '@rollup/rollup-linux-s390x-gnu@4.62.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.0': + '@rollup/rollup-linux-x64-gnu@4.62.4': optional: true - '@rollup/rollup-linux-x64-musl@4.62.0': + '@rollup/rollup-linux-x64-musl@4.62.4': optional: true - '@rollup/rollup-openbsd-x64@4.62.0': + '@rollup/rollup-openbsd-x64@4.62.4': optional: true - '@rollup/rollup-openharmony-arm64@4.62.0': + '@rollup/rollup-openharmony-arm64@4.62.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.0': + '@rollup/rollup-win32-arm64-msvc@4.62.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.0': + '@rollup/rollup-win32-ia32-msvc@4.62.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.0': + '@rollup/rollup-win32-x64-gnu@4.62.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.0': + '@rollup/rollup-win32-x64-msvc@4.62.4': optional: true '@rtsao/scc@1.1.0': {} @@ -12784,7 +13776,7 @@ snapshots: '@simple-libs/stream-utils@2.0.0': {} - '@sinclair/typebox@0.34.49': {} + '@sinclair/typebox@0.34.41': {} '@sinonjs/commons@3.0.1': dependencies: @@ -12794,77 +13786,69 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@swc/core-darwin-arm64@1.15.41': - optional: true - - '@swc/core-darwin-x64@1.15.41': - optional: true - - '@swc/core-linux-arm-gnueabihf@1.15.41': + '@swc/core-darwin-arm64@1.13.5': optional: true - '@swc/core-linux-arm64-gnu@1.15.41': + '@swc/core-darwin-x64@1.13.5': optional: true - '@swc/core-linux-arm64-musl@1.15.41': + '@swc/core-linux-arm-gnueabihf@1.13.5': optional: true - '@swc/core-linux-ppc64-gnu@1.15.41': + '@swc/core-linux-arm64-gnu@1.13.5': optional: true - '@swc/core-linux-s390x-gnu@1.15.41': + '@swc/core-linux-arm64-musl@1.13.5': optional: true - '@swc/core-linux-x64-gnu@1.15.41': + '@swc/core-linux-x64-gnu@1.13.5': optional: true - '@swc/core-linux-x64-musl@1.15.41': + '@swc/core-linux-x64-musl@1.13.5': optional: true - '@swc/core-win32-arm64-msvc@1.15.41': + '@swc/core-win32-arm64-msvc@1.13.5': optional: true - '@swc/core-win32-ia32-msvc@1.15.41': + '@swc/core-win32-ia32-msvc@1.13.5': optional: true - '@swc/core-win32-x64-msvc@1.15.41': + '@swc/core-win32-x64-msvc@1.13.5': optional: true - '@swc/core@1.15.41': + '@swc/core@1.13.5': dependencies: '@swc/counter': 0.1.3 - '@swc/types': 0.1.27 + '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.41 - '@swc/core-darwin-x64': 1.15.41 - '@swc/core-linux-arm-gnueabihf': 1.15.41 - '@swc/core-linux-arm64-gnu': 1.15.41 - '@swc/core-linux-arm64-musl': 1.15.41 - '@swc/core-linux-ppc64-gnu': 1.15.41 - '@swc/core-linux-s390x-gnu': 1.15.41 - '@swc/core-linux-x64-gnu': 1.15.41 - '@swc/core-linux-x64-musl': 1.15.41 - '@swc/core-win32-arm64-msvc': 1.15.41 - '@swc/core-win32-ia32-msvc': 1.15.41 - '@swc/core-win32-x64-msvc': 1.15.41 + '@swc/core-darwin-arm64': 1.13.5 + '@swc/core-darwin-x64': 1.13.5 + '@swc/core-linux-arm-gnueabihf': 1.13.5 + '@swc/core-linux-arm64-gnu': 1.13.5 + '@swc/core-linux-arm64-musl': 1.13.5 + '@swc/core-linux-x64-gnu': 1.13.5 + '@swc/core-linux-x64-musl': 1.13.5 + '@swc/core-win32-arm64-msvc': 1.13.5 + '@swc/core-win32-ia32-msvc': 1.13.5 + '@swc/core-win32-x64-msvc': 1.13.5 '@swc/counter@0.1.3': {} - '@swc/jest@0.2.39(@swc/core@1.15.41)': + '@swc/jest@0.2.39(@swc/core@1.13.5)': dependencies: - '@jest/create-cache-key-function': 30.4.1 - '@swc/core': 1.15.41 + '@jest/create-cache-key-function': 30.2.0 + '@swc/core': 1.13.5 '@swc/counter': 0.1.3 jsonc-parser: 3.3.1 - '@swc/types@0.1.27': + '@swc/types@0.1.25': dependencies: '@swc/counter': 0.1.3 '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.29.7 - '@babel/runtime': 7.29.7 + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.28.4 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -12874,28 +13858,30 @@ snapshots: '@testing-library/jest-dom@6.9.1': dependencies: - '@adobe/css-tools': 4.5.0 + '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 '@testing-library/dom': 10.4.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.2 + '@types/react-dom': 19.2.4(@types/react@19.2.2) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: '@testing-library/dom': 10.4.1 - '@tsconfig/node10@1.0.12': {} + '@trysound/sax@0.2.0': {} + + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -12903,57 +13889,38 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@turbo/darwin-64@2.9.18': - optional: true - - '@turbo/darwin-arm64@2.9.18': - optional: true - - '@turbo/linux-64@2.9.18': - optional: true - - '@turbo/linux-arm64@2.9.18': - optional: true - - '@turbo/windows-64@2.9.18': - optional: true - - '@turbo/windows-arm64@2.9.18': - optional: true - - '@turf/area@7.3.5': + '@turf/area@7.2.0': dependencies: - '@turf/helpers': 7.3.5 - '@turf/meta': 7.3.5 + '@turf/helpers': 7.2.0 + '@turf/meta': 7.2.0 '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/bbox@7.3.5': + '@turf/bbox@7.2.0': dependencies: - '@turf/helpers': 7.3.5 - '@turf/meta': 7.3.5 + '@turf/helpers': 7.2.0 + '@turf/meta': 7.2.0 '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/centroid@7.3.5': + '@turf/centroid@7.2.0': dependencies: - '@turf/helpers': 7.3.5 - '@turf/meta': 7.3.5 + '@turf/helpers': 7.2.0 + '@turf/meta': 7.2.0 '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/helpers@7.3.5': + '@turf/helpers@7.2.0': dependencies: '@types/geojson': 7946.0.16 tslib: 2.8.1 - '@turf/meta@7.3.5': + '@turf/meta@7.2.0': dependencies: - '@turf/helpers': 7.3.5 + '@turf/helpers': 7.2.0 '@types/geojson': 7946.0.16 - tslib: 2.8.1 - '@tybys/wasm-util@0.10.2': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true @@ -12962,24 +13929,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.28.4 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.28.4 '@types/big.js@6.2.2': {} @@ -12989,6 +13956,14 @@ snapshots: '@types/deep-equal@1.0.4': {} + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.9 + '@types/json-schema': 7.0.15 + optional: true + + '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/fs-extra@8.1.5': @@ -13006,7 +13981,7 @@ snapshots: '@types/minimatch': 3.0.5 '@types/node': 24.12.4 - '@types/google.maps@3.65.1': {} + '@types/google.maps@3.58.1': {} '@types/istanbul-lib-coverage@2.0.6': {} @@ -13035,7 +14010,7 @@ snapshots: '@types/json5@0.0.29': {} - '@types/katex@0.16.8': {} + '@types/katex@0.16.7': {} '@types/leaflet@1.9.21': dependencies: @@ -13065,7 +14040,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: '@types/node': 24.12.4 - form-data: 4.0.6 + form-data: 4.0.4 '@types/node@24.12.4': dependencies: @@ -13075,57 +14050,57 @@ snapshots: '@types/plotly.js-dist-min@2.3.4': dependencies: - '@types/plotly.js': 3.0.10 + '@types/plotly.js': 3.0.7 - '@types/plotly.js@3.0.10': {} + '@types/plotly.js@3.0.7': {} '@types/prop-types@15.7.15': {} '@types/rc-slider@8.6.6': dependencies: '@types/rc-tooltip': 3.7.14 - '@types/react': 19.2.17 + '@types/react': 19.2.2 '@types/rc-tooltip@3.7.14': dependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 '@types/react-big-calendar@1.16.3': dependencies: '@types/date-arithmetic': 4.1.4 '@types/prop-types': 15.7.15 - '@types/react': 19.2.17 + '@types/react': 19.2.2 - '@types/react-color@2.17.12(@types/react@19.2.17)': + '@types/react-color@2.17.12(@types/react@19.2.2)': dependencies: - '@types/react': 19.2.17 - '@types/reactcss': 1.2.13(@types/react@19.2.17) + '@types/react': 19.2.2 + '@types/reactcss': 1.2.13(@types/react@19.2.2) '@types/react-datepicker@6.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/react': 19.2.17 + '@types/react': 19.2.2 date-fns: 3.6.0 transitivePeerDependencies: - react - react-dom - '@types/react-dom@19.2.3(@types/react@19.2.17)': + '@types/react-dom@19.2.4(@types/react@19.2.2)': dependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 - '@types/react-plotly.js@2.6.4': + '@types/react-plotly.js@2.6.3': dependencies: - '@types/plotly.js': 3.0.10 - '@types/react': 19.2.17 + '@types/plotly.js': 3.0.7 + '@types/react': 19.2.2 - '@types/react@19.2.17': + '@types/react@19.2.2': dependencies: - csstype: 3.2.3 + csstype: 3.1.3 - '@types/reactcss@1.2.13(@types/react@19.2.17)': + '@types/reactcss@1.2.13(@types/react@19.2.2)': dependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 '@types/resolve@1.20.2': {} @@ -13150,25 +14125,25 @@ snapshots: '@types/trusted-types@2.0.7': optional: true - '@types/warning@3.0.4': {} + '@types/warning@3.0.3': {} '@types/whatwg-mimetype@3.0.2': {} '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.35': + '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.1 - eslint: 9.39.4(jiti@2.6.1) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.66.0 + '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.66.0 + eslint: 9.39.3(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -13176,192 +14151,165 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 - eslint: 9.39.4(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/scope-manager': 8.66.0 + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/project-service@8.46.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3) + '@typescript-eslint/types': 8.46.1 debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.66.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) + '@typescript-eslint/types': 8.66.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': + '@typescript-eslint/scope-manager@8.46.1': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - debug: 4.4.3 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/visitor-keys': 8.46.1 - '@typescript-eslint/scope-manager@8.61.1': + '@typescript-eslint/scope-manager@8.66.0': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/visitor-keys': 8.66.0 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.46.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.66.0(typescript@5.9.3)': dependencies: - typescript: 6.0.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/types@8.46.1': {} + + '@typescript-eslint/types@8.66.0': {} + + '@typescript-eslint/typescript-estree@8.46.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/project-service': 8.46.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.1(typescript@5.9.3) + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/visitor-keys': 8.46.1 debug: 4.4.3 - eslint: 9.39.4(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.61.1': {} - - '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.66.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.66.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 + minimatch: 10.2.6 + semver: 7.7.3 + tinyglobby: 0.2.15 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': + '@typescript-eslint/utils@8.46.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.4 - tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.3(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.46.1 + '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/typescript-estree': 8.46.1(typescript@5.9.3) + eslint: 9.39.3(jiti@2.6.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) - eslint: 9.39.4(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.3(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.66.0 + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) + eslint: 9.39.3(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/visitor-keys@8.46.1': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.46.1 + eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.61.1': + '@typescript-eslint/visitor-keys@8.66.0': dependencies: - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/types': 8.66.0 eslint-visitor-keys: 5.0.1 - '@uiw/codemirror-extensions-basic-setup@4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/codemirror-extensions-basic-setup@4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)': dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.0 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.9.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 - '@uiw/codemirror-theme-github@4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/codemirror-theme-github@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)': dependencies: - '@uiw/codemirror-themes': 4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) + '@uiw/codemirror-themes': 4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6) transitivePeerDependencies: - '@codemirror/language' - '@codemirror/state' - '@codemirror/view' - '@uiw/codemirror-themes@4.25.10(@codemirror/language@6.12.3)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1)': + '@uiw/codemirror-themes@4.25.2(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)': dependencies: - '@codemirror/language': 6.12.3 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 - '@uiw/react-codemirror@4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.1)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@uiw/react-codemirror@4.25.2(@babel/runtime@7.28.4)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.6)(codemirror@6.0.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.29.7 - '@codemirror/commands': 6.10.3 - '@codemirror/state': 6.6.0 + '@babel/runtime': 7.28.4 + '@codemirror/commands': 6.9.0 + '@codemirror/state': 6.5.2 '@codemirror/theme-one-dark': 6.1.3 - '@codemirror/view': 6.43.1 - '@uiw/codemirror-extensions-basic-setup': 4.25.10(@codemirror/autocomplete@6.20.3)(@codemirror/commands@6.10.3)(@codemirror/language@6.12.3)(@codemirror/lint@6.9.7)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.1) + '@codemirror/view': 6.38.6 + '@uiw/codemirror-extensions-basic-setup': 4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6) codemirror: 6.0.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -13371,7 +14319,7 @@ snapshots: - '@codemirror/lint' - '@codemirror/search' - '@ungap/structured-clone@1.3.1': {} + '@ungap/structured-clone@1.3.3': {} '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -13431,7 +14379,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': @@ -13445,7 +14393,7 @@ snapshots: '@vis.gl/react-google-maps@0.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@types/google.maps': 3.65.1 + '@types/google.maps': 3.58.1 fast-deep-equal: 3.1.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -13472,32 +14420,32 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-jsx@5.3.2(acorn@8.17.0): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.17.0 + acorn: 8.15.0 - acorn-walk@8.3.5: + acorn-walk@8.3.4: dependencies: - acorn: 8.17.0 + acorn: 8.15.0 acorn@7.4.1: {} - acorn@8.17.0: {} + acorn@8.15.0: {} agent-base@7.1.4: {} - ajv-formats@3.0.1(ajv@8.20.0): + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: - ajv: 8.20.0 + ajv: 8.17.1 - ajv@6.15.0: + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.20.0: + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.4 @@ -13525,7 +14473,7 @@ snapshots: anymatch@3.1.3: dependencies: normalize-path: 3.0.0 - picomatch: 2.3.2 + picomatch: 2.3.1 arg@4.1.3: {} @@ -13556,11 +14504,11 @@ snapshots: array-includes@3.1.9: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 @@ -13571,6 +14519,8 @@ snapshots: array-range@1.0.1: {} + array-rearrange@2.2.2: {} + array-union@1.0.2: dependencies: array-uniq: 1.0.3 @@ -13581,51 +14531,51 @@ snapshots: array.prototype.findlast@1.2.5: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 array.prototype.findlastindex@1.2.6: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -13648,15 +14598,15 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.4: {} + axe-core@4.12.1: {} - babel-jest@30.3.0(@babel/core@7.29.7): + babel-jest@30.4.1(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 - '@jest/transform': 30.3.0 + '@jest/transform': 30.4.1 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.3.0(@babel/core@7.29.7) + babel-preset-jest: 30.4.0(@babel/core@7.29.7) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -13665,18 +14615,27 @@ snapshots: babel-plugin-istanbul@7.0.1: dependencies: - '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.6 + '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 6.0.3 test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@30.3.0: + babel-plugin-jest-hoist@30.4.0: dependencies: '@types/babel__core': 7.20.5 + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.29.7): + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.29.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): dependencies: '@babel/compat-data': 7.29.7 @@ -13689,8 +14648,8 @@ snapshots: babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) - core-js-compat: 3.49.0 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.29.7) + core-js-compat: 3.46.0 transitivePeerDependencies: - supports-color @@ -13698,7 +14657,14 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) - core-js-compat: 3.49.0 + core-js-compat: 3.50.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.29.7): + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.29.7) transitivePeerDependencies: - supports-color @@ -13715,7 +14681,7 @@ snapshots: babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.7): dependencies: - '@babel/plugin-syntax-flow': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.29.7) transitivePeerDependencies: - '@babel/core' @@ -13726,7 +14692,7 @@ snapshots: '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7) '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7) '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7) - '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.29.7) '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7) '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7) '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7) @@ -13738,10 +14704,10 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7) - babel-preset-jest@30.3.0(@babel/core@7.29.7): + babel-preset-jest@30.4.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 - babel-plugin-jest-hoist: 30.3.0 + babel-plugin-jest-hoist: 30.4.0 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) balanced-match@1.0.2: {} @@ -13752,7 +14718,9 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.37: {} + baseline-browser-mapping@2.11.13: {} + + baseline-browser-mapping@2.8.16: {} big.js@6.2.2: {} @@ -13781,9 +14749,9 @@ snapshots: content-type: 2.0.0 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 on-finished: 2.4.1 - qs: 6.15.2 + qs: 6.15.3 raw-body: 3.0.2 type-is: 2.1.0 transitivePeerDependencies: @@ -13791,16 +14759,16 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.16: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.3: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.8: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -13808,20 +14776,28 @@ snapshots: dependencies: fill-range: 7.1.1 - brandi-react@5.1.0(brandi@5.1.0)(react@18.3.1): + brandi-react@5.0.0(brandi@5.0.0)(react@18.3.1): dependencies: - brandi: 5.1.0 + brandi: 5.0.0 react: 18.3.1 - brandi@5.1.0: {} + brandi@5.0.0: {} + + browserslist@4.26.3: + dependencies: + baseline-browser-mapping: 2.8.16 + caniuse-lite: 1.0.30001750 + electron-to-chromium: 1.5.237 + node-releases: 2.0.23 + update-browserslist-db: 1.1.3(browserslist@4.26.3) - browserslist@4.28.2: + browserslist@4.28.8: dependencies: - baseline-browser-mapping: 2.10.37 - caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.373 - node-releases: 2.0.47 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + baseline-browser-mapping: 2.11.13 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.403 + node-releases: 2.0.53 + update-browserslist-db: 1.3.0(browserslist@4.28.8) bs-logger@0.2.6: dependencies: @@ -13845,7 +14821,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.9: + call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 @@ -13865,14 +14841,20 @@ snapshots: caniuse-api@3.0.0: dependencies: - browserslist: 4.28.2 - caniuse-lite: 1.0.30001799 + browserslist: 4.26.3 + caniuse-lite: 1.0.30001750 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001750: {} + + caniuse-lite@1.0.30001809: {} + + canvas-fit@1.5.0: + dependencies: + element-size: 1.1.1 - canvas@3.2.3: + canvas@3.2.0: dependencies: node-addon-api: 7.1.1 prebuild-install: 7.1.3 @@ -13909,12 +14891,12 @@ snapshots: chokidar@5.0.0: dependencies: - readdirp: 5.0.0 + readdirp: 5.1.1 chownr@1.1.4: optional: true - ci-info@4.4.0: {} + ci-info@4.3.1: {} cjs-module-lexer@2.2.0: {} @@ -13922,8 +14904,6 @@ snapshots: classnames@2.5.1: {} - cldrjs@0.5.5: {} - cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -13945,7 +14925,7 @@ snapshots: cliui@9.0.1: dependencies: string-width: 7.2.0 - strip-ansi: 7.2.0 + strip-ansi: 7.1.2 wrap-ansi: 9.0.2 clone-deep@4.0.1: @@ -13962,17 +14942,17 @@ snapshots: codemirror@6.0.2: dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.10.3 - '@codemirror/language': 6.12.3 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.0 - '@codemirror/state': 6.6.0 - '@codemirror/view': 6.43.1 + '@codemirror/autocomplete': 6.19.0 + '@codemirror/commands': 6.9.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.0 + '@codemirror/search': 6.5.11 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.6 - collect-v8-coverage@1.0.3: {} + collect-v8-coverage@1.0.2: {} - color-alpha@1.1.3: + color-alpha@1.0.4: dependencies: color-parse: 1.4.3 @@ -13986,8 +14966,6 @@ snapshots: color-name@1.1.4: {} - color-name@2.1.0: {} - color-normalize@1.5.0: dependencies: clamp: 1.0.1 @@ -13998,9 +14976,9 @@ snapshots: dependencies: color-name: 1.1.4 - color-parse@2.0.2: + color-parse@2.0.0: dependencies: - color-name: 2.1.0 + color-name: 1.1.4 color-rgba@2.4.0: dependencies: @@ -14009,7 +14987,7 @@ snapshots: color-rgba@3.0.0: dependencies: - color-parse: 2.0.2 + color-parse: 2.0.0 color-space: 2.3.2 color-space@2.3.2: {} @@ -14057,7 +15035,7 @@ snapshots: dependencies: chalk: 4.1.2 date-fns: 2.30.0 - lodash: 4.18.1 + lodash: 4.17.23 rxjs: 6.6.7 spawn-command: 0.0.2 supports-color: 8.1.1 @@ -14083,7 +15061,7 @@ snapshots: dependencies: '@conventional-changelog/template': 1.2.1 - conventional-commits-parser@7.1.0: + conventional-commits-parser@7.1.2: dependencies: '@simple-libs/stream-utils': 2.0.0 argue-cli: 3.1.0 @@ -14101,11 +15079,15 @@ snapshots: glob: 10.5.0 glob-parent: 6.0.2 - core-js-compat@3.49.0: + core-js-compat@3.46.0: + dependencies: + browserslist: 4.26.3 + + core-js-compat@3.50.0: dependencies: - browserslist: 4.28.2 + browserslist: 4.28.8 - core-js@3.49.0: {} + core-js@3.46.0: {} core-util-is@1.0.3: {} @@ -14114,21 +15096,21 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@6.3.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@6.0.3))(typescript@6.0.3): + cosmiconfig-typescript-loader@6.2.0(@types/node@24.12.4)(cosmiconfig@9.0.2(typescript@5.9.3))(typescript@5.9.3): dependencies: '@types/node': 24.12.4 - cosmiconfig: 9.0.2(typescript@6.0.3) + cosmiconfig: 9.0.2(typescript@5.9.3) jiti: 2.6.1 - typescript: 6.0.3 + typescript: 5.9.3 - cosmiconfig@9.0.2(typescript@6.0.3): + cosmiconfig@9.0.2(typescript@5.9.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.3.0 + js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 6.0.3 + typescript: 5.9.3 country-regex@1.1.0: {} @@ -14156,9 +15138,13 @@ snapshots: cross-zip@4.0.1: {} - css-declaration-sorter@6.4.1(postcss@8.5.15): + css-declaration-sorter@6.4.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + css-declaration-sorter@6.4.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 css-font-size-keywords@1.0.0: {} @@ -14207,49 +15193,93 @@ snapshots: cssfontparser@1.2.1: {} - cssnano-preset-default@5.2.14(postcss@8.5.15): - dependencies: - css-declaration-sorter: 6.4.1(postcss@8.5.15) - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 - postcss-calc: 8.2.4(postcss@8.5.15) - postcss-colormin: 5.3.1(postcss@8.5.15) - postcss-convert-values: 5.1.3(postcss@8.5.15) - postcss-discard-comments: 5.1.2(postcss@8.5.15) - postcss-discard-duplicates: 5.1.0(postcss@8.5.15) - postcss-discard-empty: 5.1.1(postcss@8.5.15) - postcss-discard-overridden: 5.1.0(postcss@8.5.15) - postcss-merge-longhand: 5.1.7(postcss@8.5.15) - postcss-merge-rules: 5.1.4(postcss@8.5.15) - postcss-minify-font-values: 5.1.0(postcss@8.5.15) - postcss-minify-gradients: 5.1.1(postcss@8.5.15) - postcss-minify-params: 5.1.4(postcss@8.5.15) - postcss-minify-selectors: 5.2.1(postcss@8.5.15) - postcss-normalize-charset: 5.1.0(postcss@8.5.15) - postcss-normalize-display-values: 5.1.0(postcss@8.5.15) - postcss-normalize-positions: 5.1.1(postcss@8.5.15) - postcss-normalize-repeat-style: 5.1.1(postcss@8.5.15) - postcss-normalize-string: 5.1.0(postcss@8.5.15) - postcss-normalize-timing-functions: 5.1.0(postcss@8.5.15) - postcss-normalize-unicode: 5.1.1(postcss@8.5.15) - postcss-normalize-url: 5.1.0(postcss@8.5.15) - postcss-normalize-whitespace: 5.1.1(postcss@8.5.15) - postcss-ordered-values: 5.1.3(postcss@8.5.15) - postcss-reduce-initial: 5.1.2(postcss@8.5.15) - postcss-reduce-transforms: 5.1.0(postcss@8.5.15) - postcss-svgo: 5.1.0(postcss@8.5.15) - postcss-unique-selectors: 5.1.1(postcss@8.5.15) - - cssnano-utils@3.1.0(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - - cssnano@5.1.15(postcss@8.5.15): - dependencies: - cssnano-preset-default: 5.2.14(postcss@8.5.15) + cssnano-preset-default@5.2.14(postcss@8.5.26): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.26) + cssnano-utils: 3.1.0(postcss@8.5.26) + postcss: 8.5.26 + postcss-calc: 8.2.4(postcss@8.5.26) + postcss-colormin: 5.3.1(postcss@8.5.26) + postcss-convert-values: 5.1.3(postcss@8.5.26) + postcss-discard-comments: 5.1.2(postcss@8.5.26) + postcss-discard-duplicates: 5.1.0(postcss@8.5.26) + postcss-discard-empty: 5.1.1(postcss@8.5.26) + postcss-discard-overridden: 5.1.0(postcss@8.5.26) + postcss-merge-longhand: 5.1.7(postcss@8.5.26) + postcss-merge-rules: 5.1.4(postcss@8.5.26) + postcss-minify-font-values: 5.1.0(postcss@8.5.26) + postcss-minify-gradients: 5.1.1(postcss@8.5.26) + postcss-minify-params: 5.1.4(postcss@8.5.26) + postcss-minify-selectors: 5.2.1(postcss@8.5.26) + postcss-normalize-charset: 5.1.0(postcss@8.5.26) + postcss-normalize-display-values: 5.1.0(postcss@8.5.26) + postcss-normalize-positions: 5.1.1(postcss@8.5.26) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.26) + postcss-normalize-string: 5.1.0(postcss@8.5.26) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.26) + postcss-normalize-unicode: 5.1.1(postcss@8.5.26) + postcss-normalize-url: 5.1.0(postcss@8.5.26) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.26) + postcss-ordered-values: 5.1.3(postcss@8.5.26) + postcss-reduce-initial: 5.1.2(postcss@8.5.26) + postcss-reduce-transforms: 5.1.0(postcss@8.5.26) + postcss-svgo: 5.1.0(postcss@8.5.26) + postcss-unique-selectors: 5.1.1(postcss@8.5.26) + + cssnano-preset-default@5.2.14(postcss@8.5.6): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.6) + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 8.2.4(postcss@8.5.6) + postcss-colormin: 5.3.1(postcss@8.5.6) + postcss-convert-values: 5.1.3(postcss@8.5.6) + postcss-discard-comments: 5.1.2(postcss@8.5.6) + postcss-discard-duplicates: 5.1.0(postcss@8.5.6) + postcss-discard-empty: 5.1.1(postcss@8.5.6) + postcss-discard-overridden: 5.1.0(postcss@8.5.6) + postcss-merge-longhand: 5.1.7(postcss@8.5.6) + postcss-merge-rules: 5.1.4(postcss@8.5.6) + postcss-minify-font-values: 5.1.0(postcss@8.5.6) + postcss-minify-gradients: 5.1.1(postcss@8.5.6) + postcss-minify-params: 5.1.4(postcss@8.5.6) + postcss-minify-selectors: 5.2.1(postcss@8.5.6) + postcss-normalize-charset: 5.1.0(postcss@8.5.6) + postcss-normalize-display-values: 5.1.0(postcss@8.5.6) + postcss-normalize-positions: 5.1.1(postcss@8.5.6) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.6) + postcss-normalize-string: 5.1.0(postcss@8.5.6) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.6) + postcss-normalize-unicode: 5.1.1(postcss@8.5.6) + postcss-normalize-url: 5.1.0(postcss@8.5.6) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.6) + postcss-ordered-values: 5.1.3(postcss@8.5.6) + postcss-reduce-initial: 5.1.2(postcss@8.5.6) + postcss-reduce-transforms: 5.1.0(postcss@8.5.6) + postcss-svgo: 5.1.0(postcss@8.5.6) + postcss-unique-selectors: 5.1.1(postcss@8.5.6) + + cssnano-utils@3.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + cssnano-utils@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano@5.1.15(postcss@8.5.26): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.26) + lilconfig: 2.1.0 + postcss: 8.5.26 + yaml: 1.10.2 + + cssnano@5.1.15(postcss@8.5.6): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.6) lilconfig: 2.1.0 - postcss: 8.5.15 - yaml: 1.10.3 + postcss: 8.5.6 + yaml: 1.10.2 csso@4.2.0: dependencies: @@ -14260,7 +15290,7 @@ snapshots: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 - csstype@3.2.3: {} + csstype@3.1.3: {} cuint@0.2.2: {} @@ -14286,7 +15316,7 @@ snapshots: commander: 2.20.3 d3-array: 1.2.4 d3-geo: 1.12.1 - resolve: 1.22.12 + resolve: 1.22.10 d3-geo@1.12.1: dependencies: @@ -14348,13 +15378,13 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 date-fns@3.6.0: {} - date-fns@4.4.0: {} + date-fns@4.1.0: {} - dayjs@1.11.21: {} + dayjs@1.11.18: {} debug@2.6.9: dependencies: @@ -14375,12 +15405,12 @@ snapshots: mimic-response: 3.1.0 optional: true - dedent@1.7.2: {} + dedent@1.7.0: {} deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.2 - call-bind: 1.0.9 + call-bind: 1.0.8 es-get-iterator: 1.1.3 get-intrinsic: 1.3.0 is-arguments: 1.2.0 @@ -14393,10 +15423,10 @@ snapshots: object-keys: 1.1.1 object.assign: 4.1.7 regexp.prototype.flags: 1.5.4 - side-channel: 1.1.1 + side-channel: 1.1.0 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.22 + which-typed-array: 1.1.19 deep-extend@0.6.0: optional: true @@ -14433,6 +15463,9 @@ snapshots: detect-kerning@2.1.2: {} + detect-libc@1.0.3: + optional: true + detect-libc@2.1.2: optional: true @@ -14440,7 +15473,7 @@ snapshots: detect-newline@4.0.1: {} - diff@4.0.4: {} + diff@4.0.2: {} dir-glob@3.0.1: dependencies: @@ -14460,13 +15493,8 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.29.7 - csstype: 3.2.3 - - dom-helpers@6.0.1: - dependencies: - '@babel/runtime': 7.29.7 - csstype: 3.2.3 + '@babel/runtime': 7.28.4 + csstype: 3.1.3 dom-serializer@1.4.1: dependencies: @@ -14484,7 +15512,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.4.12: + dompurify@3.4.13: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -14498,20 +15526,20 @@ snapshots: downshift@7.6.2(react@18.3.1): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 compute-scroll-into-view: 2.0.4 prop-types: 15.8.1 react: 18.3.1 react-is: 17.0.2 tslib: 2.8.1 - downshift@9.3.6(react@18.3.1): + downshift@9.0.10(react@18.3.1): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 compute-scroll-into-view: 3.1.1 prop-types: 15.8.1 react: 18.3.1 - react-is: 18.3.1 + react-is: 18.2.0 tslib: 2.8.1 draw-svg-path@1.0.0: @@ -14546,12 +15574,16 @@ snapshots: dependencies: '@one-ini/wasm': 0.2.1 commander: 14.0.3 - minimatch: 10.2.5 - semver: 7.8.4 + minimatch: 10.2.6 + semver: 7.8.5 ee-first@1.1.1: {} - electron-to-chromium@1.5.373: {} + electron-to-chromium@1.5.237: {} + + electron-to-chromium@1.5.403: {} + + element-size@1.1.1: {} elementary-circuits-directed-graph@1.3.1: dependencies: @@ -14592,29 +15624,22 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract-get@1.0.0: - dependencies: - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - is-callable: 1.2.7 - object-inspect: 1.13.4 - - es-abstract@1.24.2: + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.1 - function.prototype.name: 1.2.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 @@ -14623,7 +15648,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.4 + hasown: 2.0.2 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -14641,20 +15666,20 @@ snapshots: object.assign: 4.1.7 own-keys: 1.0.1 regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.4 + safe-array-concat: 1.1.3 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.11 - string.prototype.trimend: 1.0.10 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.8 + typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.22 + which-typed-array: 1.1.19 es-define-property@1.0.1: {} @@ -14662,7 +15687,7 @@ snapshots: es-get-iterator@1.1.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 get-intrinsic: 1.3.0 has-symbols: 1.1.0 is-arguments: 1.2.0 @@ -14672,12 +15697,12 @@ snapshots: isarray: 2.0.5 stop-iteration-iterator: 1.1.0 - es-iterator-helpers@1.3.3: + es-iterator-helpers@1.2.1: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -14689,9 +15714,9 @@ snapshots: has-symbols: 1.1.0 internal-slot: 1.1.0 iterator.prototype: 1.1.5 - math-intrinsics: 1.1.0 + safe-array-concat: 1.1.3 - es-object-atoms@1.1.2: + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -14700,21 +15725,19 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.2 es-shim-unscopables@1.1.0: dependencies: - hasown: 2.0.4 + hasown: 2.0.2 - es-to-primitive@1.3.1: + es-to-primitive@1.3.0: dependencies: - es-abstract-get: 1.0.0 - es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.49.0: {} + es-toolkit@1.50.0: {} es5-ext@0.10.64: dependencies: @@ -14757,40 +15780,40 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)): + eslint-config-prettier@9.1.2(eslint@9.39.3(jiti@2.6.1)): dependencies: - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) - eslint-fix-utils@0.4.2(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1)): + eslint-fix-utils@0.4.3(@types/estree@1.0.9)(eslint@9.39.3(jiti@2.6.1)): dependencies: - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) optionalDependencies: '@types/estree': 1.0.9 eslint-import-resolver-node@0.3.10: dependencies: debug: 3.2.7 - is-core-module: 2.16.2 + is-core-module: 2.16.1 resolve: 2.0.0-next.7 transitivePeerDependencies: - supports-color - eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.3(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.3(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 transitivePeerDependencies: - supports-color - eslint-plugin-cypress@5.4.0(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-cypress@5.2.0(eslint@9.39.3(jiti@2.6.1)): dependencies: - eslint: 9.39.4(jiti@2.6.1) - globals: 17.6.0 + eslint: 9.39.3(jiti@2.6.1) + globals: 16.4.0 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -14799,102 +15822,103 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.4(jiti@2.6.1) + eslint: 9.39.3(jiti@2.6.1) eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.4(jiti@2.6.1)) - hasown: 2.0.4 - is-core-module: 2.16.2 + eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint@9.39.3(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 is-glob: 4.0.3 - minimatch: 3.1.5 + minimatch: 3.1.4 object.fromentries: 2.0.8 object.groupby: 1.0.3 object.values: 1.2.1 semver: 6.3.1 - string.prototype.trimend: 1.0.10 + string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@6.0.3): + eslint-plugin-jest@29.16.0(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(jest@30.3.0(@types/node@24.12.4))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) + '@typescript-eslint/utils': 8.46.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.3(jiti@2.6.1) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) jest: 30.3.0(@types/node@24.12.4) - typescript: 6.0.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-package-json@0.89.4(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1))(jsonc-eslint-parser@3.1.0): + eslint-plugin-package-json@0.89.4(@types/estree@1.0.9)(eslint@9.39.3(jiti@2.6.1))(jsonc-eslint-parser@2.4.1): dependencies: - '@altano/repository-tools': 2.0.3 + '@altano/repository-tools': 2.0.1 change-case: 5.4.4 detect-indent: 7.0.2 detect-newline: 4.0.1 - eslint: 9.39.4(jiti@2.6.1) - eslint-fix-utils: 0.4.2(@types/estree@1.0.9)(eslint@9.39.4(jiti@2.6.1)) - jsonc-eslint-parser: 3.1.0 - package-json-validator: 1.5.2 - semver: 7.8.4 + eslint: 9.39.3(jiti@2.6.1) + eslint-fix-utils: 0.4.3(@types/estree@1.0.9)(eslint@9.39.3(jiti@2.6.1)) + jsonc-eslint-parser: 2.4.1 + package-json-validator: 1.6.0 + semver: 7.7.3 sort-object-keys: 2.1.0 - sort-package-json: 3.7.1 + sort-package-json: 3.4.0 validate-npm-package-name: 7.0.2 transitivePeerDependencies: - '@types/estree' - eslint-plugin-playwright@2.10.4(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-playwright@2.11.0(eslint@9.39.3(jiti@2.6.1)): dependencies: - eslint: 9.39.4(jiti@2.6.1) - globals: 17.6.0 + eslint: 9.39.3(jiti@2.6.1) + globals: 17.9.0 - eslint-plugin-prettier@5.5.6(eslint-config-prettier@9.1.2(eslint@9.39.4(jiti@2.6.1)))(eslint@9.39.4(jiti@2.6.1))(prettier@3.8.4): + eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.39.3(jiti@2.6.1)))(eslint@9.39.3(jiti@2.6.1))(prettier@3.9.6): dependencies: - eslint: 9.39.4(jiti@2.6.1) - prettier: 3.8.4 + eslint: 9.39.3(jiti@2.6.1) + prettier: 3.9.6 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: - eslint-config-prettier: 9.1.2(eslint@9.39.4(jiti@2.6.1)) + '@types/eslint': 9.6.1 + eslint-config-prettier: 9.1.2(eslint@9.39.3(jiti@2.6.1)) - eslint-plugin-promise@7.3.0(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-promise@7.2.1(eslint@9.39.3(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - eslint: 9.39.4(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.3(jiti@2.6.1)) + eslint: 9.39.3(jiti@2.6.1) - eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.3(jiti@2.6.1)): dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - eslint: 9.39.4(jiti@2.6.1) + '@babel/parser': 7.28.4 + eslint: 9.39.3(jiti@2.6.1) hermes-parser: 0.25.1 zod: 3.25.76 zod-validation-error: 4.0.2(zod@3.25.76) transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-react@7.37.5(eslint@9.39.3(jiti@2.6.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.3.3 - eslint: 9.39.4(jiti@2.6.1) + es-iterator-helpers: 1.2.1 + eslint: 9.39.3(jiti@2.6.1) estraverse: 5.3.0 - hasown: 2.0.4 + hasown: 2.0.2 jsx-ast-utils: 3.3.5 - minimatch: 3.1.5 + minimatch: 3.1.4 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 prop-types: 15.8.1 - resolve: 2.0.0-next.7 + resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 @@ -14917,21 +15941,21 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.4(jiti@2.6.1): + eslint@9.39.3(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.3(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 - '@eslint/js': 9.39.4 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.3 '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.8 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.9 - ajv: 6.15.0 + '@types/estree': 1.0.8 + ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -14939,7 +15963,7 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.7.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -14950,7 +15974,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.5 + minimatch: 3.1.4 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -14967,13 +15991,19 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + esprima@4.0.1: {} - esquery@1.7.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -15000,7 +16030,7 @@ snapshots: eventemitter3@4.0.7: {} - eventemitter3@5.0.4: {} + eventemitter3@5.0.1: {} events@3.3.0: {} @@ -15055,10 +16085,13 @@ snapshots: jest-mock: 30.4.1 jest-util: 30.4.1 - express-rate-limit@8.5.2(express@5.2.1): + express-rate-limit@8.6.2(express@5.2.1): dependencies: + debug: 4.4.3 express: 5.2.1 - ip-address: 10.4.0 + ip-address: 10.5.0 + transitivePeerDependencies: + - supports-color express@5.2.1: dependencies: @@ -15075,19 +16108,19 @@ snapshots: etag: 1.8.1 finalhandler: 2.1.1 fresh: 2.0.0 - http-errors: 2.0.1 + http-errors: 2.0.0 merge-descriptors: 2.0.0 mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.2 + qs: 6.15.3 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 serve-static: 2.2.1 - statuses: 2.0.2 + statuses: 2.0.1 type-is: 2.1.0 vary: 1.1.2 transitivePeerDependencies: @@ -15124,11 +16157,11 @@ snapshots: fast-uri@3.1.4: {} - fast-xml-parser@4.5.6: + fast-xml-parser@4.5.3: dependencies: strnum: 1.1.2 - fastq@1.20.1: + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -15136,11 +16169,13 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.5.0: {} + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.5 fetch-blob@3.2.0: dependencies: @@ -15166,7 +16201,7 @@ snapshots: escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 - statuses: 2.0.2 + statuses: 2.0.1 transitivePeerDependencies: - supports-color @@ -15194,16 +16229,20 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.2 + flatted: 3.3.3 keyv: 4.5.4 - flatted@3.4.2: {} + flatted@3.3.3: {} flatten-vertex-data@1.0.2: dependencies: dtype: 2.0.0 - flow-parser@0.318.0: {} + flow-estree@0.326.0: {} + + flow-parser@0.326.0: + dependencies: + flow-estree: 0.326.0 font-atlas@2.1.0: dependencies: @@ -15222,12 +16261,12 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.6: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.4 + hasown: 2.0.2 mime-types: 2.1.35(patch_hash=f54449b9273bc9e74fb67a14fcd001639d788d038b7eb0b5f43c10dff2b1adfb) formdata-polyfill@4.0.10: @@ -15246,10 +16285,10 @@ snapshots: fs-constants@1.0.0: optional: true - fs-extra@11.3.5: + fs-extra@11.4.0: dependencies: graceful-fs: 4.2.11 - jsonfile: 6.2.1 + jsonfile: 6.2.0 universalify: 2.0.1 fs-extra@8.1.0: @@ -15268,17 +16307,14 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.2.0: + function.prototype.name@1.1.8: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 - es-define-property: 1.0.1 - es-errors: 1.3.0 + define-properties: 1.2.1 functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.2 is-callable: 1.2.7 - is-document.all: 1.0.0 functions-have-names@1.2.3: {} @@ -15292,25 +16328,25 @@ snapshots: geojson-vt@3.2.1: {} - geojson-vt@4.0.3: {} + geojson-vt@4.0.2: {} get-caller-file@2.0.5: {} get-canvas-context@1.0.2: {} - get-east-asian-width@1.6.0: {} + get-east-asian-width@1.4.0: {} get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.4 + hasown: 2.0.2 math-intrinsics: 1.1.0 get-package-type@0.1.0: {} @@ -15318,11 +16354,11 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-stream@4.1.0: dependencies: - pump: 3.0.4 + pump: 3.0.3 get-stream@6.0.1: {} @@ -15332,7 +16368,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - git-hooks-list@4.2.1: {} + git-hooks-list@4.1.1: {} github-from-package@0.0.0: optional: true @@ -15358,7 +16394,7 @@ snapshots: parse-unit: 1.0.1 pick-by-alias: 1.2.0 regl: 2.1.1 - to-px: 1.1.0 + to-px: 1.0.1 typedarray-pool: 1.2.0 gl-util@3.1.3: @@ -15383,23 +16419,23 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.3 + minimatch: 9.0.5 + minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 glob@11.1.0: dependencies: foreground-child: 3.3.1 - jackspeak: 4.2.3 - minimatch: 10.2.5 - minipass: 7.1.3 + jackspeak: 4.1.1 + minimatch: 10.2.6 + minipass: 7.1.2 package-json-from-dist: 1.0.1 - path-scurry: 2.0.2 + path-scurry: 2.0.0 glob@13.0.6: dependencies: - minimatch: 10.2.5 + minimatch: 10.2.6 minipass: 7.1.3 path-scurry: 2.0.2 @@ -15408,14 +16444,14 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.5 + minimatch: 3.1.4 once: 1.4.0 path-is-absolute: 1.0.1 glob@9.3.5: dependencies: fs.realpath: 1.0.0 - minimatch: 8.0.7 + minimatch: 8.0.4 minipass: 4.2.8 path-scurry: 1.11.1 @@ -15429,13 +16465,13 @@ snapshots: kind-of: 6.0.3 which: 4.0.0 - globalize@1.7.1: - dependencies: - cldrjs: 0.5.5 + globalize@0.1.1: {} globals@14.0.0: {} - globals@17.6.0: {} + globals@16.4.0: {} + + globals@17.9.0: {} globalthis@1.0.4: dependencies: @@ -15515,7 +16551,7 @@ snapshots: graceful-fs: 4.2.11 inherits: 2.0.4 map-limit: 0.0.1 - resolve: 1.22.12 + resolve: 1.22.10 glslify@7.1.1: dependencies: @@ -15529,7 +16565,7 @@ snapshots: glslify-bundle: 5.1.1 glslify-deps: 1.3.2 minimist: 1.2.8 - resolve: 1.22.12 + resolve: 1.22.10 stack-trace: 0.0.9 static-eval: 2.1.1 through2: 2.0.5 @@ -15584,6 +16620,10 @@ snapshots: dependencies: has-symbols: 1.1.0 + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -15596,11 +16636,11 @@ snapshots: highlight.js@11.11.1: {} - hono@4.12.27: {} + hono@4.13.1: {} hosted-git-info@9.0.3: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.2.2 html-encoding-sniffer@4.0.0: dependencies: @@ -15615,6 +16655,14 @@ snapshots: domutils: 2.8.0 entities: 2.2.0 + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -15649,15 +16697,19 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 icss-replace-symbols@1.1.0: {} - icss-utils@5.1.0(postcss@8.5.15): + icss-utils@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + icss-utils@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 identity-obj-proxy@3.0.0: dependencies: @@ -15671,7 +16723,7 @@ snapshots: immediate@3.0.6: {} - immutable@5.1.6: {} + immutable@5.1.9: {} import-cwd@3.0.0: dependencies: @@ -15711,8 +16763,8 @@ snapshots: internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.4 - side-channel: 1.1.1 + hasown: 2.0.2 + side-channel: 1.1.0 interpret@1.4.0: {} @@ -15720,7 +16772,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - ip-address@10.4.0: {} + ip-address@10.5.0: {} ip@2.0.1: {} @@ -15733,7 +16785,7 @@ snapshots: is-array-buffer@3.0.5: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 @@ -15764,6 +16816,10 @@ snapshots: is-callable@1.2.7: {} + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + is-core-module@2.16.2: dependencies: hasown: 2.0.4 @@ -15779,10 +16835,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-document.all@1.0.0: - dependencies: - call-bound: 1.0.4 - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -15809,6 +16861,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-iexplorer@1.0.0: {} + is-interactive@1.0.0: {} is-map@2.0.3: {} @@ -15846,14 +16900,14 @@ snapshots: is-reference@1.2.1: dependencies: - '@types/estree': 1.0.9 + '@types/estree': 1.0.8 is-regex@1.2.1: dependencies: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.4 + hasown: 2.0.2 is-set@2.0.3: {} @@ -15882,7 +16936,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.22 + which-typed-array: 1.1.19 is-unicode-supported@0.1.0: {} @@ -15905,7 +16959,7 @@ snapshots: isexe@2.0.0: {} - isexe@3.1.5: {} + isexe@3.1.1: {} isobject@3.0.1: {} @@ -15914,10 +16968,10 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 - '@istanbuljs/schema': 0.1.6 + '@babel/parser': 7.28.4 + '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.8.4 + semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -15943,7 +16997,7 @@ snapshots: iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 @@ -15955,9 +17009,9 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.2.3: + jackspeak@4.1.1: dependencies: - '@isaacs/cliui': 9.0.0 + '@isaacs/cliui': 8.0.2 jasmine-core@3.99.1: {} @@ -15986,7 +17040,7 @@ snapshots: '@types/node': 24.12.4 chalk: 4.1.2 co: 4.6.0 - dedent: 1.7.2 + dedent: 1.7.0 is-generator-fn: 2.1.0 jest-each: 30.3.0 jest-matcher-utils: 30.3.0 @@ -16023,15 +17077,15 @@ snapshots: - ts-node optional: true - jest-cli@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest-cli@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-config: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) jest-util: 30.3.0 jest-validate: 30.3.0 yargs: 17.7.2 @@ -16049,9 +17103,9 @@ snapshots: '@jest/pattern': 30.0.1 '@jest/test-sequencer': 30.3.0 '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.7) + babel-jest: 30.4.1(@babel/core@7.29.7) chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 deepmerge: 4.3.1 glob: 10.5.0 graceful-fs: 4.2.11 @@ -16074,16 +17128,16 @@ snapshots: - supports-color optional: true - jest-config@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest-config@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 '@jest/test-sequencer': 30.3.0 '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.7) + babel-jest: 30.4.1(@babel/core@7.29.7) chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 deepmerge: 4.3.1 glob: 10.5.0 graceful-fs: 4.2.11 @@ -16101,7 +17155,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 24.12.4 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -16132,13 +17186,25 @@ snapshots: jest-util: 30.3.0 pretty-format: 30.3.0 - jest-environment-jsdom@30.3.0(canvas@3.2.3): + jest-environment-jsdom@30.3.0(canvas@3.2.0): dependencies: '@jest/environment': 30.3.0 - '@jest/environment-jsdom-abstract': 30.3.0(canvas@3.2.3)(jsdom@26.1.0(canvas@3.2.3)) - jsdom: 26.1.0(canvas@3.2.3) + '@jest/environment-jsdom-abstract': 30.3.0(canvas@3.2.0)(jsdom@26.1.0(canvas@3.2.0)) + jsdom: 26.1.0(canvas@3.2.0) + optionalDependencies: + canvas: 3.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jest-environment-jsdom@30.4.1(canvas@3.2.0): + dependencies: + '@jest/environment': 30.4.1 + '@jest/environment-jsdom-abstract': 30.4.1(canvas@3.2.0)(jsdom@26.1.0(canvas@3.2.0)) + jsdom: 26.1.0(canvas@3.2.0) optionalDependencies: - canvas: 3.2.3 + canvas: 3.2.0 transitivePeerDependencies: - bufferutil - supports-color @@ -16164,7 +17230,22 @@ snapshots: jest-regex-util: 30.0.1 jest-util: 30.3.0 jest-worker: 30.3.0 - picomatch: 4.0.4 + picomatch: 4.0.3 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-haste-map@30.4.1: + dependencies: + '@jest/types': 30.4.1 + '@types/node': 24.12.4 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.4.0 + jest-util: 30.4.1 + jest-worker: 30.4.1 + picomatch: 4.0.3 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -16195,7 +17276,7 @@ snapshots: dependencies: mkdirp: 1.0.4 strip-ansi: 6.0.1 - uuid: 14.0.0 + uuid: 14.0.1 xml: 1.0.1 jest-leak-detector@30.3.0: @@ -16219,25 +17300,25 @@ snapshots: jest-message-util@30.3.0: dependencies: - '@babel/code-frame': 7.29.7 + '@babel/code-frame': 7.27.1 '@jest/types': 30.3.0 '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - picomatch: 4.0.4 + picomatch: 4.0.3 pretty-format: 30.3.0 slash: 3.0.0 stack-utils: 2.0.6 jest-message-util@30.4.1: dependencies: - '@babel/code-frame': 7.29.7 + '@babel/code-frame': 7.27.1 '@jest/types': 30.4.1 '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 jest-util: 30.4.1 - picomatch: 4.0.4 + picomatch: 4.0.3 pretty-format: 30.4.1 slash: 3.0.0 stack-utils: 2.0.6 @@ -16319,7 +17400,7 @@ snapshots: '@types/node': 24.12.4 chalk: 4.1.2 cjs-module-lexer: 2.2.0 - collect-v8-coverage: 1.0.3 + collect-v8-coverage: 1.0.2 glob: 10.5.0 graceful-fs: 4.2.11 jest-haste-map: 30.3.0 @@ -16337,10 +17418,10 @@ snapshots: jest-snapshot@30.3.0: dependencies: '@babel/core': 7.29.7 - '@babel/generator': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.7) + '@babel/types': 7.28.4 '@jest/expect-utils': 30.3.0 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.3.0 @@ -16355,8 +17436,8 @@ snapshots: jest-message-util: 30.3.0 jest-util: 30.3.0 pretty-format: 30.3.0 - semver: 7.8.4 - synckit: 0.11.13 + semver: 7.7.3 + synckit: 0.11.11 transitivePeerDependencies: - supports-color @@ -16365,18 +17446,18 @@ snapshots: '@jest/types': 30.3.0 '@types/node': 24.12.4 chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 graceful-fs: 4.2.11 - picomatch: 4.0.4 + picomatch: 4.0.3 jest-util@30.4.1: dependencies: '@jest/types': 30.4.1 '@types/node': 24.12.4 chalk: 4.1.2 - ci-info: 4.4.0 + ci-info: 4.3.1 graceful-fs: 4.2.11 - picomatch: 4.0.4 + picomatch: 4.0.3 jest-validate@30.3.0: dependencies: @@ -16401,11 +17482,19 @@ snapshots: jest-worker@30.3.0: dependencies: '@types/node': 24.12.4 - '@ungap/structured-clone': 1.3.1 + '@ungap/structured-clone': 1.3.3 jest-util: 30.3.0 merge-stream: 2.0.0 supports-color: 8.1.1 + jest-worker@30.4.1: + dependencies: + '@types/node': 24.12.4 + '@ungap/structured-clone': 1.3.3 + jest-util: 30.4.1 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jest@30.3.0(@types/node@24.12.4): dependencies: '@jest/core': 30.3.0 @@ -16420,12 +17509,12 @@ snapshots: - ts-node optional: true - jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + '@jest/core': 30.3.0(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 - jest-cli: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest-cli: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -16435,7 +17524,7 @@ snapshots: jiti@2.6.1: {} - jose@6.2.3: {} + jose@6.2.8: {} js-beautify@2.0.3: dependencies: @@ -16449,35 +17538,35 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@3.15.0: + js-yaml@3.14.2: dependencies: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.3.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 - jsbarcode@3.12.3: {} + jsbarcode@3.12.1: {} - jscodeshift@17.3.0(@babel/preset-env@7.29.7(@babel/core@7.29.7)): + jscodeshift@17.4.0(@babel/preset-env@7.29.7(@babel/core@7.29.7)): dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.28.4 '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.29.7) '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) '@babel/preset-flow': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) '@babel/register': 7.29.7(@babel/core@7.29.7) - flow-parser: 0.318.0 + flow-parser: 0.326.0 graceful-fs: 4.2.11 - micromatch: 4.0.8 neo-async: 2.6.2 picocolors: 1.1.1 - recast: 0.23.11 + picomatch: 4.0.3 + recast: 0.23.20 tmp: 0.2.7 write-file-atomic: 5.0.1 optionalDependencies: @@ -16485,7 +17574,7 @@ snapshots: transitivePeerDependencies: - supports-color - jsdom@26.1.0(canvas@3.2.3): + jsdom@26.1.0(canvas@3.2.0): dependencies: cssstyle: 4.6.0 data-urls: 5.0.0 @@ -16494,7 +17583,7 @@ snapshots: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.24 + nwsapi: 2.2.22 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -16505,10 +17594,10 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.21.0 + ws: 8.18.3 xml-name-validator: 5.0.0 optionalDependencies: - canvas: 3.2.3 + canvas: 3.2.0 transitivePeerDependencies: - bufferutil - supports-color @@ -16536,11 +17625,12 @@ snapshots: json5@2.2.3: {} - jsonc-eslint-parser@3.1.0: + jsonc-eslint-parser@2.4.1: dependencies: - acorn: 8.17.0 - eslint-visitor-keys: 5.0.1 - semver: 7.8.4 + acorn: 8.15.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.7.3 jsonc-parser@3.3.1: {} @@ -16548,7 +17638,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonfile@6.2.1: + jsonfile@6.2.0: dependencies: universalify: 2.0.1 optionalDependencies: @@ -16570,13 +17660,13 @@ snapshots: junk@1.0.3: {} - katex@0.16.47: + katex@0.16.25: dependencies: commander: 8.3.0 kdbush@3.0.0: {} - kdbush@4.1.0: {} + kdbush@4.0.2: {} keyv@4.5.4: dependencies: @@ -16605,7 +17695,7 @@ snapshots: dependencies: uc.micro: 2.1.0 - linkifyjs@4.3.3: {} + linkifyjs@4.3.2: {} livereload-js@3.4.1: {} @@ -16614,7 +17704,7 @@ snapshots: chokidar: 3.6.0 livereload-js: 3.4.1 opts: 2.0.2 - ws: 7.5.11 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -16634,7 +17724,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.18.1: {} + lodash-es@4.17.23: {} lodash.camelcase@4.3.0: {} @@ -16650,7 +17740,7 @@ snapshots: lodash.uniq@4.5.0: {} - lodash@4.18.1: {} + lodash@4.17.23: {} log-symbols@4.1.0: dependencies: @@ -16663,7 +17753,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.5.1: {} + lru-cache@11.2.2: {} lru-cache@5.1.1: dependencies: @@ -16679,7 +17769,7 @@ snapshots: dependencies: vlq: 0.2.3 - magic-string@0.30.21: + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -16696,7 +17786,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.4 + semver: 7.7.3 make-dir@5.1.0: {} @@ -16742,7 +17832,7 @@ snapshots: '@mapbox/geojson-rewind': 0.5.2 '@mapbox/jsonlint-lines-primitives': 2.0.2 '@mapbox/point-geometry': 0.1.0 - '@mapbox/tiny-sdf': 2.2.0 + '@mapbox/tiny-sdf': 2.0.7 '@mapbox/unitbezier': 0.0.1 '@mapbox/vector-tile': 1.3.1 '@mapbox/whoots-js': 3.1.0 @@ -16754,10 +17844,10 @@ snapshots: '@types/pbf': 3.0.5 '@types/supercluster': 7.1.3 earcut: 3.0.2 - geojson-vt: 4.0.3 + geojson-vt: 4.0.2 gl-matrix: 3.4.4 global-prefix: 4.0.0 - kdbush: 4.1.0 + kdbush: 4.0.2 murmurhash-js: 1.0.0 pbf: 3.3.0 potpack: 2.1.0 @@ -16775,9 +17865,9 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - match-sorter@8.3.0: + match-sorter@8.1.0: dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 remove-accents: 0.5.0 material-colors@1.2.6: {} @@ -16791,31 +17881,31 @@ snapshots: array-differ: 1.0.0 array-union: 1.0.2 arrify: 1.0.1 - minimatch: 3.1.5 + minimatch: 3.1.4 mdn-data@2.0.14: {} mdurl@2.0.0: {} - media-typer@1.1.0: {} + media-typer@1.1.1: {} memoize-one@6.0.0: {} mendix@10.24.75382: dependencies: '@types/big.js': 6.2.2 - '@types/react': 19.2.17 + '@types/react': 19.2.2 - mendix@11.10.0: + mendix@11.13.0: dependencies: '@types/big.js': 6.2.2 - '@types/react': 19.2.17 + '@types/react': 19.2.2 merge-descriptors@2.0.0: {} - merge-refs@1.3.0(@types/react@19.2.17): + merge-refs@1.3.0(@types/react@19.2.2): optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 merge-stream@2.0.0: {} @@ -16824,7 +17914,7 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.2 + picomatch: 2.3.1 mime-db@1.52.0: {} @@ -16853,26 +17943,32 @@ snapshots: mini-svg-data-uri@1.4.4: {} - minimatch@10.2.5: + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.9 + + minimatch@3.0.8: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 1.1.12 - minimatch@3.1.5: + minimatch@3.1.4: dependencies: - brace-expansion: 1.1.16 + brace-expansion: 1.1.12 - minimatch@8.0.7: + minimatch@8.0.4: dependencies: - brace-expansion: 2.1.3 + brace-expansion: 2.0.2 - minimatch@9.0.9: + minimatch@9.0.5: dependencies: - brace-expansion: 2.1.3 + brace-expansion: 2.0.2 minimist@1.2.8: {} minipass@4.2.8: {} + minipass@7.1.2: {} + minipass@7.1.3: {} mitt@3.0.1: {} @@ -16912,8 +18008,20 @@ snapshots: dependencies: color-name: 1.1.4 + mouse-change@1.4.0: + dependencies: + mouse-event: 1.0.5 + mouse-event-offset@3.0.2: {} + mouse-event@1.0.5: {} + + mouse-wheel@1.2.0: + dependencies: + right-now: 1.0.0 + signum: 1.0.0 + to-px: 1.0.1 + mri@1.2.0: {} ms@2.0.0: {} @@ -16924,7 +18032,9 @@ snapshots: nanoevents@9.1.0: {} - nanoid@3.3.12: {} + nanoid@3.3.11: {} + + nanoid@3.3.18: {} napi-build-utils@2.0.0: optional: true @@ -16939,7 +18049,7 @@ snapshots: dependencies: debug: 3.2.7 iconv-lite: 0.4.24 - sax: 1.6.0 + sax: 1.4.1 transitivePeerDependencies: - supports-color @@ -16951,9 +18061,9 @@ snapshots: nice-try@1.0.5: {} - node-abi@3.92.0: + node-abi@3.78.0: dependencies: - semver: 7.8.4 + semver: 7.7.3 optional: true node-addon-api@7.1.1: @@ -16961,7 +18071,7 @@ snapshots: node-domexception@1.0.0: {} - node-exports-info@1.6.0: + node-exports-info@1.6.2: dependencies: array.prototype.flatmap: 1.3.3 es-errors: 1.3.0 @@ -16980,7 +18090,9 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.47: {} + node-releases@2.0.23: {} + + node-releases@2.0.53: {} nopt@10.0.1: dependencies: @@ -17000,7 +18112,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.8.4 + semver: 7.7.3 validate-npm-package-name: 7.0.2 npm-run-path@2.0.2: @@ -17021,7 +18133,7 @@ snapshots: dependencies: is-finite: 1.1.0 - nwsapi@2.2.24: {} + nwsapi@2.2.22: {} object-assign@4.1.1: {} @@ -17029,46 +18141,46 @@ snapshots: object-is@1.1.6: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 object-keys@1.1.1: {} object.assign@4.1.7: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 object.entries@1.1.9: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 object.values@1.2.1: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 on-finished@2.4.1: dependencies: @@ -17150,10 +18262,10 @@ snapshots: package-json-from-dist@1.0.1: {} - package-json-validator@1.5.2: + package-json-validator@1.6.0: dependencies: npm-package-arg: 13.0.2 - semver: 7.8.4 + semver: 7.7.3 validate-npm-package-license: 3.0.4 validate-npm-package-name: 7.0.2 @@ -17171,7 +18283,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.7 + '@babel/code-frame': 7.27.1 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -17205,11 +18317,16 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.3 + minipass: 7.1.2 + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.2.2 + minipass: 7.1.2 path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.2.2 minipass: 7.1.3 path-to-regexp@8.4.2: {} @@ -17226,7 +18343,7 @@ snapshots: pdfjs-dist@4.8.69: optionalDependencies: - canvas: 3.2.3 + canvas: 3.2.0 path2d: 0.2.2 peggy@1.2.0: {} @@ -17237,9 +18354,11 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} + picomatch@2.3.1: {} + + picomatch@4.0.3: {} - picomatch@4.0.4: {} + picomatch@4.0.5: {} pify@2.3.0: {} @@ -17259,30 +18378,33 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.61.0: {} + playwright-core@1.62.1: {} playwright-ctrf-json-reporter@0.0.27: {} - playwright@1.61.0: + playwright@1.62.1: dependencies: - playwright-core: 1.61.0 + playwright-core: 1.62.1 optionalDependencies: fsevents: 2.3.2 - plotly.js-dist-min@3.6.0: {} + plotly.js-dist-min@3.1.1: {} - plotly.js@3.6.0(mapbox-gl@1.13.3): + plotly.js@3.1.1(mapbox-gl@1.13.3): dependencies: '@plotly/d3': 3.8.2 '@plotly/d3-sankey': 0.7.2 '@plotly/d3-sankey-circular': 0.33.1 '@plotly/mapbox-gl': 1.13.4(mapbox-gl@1.13.3) '@plotly/regl': 2.1.2 - '@turf/area': 7.3.5 - '@turf/bbox': 7.3.5 - '@turf/centroid': 7.3.5 + '@turf/area': 7.2.0 + '@turf/bbox': 7.2.0 + '@turf/centroid': 7.2.0 base64-arraybuffer: 1.0.2 + canvas-fit: 1.5.0 + color-alpha: 1.0.4 color-normalize: 1.5.0 + color-parse: 2.0.0 color-rgba: 3.0.0 country-regex: 1.1.0 d3-force: 1.2.1 @@ -17300,19 +18422,23 @@ snapshots: has-passive-events: 1.0.0 is-mobile: 4.0.0 maplibre-gl: 4.7.1 + mouse-change: 1.4.0 mouse-event-offset: 3.0.2 + mouse-wheel: 1.2.0 native-promise-only: 0.8.1 parse-svg-path: 0.1.2 point-in-polygon: 1.1.0 polybooljs: 1.2.2 - probe-image-size: 7.3.0 + probe-image-size: 7.2.3 regl-error2d: 2.0.12 regl-line2d: 3.1.3 - regl-scatter2d: 3.4.0 + regl-scatter2d: 3.3.1 regl-splom: 1.0.14 strongly-connected-components: 1.0.1 + superscript-text: 1.0.0 svg-path-sdf: 1.1.3 tinycolor2: 1.6.0 + to-px: 1.0.1 topojson-client: 3.1.0 webgl-context: 2.2.0 world-calendars: 1.0.4 @@ -17326,240 +18452,435 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@8.2.4(postcss@8.5.15): + postcss-calc@8.2.4(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-calc@8.2.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + + postcss-colormin@5.3.1(postcss@8.5.26): dependencies: - postcss: 8.5.15 - postcss-selector-parser: 6.1.4 + browserslist: 4.26.3 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-colormin@5.3.1(postcss@8.5.15): + postcss-colormin@5.3.1(postcss@8.5.6): dependencies: - browserslist: 4.28.2 + browserslist: 4.26.3 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.5.15 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-convert-values@5.1.3(postcss@8.5.26): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-convert-values@5.1.3(postcss@8.5.15): + postcss-convert-values@5.1.3(postcss@8.5.6): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 + browserslist: 4.26.3 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-discard-comments@5.1.2(postcss@8.5.15): + postcss-discard-comments@5.1.2(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-discard-comments@5.1.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-duplicates@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-discard-duplicates@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-empty@5.1.1(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 - postcss-discard-duplicates@5.1.0(postcss@8.5.15): + postcss-discard-empty@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 - postcss-discard-empty@5.1.1(postcss@8.5.15): + postcss-discard-overridden@5.1.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 - postcss-discard-overridden@5.1.0(postcss@8.5.15): + postcss-discard-overridden@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 - postcss-import@14.1.0(postcss@8.5.15): + postcss-import@14.1.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-import@16.1.1(postcss@8.5.15): + postcss-import@16.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.12 + resolve: 1.22.10 - postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + postcss-load-config@3.1.4(postcss@8.5.26)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: lilconfig: 2.1.0 - yaml: 1.10.3 + yaml: 1.10.2 optionalDependencies: - postcss: 8.5.15 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3) + postcss: 8.5.26 + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3) - postcss-load-config@3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + postcss-load-config@3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: lilconfig: 2.1.0 - yaml: 1.10.3 + yaml: 1.10.2 optionalDependencies: - postcss: 8.5.15 - ts-node: 10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3) + postcss: 8.5.6 + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3) + + postcss-merge-longhand@5.1.7(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1(postcss@8.5.26) - postcss-merge-longhand@5.1.7(postcss@8.5.15): + postcss-merge-longhand@5.1.7(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.5.15) + stylehacks: 5.1.1(postcss@8.5.6) + + postcss-merge-rules@5.1.4(postcss@8.5.26): + dependencies: + browserslist: 4.26.3 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0(postcss@8.5.26) + postcss: 8.5.26 + postcss-selector-parser: 6.1.2 - postcss-merge-rules@5.1.4(postcss@8.5.15): + postcss-merge-rules@5.1.4(postcss@8.5.6): dependencies: - browserslist: 4.28.2 + browserslist: 4.26.3 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 - postcss-selector-parser: 6.1.4 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - postcss-minify-font-values@5.1.0(postcss@8.5.15): + postcss-minify-font-values@5.1.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-minify-gradients@5.1.1(postcss@8.5.15): + postcss-minify-font-values@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.5.26): + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0(postcss@8.5.26) + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@5.1.1(postcss@8.5.6): dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-params@5.1.4(postcss@8.5.26): + dependencies: + browserslist: 4.26.3 + cssnano-utils: 3.1.0(postcss@8.5.26) + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-minify-params@5.1.4(postcss@8.5.15): + postcss-minify-params@5.1.4(postcss@8.5.6): dependencies: - browserslist: 4.28.2 - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + browserslist: 4.26.3 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-minify-selectors@5.2.1(postcss@8.5.15): + postcss-minify-selectors@5.2.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser: 6.1.2 + + postcss-minify-selectors@5.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 - postcss-selector-parser: 6.1.4 + postcss: 8.5.6 - postcss-modules-extract-imports@3.1.0(postcss@8.5.15): + postcss-modules-local-by-default@4.2.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + icss-utils: 5.1.0(postcss@8.5.26) + postcss: 8.5.26 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 - postcss-modules-local-by-default@4.2.0(postcss@8.5.15): + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.15): + postcss-modules-scope@3.2.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser: 7.1.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-modules-values@4.0.0(postcss@8.5.26): + dependencies: + icss-utils: 5.1.0(postcss@8.5.26) + postcss: 8.5.26 + + postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 - postcss-modules-values@4.0.0(postcss@8.5.15): + postcss-modules@4.3.1(postcss@8.5.26): dependencies: - icss-utils: 5.1.0(postcss@8.5.15) - postcss: 8.5.15 + generic-names: 4.0.0 + icss-replace-symbols: 1.1.0 + lodash.camelcase: 4.3.0 + postcss: 8.5.26 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.26) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.26) + postcss-modules-scope: 3.2.1(postcss@8.5.26) + postcss-modules-values: 4.0.0(postcss@8.5.26) + string-hash: 1.1.3 - postcss-modules@4.3.1(postcss@8.5.15): + postcss-modules@4.3.1(postcss@8.5.6): dependencies: generic-names: 4.0.0 icss-replace-symbols: 1.1.0 lodash.camelcase: 4.3.0 - postcss: 8.5.15 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) - postcss-modules-scope: 3.2.1(postcss@8.5.15) - postcss-modules-values: 4.0.0(postcss@8.5.15) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) string-hash: 1.1.3 - postcss-normalize-charset@5.1.0(postcss@8.5.15): + postcss-normalize-charset@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + + postcss-normalize-charset@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-normalize-display-values@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + postcss-normalize-display-values@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 - postcss-normalize-display-values@5.1.0(postcss@8.5.15): + postcss-normalize-string@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-positions@5.1.1(postcss@8.5.15): + postcss-normalize-timing-functions@5.1.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@5.1.1(postcss@8.5.15): + postcss-normalize-timing-functions@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-string@5.1.0(postcss@8.5.15): + postcss-normalize-unicode@5.1.1(postcss@8.5.26): dependencies: - postcss: 8.5.15 + browserslist: 4.26.3 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@5.1.0(postcss@8.5.15): + postcss-normalize-unicode@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 + browserslist: 4.26.3 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@5.1.1(postcss@8.5.15): + postcss-normalize-url@5.1.0(postcss@8.5.26): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 + normalize-url: 6.1.0 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-normalize-url@5.1.0(postcss@8.5.15): + postcss-normalize-url@5.1.0(postcss@8.5.6): dependencies: normalize-url: 6.1.0 - postcss: 8.5.15 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@5.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@5.1.1(postcss@8.5.15): + postcss-ordered-values@5.1.3(postcss@8.5.26): dependencies: - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.26) + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-ordered-values@5.1.3(postcss@8.5.15): + postcss-ordered-values@5.1.3(postcss@8.5.6): dependencies: - cssnano-utils: 3.1.0(postcss@8.5.15) - postcss: 8.5.15 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - postcss-reduce-initial@5.1.2(postcss@8.5.15): + postcss-reduce-initial@5.1.2(postcss@8.5.26): + dependencies: + browserslist: 4.26.3 + caniuse-api: 3.0.0 + postcss: 8.5.26 + + postcss-reduce-initial@5.1.2(postcss@8.5.6): dependencies: - browserslist: 4.28.2 + browserslist: 4.26.3 caniuse-api: 3.0.0 - postcss: 8.5.15 + postcss: 8.5.6 - postcss-reduce-transforms@5.1.0(postcss@8.5.15): + postcss-reduce-transforms@5.1.0(postcss@8.5.26): dependencies: - postcss: 8.5.15 + postcss: 8.5.26 postcss-value-parser: 4.2.0 - postcss-selector-parser@6.1.4: + postcss-reduce-transforms@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.4: + postcss-selector-parser@7.1.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@5.1.0(postcss@8.5.15): + postcss-svgo@5.1.0(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + + postcss-svgo@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.5.15 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - svgo: 2.8.2 + svgo: 2.8.0 + + postcss-unique-selectors@5.1.1(postcss@8.5.26): + dependencies: + postcss: 8.5.26 + postcss-selector-parser: 6.1.2 - postcss-unique-selectors@5.1.1(postcss@8.5.15): + postcss-unique-selectors@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.5.15 - postcss-selector-parser: 6.1.4 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-url@10.1.3(postcss@8.5.26): + dependencies: + make-dir: 3.1.0 + mime: 2.5.2 + minimatch: 3.0.8 + postcss: 8.5.26 + xxhashjs: 0.2.2 - postcss-url@10.1.4(postcss@8.5.15): + postcss-url@10.1.3(postcss@8.5.6): dependencies: make-dir: 3.1.0 mime: 2.5.2 - minimatch: 3.1.5 - postcss: 8.5.15 + minimatch: 3.0.8 + postcss: 8.5.6 xxhashjs: 0.2.2 postcss-value-parser@4.2.0: {} - postcss@8.5.15: + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -17575,8 +18896,8 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.92.0 - pump: 3.0.4 + node-abi: 3.78.0 + pump: 3.0.3 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.4 @@ -17589,13 +18910,14 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-packagejson@2.5.22(prettier@3.8.4): + prettier-plugin-packagejson@2.5.19(prettier@3.9.6): dependencies: - sort-package-json: 3.6.0 + sort-package-json: 3.4.0 + synckit: 0.11.11 optionalDependencies: - prettier: 3.8.4 + prettier: 3.9.6 - prettier@3.8.4: {} + prettier@3.9.6: {} pretty-format@27.5.1: dependencies: @@ -17614,20 +18936,20 @@ snapshots: '@jest/schemas': 30.4.1 ansi-styles: 5.2.0 react-is-18: react-is@18.3.1 - react-is-19: react-is@19.2.7 + react-is-19: react-is@19.2.8 - pretty-quick@4.2.2(prettier@3.8.4): + pretty-quick@4.2.2(prettier@3.9.6): dependencies: - '@pkgr/core': 0.2.10 + '@pkgr/core': 0.2.9 ignore: 7.0.5 mri: 1.2.0 picocolors: 1.1.1 - picomatch: 4.0.4 - prettier: 3.8.4 + picomatch: 4.0.3 + prettier: 3.9.6 tinyexec: 0.3.2 tslib: 2.8.1 - probe-image-size@7.3.0: + probe-image-size@7.2.3: dependencies: lodash.merge: 4.6.2 needle: 2.9.1 @@ -17653,7 +18975,7 @@ snapshots: proto-list@1.2.4: {} - protocol-buffers-schema@3.6.1: {} + protocol-buffers-schema@3.6.0: {} proxy-addr@2.0.7: dependencies: @@ -17662,7 +18984,7 @@ snapshots: prr@1.0.1: {} - pump@3.0.4: + pump@3.0.3: dependencies: end-of-stream: 1.4.5 once: 1.4.0 @@ -17673,7 +18995,7 @@ snapshots: pure-rand@7.0.1: {} - pusher-js@8.5.0: + pusher-js@8.6.0: dependencies: tweetnacl: 1.0.3 @@ -17681,8 +19003,9 @@ snapshots: dependencies: react: 18.3.1 - qs@6.15.2: + qs@6.15.3: dependencies: + es-define-property: 1.0.1 side-channel: 1.1.1 queue-microtask@1.2.3: {} @@ -17697,12 +19020,12 @@ snapshots: lodash.clonedeep: 4.5.0 lodash.isequal: 4.5.0 - quill-resize-module@2.1.3: {} + quill-resize-module@2.0.8: {} quill@2.0.3: dependencies: - eventemitter3: 5.0.4 - lodash-es: 4.18.1 + eventemitter3: 5.0.1 + lodash-es: 4.17.23 parchment: 3.0.0 quill-delta: 5.1.0 @@ -17720,7 +19043,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 unpipe: 1.0.0 rc@1.2.8: @@ -17731,17 +19054,17 @@ snapshots: strip-json-comments: 2.0.1 optional: true - react-big-calendar@1.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-big-calendar@1.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 clsx: 2.1.1 date-arithmetic: 4.1.0 - dayjs: 1.11.21 - dom-helpers: 6.0.1 - globalize: 1.7.1 + dayjs: 1.11.18 + dom-helpers: 5.2.1 + globalize: 0.1.1 invariant: 2.2.4 - lodash: 4.18.1 - lodash-es: 4.18.1 + lodash: 4.17.23 + lodash-es: 4.17.23 luxon: 3.7.2 memoize-one: 6.0.0 moment: 2.30.1 @@ -17755,8 +19078,8 @@ snapshots: react-color@2.19.3(react@18.3.1): dependencies: '@icons/material': 0.2.4(react@18.3.1) - lodash: 4.18.1 - lodash-es: 4.18.1 + lodash: 4.17.23 + lodash-es: 4.17.23 material-colors: 1.2.6 prop-types: 15.8.1 react: 18.3.1 @@ -17775,9 +19098,9 @@ snapshots: react-datepicker@8.10.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@floating-ui/react': 0.27.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react': 0.27.20(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 - date-fns: 4.4.0 + date-fns: 4.1.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -17787,14 +19110,14 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-dropzone@14.4.1(patch_hash=d30fd95f2a3d58218fd5d657104b52cad6924893c0ac0e173f51c8c2d8e179b6)(react@18.3.1): + react-dropzone@14.3.8(patch_hash=d30fd95f2a3d58218fd5d657104b52cad6924893c0ac0e173f51c8c2d8e179b6)(react@18.3.1): dependencies: attr-accept: 2.2.5 file-selector: 2.1.2 prop-types: 15.8.1 react: 18.3.1 - react-image-crop@11.0.10(react@18.3.1): + react-image-crop@11.1.2(react@18.3.1): dependencies: react: 18.3.1 @@ -17802,9 +19125,11 @@ snapshots: react-is@17.0.2: {} + react-is@18.2.0: {} + react-is@18.3.1: {} - react-is@19.2.7: {} + react-is@19.2.8: {} react-lifecycles-compat@3.0.4: {} @@ -17815,10 +19140,10 @@ snapshots: react-overlays@5.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 7.28.4 '@popperjs/core': 2.11.8 '@restart/hooks': 0.4.16(react@18.3.1) - '@types/warning': 3.0.4 + '@types/warning': 3.0.3 dom-helpers: 5.2.1 prop-types: 15.8.1 react: 18.3.1 @@ -17826,24 +19151,24 @@ snapshots: uncontrollable: 7.2.1(react@18.3.1) warning: 4.0.3 - react-pdf@9.2.1(@types/react@19.2.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-pdf@9.2.1(@types/react@19.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 2.1.1 dequal: 2.0.3 make-cancellable-promise: 1.3.2 make-event-props: 1.6.2 - merge-refs: 1.3.0(@types/react@19.2.17) + merge-refs: 1.3.0(@types/react@19.2.2) pdfjs-dist: 4.8.69 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tiny-invariant: 1.3.3 warning: 4.0.3 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.2 - react-plotly.js@2.6.0(plotly.js@3.6.0(mapbox-gl@1.13.3))(react@18.3.1): + react-plotly.js@2.6.0(plotly.js@3.1.1(mapbox-gl@1.13.3))(react@18.3.1): dependencies: - plotly.js: 3.6.0(mapbox-gl@1.13.3) + plotly.js: 3.1.1(mapbox-gl@1.13.3) prop-types: 15.8.1 react: 18.3.1 @@ -17854,10 +19179,10 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-test-renderer@19.2.7(react@18.3.1): + react-test-renderer@19.2.8(react@18.3.1): dependencies: react: 18.3.1 - react-is: 19.2.7 + react-is: 19.2.8 scheduler: 0.27.0 react@18.3.1: @@ -17866,7 +19191,7 @@ snapshots: reactcss@1.2.3(react@18.3.1): dependencies: - lodash: 4.18.1 + lodash: 4.17.23 react: 18.3.1 read-cache@1.0.0: @@ -17898,11 +19223,11 @@ snapshots: readdirp@3.6.0: dependencies: - picomatch: 2.3.2 + picomatch: 2.3.1 - readdirp@5.0.0: {} + readdirp@5.1.1: {} - recast@0.23.11: + recast@0.23.20: dependencies: ast-types: 0.16.1 esprima: 4.0.1 @@ -17912,7 +19237,7 @@ snapshots: rechoir@0.6.2: dependencies: - resolve: 1.22.12 + resolve: 1.22.10 recursive-copy@2.0.14: dependencies: @@ -17933,11 +19258,11 @@ snapshots: reflect.getprototypeof@1.0.10: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -17952,7 +19277,7 @@ snapshots: regexp.prototype.flags@1.5.4: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 @@ -17964,13 +19289,13 @@ snapshots: regenerate: 1.4.2 regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 - regjsparser: 0.13.2 + regjsparser: 0.13.0 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.13.2: + regjsparser@0.13.0: dependencies: jsesc: 3.1.0 @@ -17998,14 +19323,19 @@ snapshots: pick-by-alias: 1.2.0 to-float32: 1.1.0 - regl-scatter2d@3.4.0: + regl-scatter2d@3.3.1: dependencies: '@plotly/point-cluster': 3.1.9 - array-bounds: 1.0.1 + array-range: 1.0.1 + array-rearrange: 2.2.2 + clamp: 1.0.1 color-id: 1.1.0 color-normalize: 1.5.0 + color-rgba: 2.4.0 flatten-vertex-data: 1.0.2 glslify: 7.1.1 + is-iexplorer: 1.0.0 + object-assign: 4.1.1 parse-rect: 1.2.0 pick-by-alias: 1.2.0 to-float32: 1.1.0 @@ -18015,12 +19345,12 @@ snapshots: dependencies: array-bounds: 1.0.1 array-range: 1.0.1 - color-alpha: 1.1.3 + color-alpha: 1.0.4 flatten-vertex-data: 1.0.2 parse-rect: 1.2.0 pick-by-alias: 1.2.0 raf: 3.4.1 - regl-scatter2d: 3.4.0 + regl-scatter2d: 3.3.1 regl@2.1.1: {} @@ -18040,14 +19370,26 @@ snapshots: resolve-protobuf-schema@2.1.0: dependencies: - protocol-buffers-schema: 3.6.1 + protocol-buffers-schema: 3.6.0 resolve@0.6.3: {} + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + resolve@1.22.12: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.2 + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -18055,7 +19397,7 @@ snapshots: dependencies: es-errors: 1.3.0 is-core-module: 2.16.2 - node-exports-info: 1.6.0 + node-exports-info: 1.6.2 object-keys: 1.1.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -18067,6 +19409,8 @@ snapshots: reusify@1.1.0: {} + right-now@1.0.0: {} + rimraf@2.7.1: dependencies: glob: 7.2.3 @@ -18089,29 +19433,29 @@ snapshots: globby: 10.0.1 is-plain-object: 3.0.1 - rollup-plugin-license@3.7.1(picomatch@4.0.4)(rollup@4.62.0): + rollup-plugin-license@3.6.0(picomatch@4.0.3)(rollup@3.29.5): dependencies: commenting: 1.1.0 - fdir: 6.5.0(picomatch@4.0.4) - lodash: 4.18.1 - magic-string: 0.30.21 + fdir: 6.5.0(picomatch@4.0.3) + lodash: 4.17.23 + magic-string: 0.30.19 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 4.62.0 + rollup: 3.29.5 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: - picomatch - rollup-plugin-license@3.7.1(rollup@4.62.0): + rollup-plugin-license@3.7.1(picomatch@4.0.5)(rollup@4.62.4): dependencies: commenting: 1.1.0 - fdir: 6.5.0 - lodash: 4.18.1 - magic-string: 0.30.21 + fdir: 6.5.0(picomatch@4.0.5) + lodash: 4.17.23 + magic-string: 0.30.19 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 4.62.0 + rollup: 4.62.4 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: @@ -18124,38 +19468,38 @@ snapshots: - bufferutil - utf-8-validate - rollup-plugin-postcss@4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)): + rollup-plugin-postcss@4.0.2(postcss@8.5.26)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: chalk: 4.1.2 concat-with-sourcemaps: 1.1.0 - cssnano: 5.1.15(postcss@8.5.15) + cssnano: 5.1.15(postcss@8.5.26) import-cwd: 3.0.0 p-queue: 6.6.2 pify: 5.0.0 - postcss: 8.5.15 - postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) - postcss-modules: 4.3.1(postcss@8.5.15) + postcss: 8.5.26 + postcss-load-config: 3.1.4(postcss@8.5.26)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) + postcss-modules: 4.3.1(postcss@8.5.26) promise.series: 0.2.0 - resolve: 1.22.12 + resolve: 1.22.10 rollup-pluginutils: 2.8.2 safe-identifier: 0.4.2 style-inject: 0.3.0 transitivePeerDependencies: - ts-node - rollup-plugin-postcss@4.0.2(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)): + rollup-plugin-postcss@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)): dependencies: chalk: 4.1.2 concat-with-sourcemaps: 1.1.0 - cssnano: 5.1.15(postcss@8.5.15) + cssnano: 5.1.15(postcss@8.5.6) import-cwd: 3.0.0 p-queue: 6.6.2 pify: 5.0.0 - postcss: 8.5.15 - postcss-load-config: 3.1.4(postcss@8.5.15)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3)) - postcss-modules: 4.3.1(postcss@8.5.15) + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) + postcss-modules: 4.3.1(postcss@8.5.6) promise.series: 0.2.0 - resolve: 1.22.12 + resolve: 1.22.10 rollup-pluginutils: 2.8.2 safe-identifier: 0.4.2 style-inject: 0.3.0 @@ -18171,40 +19515,45 @@ snapshots: dependencies: estree-walker: 0.6.1 - rollup-preserve-directives@1.1.3(rollup@4.62.0): + rollup-preserve-directives@1.1.3(rollup@4.62.4): dependencies: - magic-string: 0.30.21 - rollup: 4.62.0 + magic-string: 0.30.19 + rollup: 4.62.4 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 - rollup@4.62.0: + rollup@4.62.4: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.0 - '@rollup/rollup-android-arm64': 4.62.0 - '@rollup/rollup-darwin-arm64': 4.62.0 - '@rollup/rollup-darwin-x64': 4.62.0 - '@rollup/rollup-freebsd-arm64': 4.62.0 - '@rollup/rollup-freebsd-x64': 4.62.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 - '@rollup/rollup-linux-arm-musleabihf': 4.62.0 - '@rollup/rollup-linux-arm64-gnu': 4.62.0 - '@rollup/rollup-linux-arm64-musl': 4.62.0 - '@rollup/rollup-linux-loong64-gnu': 4.62.0 - '@rollup/rollup-linux-loong64-musl': 4.62.0 - '@rollup/rollup-linux-ppc64-gnu': 4.62.0 - '@rollup/rollup-linux-ppc64-musl': 4.62.0 - '@rollup/rollup-linux-riscv64-gnu': 4.62.0 - '@rollup/rollup-linux-riscv64-musl': 4.62.0 - '@rollup/rollup-linux-s390x-gnu': 4.62.0 - '@rollup/rollup-linux-x64-gnu': 4.62.0 - '@rollup/rollup-linux-x64-musl': 4.62.0 - '@rollup/rollup-openbsd-x64': 4.62.0 - '@rollup/rollup-openharmony-arm64': 4.62.0 - '@rollup/rollup-win32-arm64-msvc': 4.62.0 - '@rollup/rollup-win32-ia32-msvc': 4.62.0 - '@rollup/rollup-win32-x64-gnu': 4.62.0 - '@rollup/rollup-win32-x64-msvc': 4.62.0 + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.62.4 + '@rollup/rollup-android-arm64': 4.62.4 + '@rollup/rollup-darwin-arm64': 4.62.4 + '@rollup/rollup-darwin-x64': 4.62.4 + '@rollup/rollup-freebsd-arm64': 4.62.4 + '@rollup/rollup-freebsd-x64': 4.62.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.4 + '@rollup/rollup-linux-arm-musleabihf': 4.62.4 + '@rollup/rollup-linux-arm64-gnu': 4.62.4 + '@rollup/rollup-linux-arm64-musl': 4.62.4 + '@rollup/rollup-linux-loong64-gnu': 4.62.4 + '@rollup/rollup-linux-loong64-musl': 4.62.4 + '@rollup/rollup-linux-ppc64-gnu': 4.62.4 + '@rollup/rollup-linux-ppc64-musl': 4.62.4 + '@rollup/rollup-linux-riscv64-gnu': 4.62.4 + '@rollup/rollup-linux-riscv64-musl': 4.62.4 + '@rollup/rollup-linux-s390x-gnu': 4.62.4 + '@rollup/rollup-linux-x64-gnu': 4.62.4 + '@rollup/rollup-linux-x64-musl': 4.62.4 + '@rollup/rollup-openbsd-x64': 4.62.4 + '@rollup/rollup-openharmony-arm64': 4.62.4 + '@rollup/rollup-win32-arm64-msvc': 4.62.4 + '@rollup/rollup-win32-ia32-msvc': 4.62.4 + '@rollup/rollup-win32-x64-gnu': 4.62.4 + '@rollup/rollup-win32-x64-msvc': 4.62.4 fsevents: 2.3.3 router@2.2.0: @@ -18229,9 +19578,9 @@ snapshots: dependencies: tslib: 1.14.1 - safe-array-concat@1.1.4: + safe-array-concat@1.1.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 @@ -18256,15 +19605,15 @@ snapshots: safer-buffer@2.1.2: {} - sass@1.101.0: + sass@1.102.0: dependencies: chokidar: 5.0.0 - immutable: 5.1.6 + immutable: 5.1.9 source-map-js: 1.2.1 optionalDependencies: - '@parcel/watcher': 2.5.6 + '@parcel/watcher': 2.5.1 - sax@1.6.0: {} + sax@1.4.1: {} saxes@6.0.0: dependencies: @@ -18280,7 +19629,9 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} + semver@7.7.3: {} + + semver@7.8.5: {} send@1.2.1: dependencies: @@ -18302,7 +19653,7 @@ snapshots: dependencies: randombytes: 2.1.0 - serialize-javascript@7.0.5: {} + serialize-javascript@7.1.0: {} serve-static@2.2.1: dependencies: @@ -18333,7 +19684,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 setimmediate@1.0.5: {} @@ -18384,6 +19735,11 @@ snapshots: minimist: 1.2.8 shelljs: 0.9.2 + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -18404,6 +19760,14 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + side-channel@1.1.1: dependencies: es-errors: 1.3.0 @@ -18418,6 +19782,8 @@ snapshots: signature_pad@5.1.3: {} + signum@1.0.0: {} + simple-concat@1.0.1: optional: true @@ -18432,29 +19798,21 @@ snapshots: slash@3.0.0: {} - smob@1.6.2: {} + smob@1.5.0: {} - sort-object-keys@2.1.0: {} + sort-object-keys@1.1.3: {} - sort-package-json@3.6.0: - dependencies: - detect-indent: 7.0.2 - detect-newline: 4.0.1 - git-hooks-list: 4.2.1 - is-plain-obj: 4.1.0 - semver: 7.8.4 - sort-object-keys: 2.1.0 - tinyglobby: 0.2.17 + sort-object-keys@2.1.0: {} - sort-package-json@3.7.1: + sort-package-json@3.4.0: dependencies: detect-indent: 7.0.2 detect-newline: 4.0.1 - git-hooks-list: 4.2.1 + git-hooks-list: 4.1.1 is-plain-obj: 4.1.0 - semver: 7.8.4 - sort-object-keys: 2.1.0 - tinyglobby: 0.2.17 + semver: 7.7.3 + sort-object-keys: 1.1.3 + tinyglobby: 0.2.15 source-map-js@1.2.1: {} @@ -18481,20 +19839,20 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.23 + spdx-license-ids: 3.0.22 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.23 + spdx-license-ids: 3.0.22 spdx-expression-validate@2.0.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids@3.0.23: {} + spdx-license-ids@3.0.22: {} spdx-ranges@2.1.1: {} @@ -18518,6 +19876,8 @@ snapshots: dependencies: escodegen: 2.1.0 + statuses@2.0.1: {} + statuses@2.0.2: {} stop-iteration-iterator@1.1.0: @@ -18554,58 +19914,57 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.2.0 + strip-ansi: 7.1.2 string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 string.prototype.matchall@4.0.12: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 es-errors: 1.3.0 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 - side-channel: 1.1.1 + side-channel: 1.1.0 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.2 + es-abstract: 1.24.0 - string.prototype.trim@1.2.11: + string.prototype.trim@1.2.10: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.2 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 - safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.10: + string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.1.2 + es-object-atoms: 1.1.1 string_decoder@0.10.31: {} @@ -18621,7 +19980,7 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.2.0: + strip-ansi@7.1.2: dependencies: ansi-regex: 6.2.2 @@ -18648,13 +20007,19 @@ snapshots: style-inject@0.3.0: {} - style-mod@4.1.3: {} + style-mod@4.1.2: {} + + stylehacks@5.1.1(postcss@8.5.26): + dependencies: + browserslist: 4.26.3 + postcss: 8.5.26 + postcss-selector-parser: 6.1.2 - stylehacks@5.1.1(postcss@8.5.15): + stylehacks@5.1.1(postcss@8.5.6): dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 - postcss-selector-parser: 6.1.4 + browserslist: 4.26.3 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 supercluster@7.1.5: dependencies: @@ -18662,7 +20027,9 @@ snapshots: supercluster@8.0.1: dependencies: - kdbush: 4.1.0 + kdbush: 4.0.2 + + superscript-text@1.0.0: {} supports-color@7.2.0: dependencies: @@ -18691,31 +20058,35 @@ snapshots: parse-svg-path: 0.1.2 svg-path-bounds: 1.0.2 - svgo@2.8.2: + svgo@2.8.0: dependencies: + '@trysound/sax': 0.2.0 commander: 7.2.0 css-select: 4.3.0 css-tree: 1.1.3 csso: 4.2.0 picocolors: 1.1.1 - sax: 1.6.0 stable: 0.1.8 - swiper@12.2.0: {} + swiper@12.1.2: {} symbol-tree@3.2.4: {} + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + synckit@0.11.13: dependencies: '@pkgr/core': 0.3.6 - tabbable@6.4.0: {} + tabbable@6.2.0: {} tar-fs@2.1.4: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.4 + pump: 3.0.3 tar-stream: 2.2.0 optional: true @@ -18728,18 +20099,18 @@ snapshots: readable-stream: 3.6.2 optional: true - terser@5.48.0: + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.17.0 + acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 test-exclude@6.0.0: dependencies: - '@istanbuljs/schema': 0.1.6 + '@istanbuljs/schema': 0.1.3 glob: 7.2.3 - minimatch: 3.1.5 + minimatch: 3.1.4 through2@0.6.5: dependencies: @@ -18757,12 +20128,17 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.2.4: {} + tinyexec@1.0.1: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinyqueue@2.0.3: {} @@ -18780,7 +20156,7 @@ snapshots: to-float32@1.1.0: {} - to-px@1.1.0: + to-px@1.0.1: dependencies: parse-unit: 1.0.1 @@ -18806,75 +20182,55 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.5.0(typescript@5.9.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 - ts-api-utils@2.5.0(typescript@6.0.3): + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: - typescript: 6.0.3 + typescript: 5.9.3 ts-custom-error@3.3.1: {} - ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.3.0)(@jest/types@30.4.1)(babel-jest@30.3.0(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3)) + jest: 30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.8.4 + semver: 7.8.5 type-fest: 4.41.0 typescript: 5.9.3 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.29.7 - '@jest/transform': 30.3.0 + '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - babel-jest: 30.3.0(@babel/core@7.29.7) + babel-jest: 30.4.1(@babel/core@7.29.7) jest-util: 30.4.1 - ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@5.9.3): + ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 + '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 24.12.4 - acorn: 8.17.0 - acorn-walk: 8.3.5 + acorn: 8.15.0 + acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.4 + diff: 4.0.2 make-error: 1.3.6 typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: - '@swc/core': 1.15.41 - - ts-node@10.9.2(@swc/core@1.15.41)(@types/node@24.12.4)(typescript@6.0.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 24.12.4 - acorn: 8.17.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 6.0.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.15.41 + '@swc/core': 1.13.5 tsconfig-paths@3.15.0: dependencies: @@ -18892,14 +20248,32 @@ snapshots: safe-buffer: 5.2.1 optional: true - turbo@2.9.18: + turbo-darwin-64@2.5.8: + optional: true + + turbo-darwin-arm64@2.5.8: + optional: true + + turbo-linux-64@2.5.8: + optional: true + + turbo-linux-arm64@2.5.8: + optional: true + + turbo-windows-64@2.5.8: + optional: true + + turbo-windows-arm64@2.5.8: + optional: true + + turbo@2.5.8: optionalDependencies: - '@turbo/darwin-64': 2.9.18 - '@turbo/darwin-arm64': 2.9.18 - '@turbo/linux-64': 2.9.18 - '@turbo/linux-arm64': 2.9.18 - '@turbo/windows-64': 2.9.18 - '@turbo/windows-arm64': 2.9.18 + turbo-darwin-64: 2.5.8 + turbo-darwin-arm64: 2.5.8 + turbo-linux-64: 2.5.8 + turbo-linux-arm64: 2.5.8 + turbo-windows-64: 2.5.8 + turbo-windows-arm64: 2.5.8 tweetnacl@1.0.3: {} @@ -18916,7 +20290,7 @@ snapshots: type-is@2.1.0: dependencies: content-type: 2.0.0 - media-typer: 1.1.0 + media-typer: 1.1.1 mime-types: 3.0.2 type@2.7.3: {} @@ -18929,7 +20303,7 @@ snapshots: typed-array-byte-length@1.0.3: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 @@ -18938,16 +20312,16 @@ snapshots: typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.9 + call-bind: 1.0.8 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.8: + typed-array-length@1.0.7: dependencies: - call-bind: 1.0.9 + call-bind: 1.0.8 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 @@ -18961,21 +20335,19 @@ snapshots: typedarray@0.0.6: {} - typescript-eslint@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3): + typescript-eslint@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.61.1(@typescript-eslint/parser@8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/parser': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.6.1) - typescript: 6.0.3 + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.3(jiti@2.6.1) + typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.9.3: {} - typescript@6.0.3: {} - uc.micro@2.1.0: {} uglify-js@3.19.3: @@ -18990,8 +20362,8 @@ snapshots: uncontrollable@7.2.1(react@18.3.1): dependencies: - '@babel/runtime': 7.29.7 - '@types/react': 19.2.17 + '@babel/runtime': 7.28.4 + '@types/react': 19.2.2 invariant: 2.2.4 react: 18.3.1 react-lifecycles-compat: 3.0.4 @@ -19044,9 +20416,15 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - update-browserslist-db@1.2.3(browserslist@4.28.2): + update-browserslist-db@1.1.3(browserslist@4.26.3): + dependencies: + browserslist: 4.26.3 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-browserslist-db@1.3.0(browserslist@4.28.8): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.8 escalade: 3.2.0 picocolors: 1.1.1 @@ -19062,7 +20440,7 @@ snapshots: util-deprecate@1.0.2: {} - uuid@14.0.0: {} + uuid@14.0.1: {} v8-compile-cache-lib@3.0.1: {} @@ -19148,7 +20526,7 @@ snapshots: which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 - function.prototype.name: 1.2.0 + function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 @@ -19159,7 +20537,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.22 + which-typed-array: 1.1.19 which-collection@1.0.2: dependencies: @@ -19168,10 +20546,10 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.22: + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.9 + call-bind: 1.0.8 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 @@ -19188,7 +20566,7 @@ snapshots: which@4.0.0: dependencies: - isexe: 3.1.5 + isexe: 3.1.1 word-wrap@1.2.5: {} @@ -19208,13 +20586,13 @@ snapshots: dependencies: ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.2.0 + strip-ansi: 7.1.2 wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 string-width: 7.2.0 - strip-ansi: 7.2.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} @@ -19223,9 +20601,9 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@7.5.11: {} + ws@7.5.10: {} - ws@8.21.0: {} + ws@8.18.3: {} xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz: {} @@ -19233,7 +20611,7 @@ snapshots: xml2js@0.6.2: dependencies: - sax: 1.6.0 + sax: 1.4.1 xmlbuilder: 11.0.1 xml@1.0.1: {} @@ -19254,7 +20632,7 @@ snapshots: yallist@3.1.1: {} - yaml@1.10.3: {} + yaml@1.10.2: {} yargs-parser@20.2.9: {} @@ -19295,7 +20673,7 @@ snapshots: yocto-queue@0.1.0: {} - zip-a-folder@6.1.1: + zip-a-folder@6.1.4: dependencies: lzma: 2.3.2 tinyglobby: 0.2.17 From fb2996fcdadd4201133a7db1b1e3c8956a0673e4 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 17 Dec 2025 23:00:46 +0100 Subject: [PATCH 02/36] refactor(pluggable-widgets-mcp): fix prompt timeout, refactor schema, add custom dir --- packages/pluggable-widgets-mcp/package.json | 2 +- .../src/tools/scaffolding.tools.ts | 94 +++++--------- .../pluggable-widgets-mcp/src/tools/types.ts | 96 +++++++++++++-- .../src/tools/utils/generator.ts | 115 +++++++++++++----- 4 files changed, 208 insertions(+), 99 deletions(-) diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index b23cdf64bc..ce8f39a6fd 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -37,6 +37,6 @@ "keywords": [], "packageManager": "pnpm@10.17.0", "engines": { - "node": ">=22" + "node": ">=20" } } diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 962a8a7282..59f35930c9 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -1,70 +1,27 @@ import { mkdir } from "node:fs/promises"; import { z } from "zod"; import { GENERATIONS_DIR } from "@/config"; -import type { ToolContext, ToolDefinition, ToolResponse } from "@/tools/types"; import { - buildWidgetOptions, DEFAULT_WIDGET_OPTIONS, - GENERATOR_PROMPTS, - runWidgetGenerator, - SCAFFOLD_PROGRESS -} from "@/tools/utils/generator"; + widgetOptionsSchema, + type ToolContext, + type ToolDefinition, + type ToolResponse +} from "@/tools/types"; +import { buildWidgetOptions, GENERATOR_PROMPTS, runWidgetGenerator, SCAFFOLD_PROGRESS } from "@/tools/utils/generator"; import { ProgressTracker } from "@/tools/utils/progress-tracker"; import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; -const createWidgetSchema = z.object({ - name: z +/** + * Schema for create-widget tool input. + * Extends the base widgetOptionsSchema with tool-specific options like outputPath. + */ +const createWidgetSchema = widgetOptionsSchema.extend({ + outputPath: z .string() - .min(1) - .max(100) - .describe("[REQUIRED] The name of the widget in PascalCase (e.g., 'MyAwesomeWidget', 'DataChart')"), - description: z.string().min(1).max(200).describe("[REQUIRED] A brief description of what the widget does"), - version: z - .string() - .regex(/^\d+\.\d+\.\d+$/, "Version must be in semver format: x.y.z") - .optional() - .describe(`[OPTIONAL] Initial version in semver format. Default: "${DEFAULT_WIDGET_OPTIONS.version}"`), - author: z - .string() - .min(1) - .max(100) - .optional() - .describe(`[OPTIONAL] Author name. Default: "${DEFAULT_WIDGET_OPTIONS.author}"`), - license: z - .string() - .min(1) - .max(50) - .optional() - .describe(`[OPTIONAL] License type. Default: "${DEFAULT_WIDGET_OPTIONS.license}"`), - organization: z - .string() - .min(1) - .max(100) - .optional() - .describe( - `[OPTIONAL] Organization name for the widget namespace. Default: "${DEFAULT_WIDGET_OPTIONS.organization}"` - ), - template: z - .enum(["full", "empty"]) - .optional() - .describe( - `[OPTIONAL] Widget template: "full" includes sample code and examples, "empty" is minimal/blank. Default: "${DEFAULT_WIDGET_OPTIONS.template}"` - ), - programmingLanguage: z - .enum(["typescript", "javascript"]) - .optional() - .describe( - `[OPTIONAL] Programming language for the widget source code. Default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}"` - ), - unitTests: z - .boolean() - .optional() - .describe(`[OPTIONAL] Include unit test setup with Jest. Default: ${DEFAULT_WIDGET_OPTIONS.unitTests}`), - e2eTests: z - .boolean() .optional() .describe( - `[OPTIONAL] Include end-to-end test setup with Playwright. Default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}` + "[OPTIONAL] Directory where widget will be created. Defaults to ./generations/ within the MCP server package." ) }); @@ -87,9 +44,22 @@ OPTIONAL (with defaults): • programmingLanguage: "typescript" or "javascript" (default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}") • unitTests: Include Jest test setup (default: ${DEFAULT_WIDGET_OPTIONS.unitTests}) • e2eTests: Include Playwright E2E tests (default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}) + • outputPath: Directory where widget will be created (default: ./generations/) Ask the user if they want to customize any options before proceeding.`; +/** + * Returns scaffolding-related tools for widget creation and management. + * + * Currently contains only the create-widget tool, but structured as an array + * for extensibility. This modular pattern allows easy addition of related tools + * such as: + * - Widget property editing + * - XML configuration management + * - Build and deployment automation + * + * @see AGENTS.md Roadmap Context section for planned additions + */ export function getScaffoldingTools(): Array> { return [ { @@ -104,6 +74,7 @@ export function getScaffoldingTools(): Array> async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext): Promise { const options = buildWidgetOptions(args); + const outputDir = args.outputPath ?? GENERATIONS_DIR; const tracker = new ProgressTracker({ context, logger: "scaffolding", @@ -116,14 +87,15 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) await tracker.info(`Starting widget scaffolding for "${options.name}"...`, { widgetName: options.name, template: options.template, - organization: options.organization + organization: options.organization, + outputDir }); - // Ensure generations directory exists - await mkdir(GENERATIONS_DIR, { recursive: true }); + // Ensure output directory exists + await mkdir(outputDir, { recursive: true }); - const widgetFolder = await runWidgetGenerator(options, tracker); - const widgetPath = `${GENERATIONS_DIR}/${widgetFolder}`; + const widgetFolder = await runWidgetGenerator(options, tracker, outputDir); + const widgetPath = `${outputDir}/${widgetFolder}`; console.error(`[create-widget] Widget created successfully at ${widgetPath}`); await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget created successfully!"); diff --git a/packages/pluggable-widgets-mcp/src/tools/types.ts b/packages/pluggable-widgets-mcp/src/tools/types.ts index 65b1e252b8..87e80083c7 100644 --- a/packages/pluggable-widgets-mcp/src/tools/types.ts +++ b/packages/pluggable-widgets-mcp/src/tools/types.ts @@ -1,6 +1,6 @@ import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; import type { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js"; -import type { ZodType } from "zod"; +import { z, type ZodType } from "zod"; // ============================================================================= // MCP Core Types @@ -53,7 +53,89 @@ export type LogLevel = "debug" | "info" | "notice" | "warning" | "error"; // ============================================================================= /** - * Options for creating a new Mendix pluggable widget. + * Default values for widget options. + * Centralized here to be used by both schema descriptions and buildWidgetOptions(). + */ +export const DEFAULT_WIDGET_OPTIONS = { + version: "1.0.0", + author: "Mendix", + license: "Apache-2.0", + organization: "Mendix", + template: "empty" as const, + programmingLanguage: "typescript" as const, + unitTests: true, + e2eTests: false +} as const; + +/** + * Zod schema for widget creation options. + * Single source of truth for widget options - type is derived via z.infer. + */ +export const widgetOptionsSchema = z.object({ + name: z + .string() + .min(1) + .max(100) + .describe("[REQUIRED] The name of the widget in PascalCase (e.g., 'MyAwesomeWidget', 'DataChart')"), + description: z.string().min(1).max(200).describe("[REQUIRED] A brief description of what the widget does"), + version: z + .string() + .regex(/^\d+\.\d+\.\d+$/, "Version must be in semver format: x.y.z") + .optional() + .describe(`[OPTIONAL] Initial version in semver format. Default: "${DEFAULT_WIDGET_OPTIONS.version}"`), + author: z + .string() + .min(1) + .max(100) + .optional() + .describe(`[OPTIONAL] Author name. Default: "${DEFAULT_WIDGET_OPTIONS.author}"`), + license: z + .string() + .min(1) + .max(50) + .optional() + .describe(`[OPTIONAL] License type. Default: "${DEFAULT_WIDGET_OPTIONS.license}"`), + organization: z + .string() + .min(1) + .max(100) + .optional() + .describe( + `[OPTIONAL] Organization name for the widget namespace. Default: "${DEFAULT_WIDGET_OPTIONS.organization}"` + ), + template: z + .enum(["full", "empty"]) + .optional() + .describe( + `[OPTIONAL] Widget template: "full" includes sample code and examples, "empty" is minimal/blank. Default: "${DEFAULT_WIDGET_OPTIONS.template}"` + ), + programmingLanguage: z + .enum(["typescript", "javascript"]) + .optional() + .describe( + `[OPTIONAL] Programming language for the widget source code. Default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}"` + ), + unitTests: z + .boolean() + .optional() + .describe(`[OPTIONAL] Include unit test setup with Jest. Default: ${DEFAULT_WIDGET_OPTIONS.unitTests}`), + e2eTests: z + .boolean() + .optional() + .describe( + `[OPTIONAL] Include end-to-end test setup with Playwright. Default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}` + ) +}); + +/** + * Input options for creating a new Mendix pluggable widget (with optional fields). + * Derived from widgetOptionsSchema to ensure type-schema consistency. + */ +export type WidgetOptionsInput = z.infer; + +/** + * Resolved widget options with all defaults applied (all fields required). + * This is the type returned by buildWidgetOptions() after applying defaults. */ export interface WidgetOptions { name: string; @@ -61,9 +143,9 @@ export interface WidgetOptions { version: string; author: string; license: string; - organization?: string; - template?: "full" | "empty"; - programmingLanguage?: "typescript" | "javascript"; - unitTests?: boolean; - e2eTests?: boolean; + organization: string; + template: "full" | "empty"; + programmingLanguage: "typescript" | "javascript"; + unitTests: boolean; + e2eTests: boolean; } diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts index 6fc590b63c..d6152428a4 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -1,8 +1,11 @@ import * as pty from "node-pty"; import { GENERATIONS_DIR, SCAFFOLD_TIMEOUT_MS } from "@/config"; -import type { WidgetOptions } from "@/tools/types"; +import { DEFAULT_WIDGET_OPTIONS, type WidgetOptions, type WidgetOptionsInput } from "@/tools/types"; import { ProgressTracker } from "./progress-tracker"; +// Re-export for backward compatibility with existing imports +export { DEFAULT_WIDGET_OPTIONS }; + /** * Generator prompt patterns in order - must match answers array. */ @@ -35,18 +38,15 @@ export const SCAFFOLD_PROGRESS = { } as const; /** - * Default values for widget options. + * Buffer size for prompt detection in terminal output. + * Increased from 500 to improve reliability with terminal buffering. */ -export const DEFAULT_WIDGET_OPTIONS = { - version: "1.0.0", - author: "Mendix", - license: "Apache-2.0", - organization: "Mendix", - template: "empty" as const, - programmingLanguage: "typescript" as const, - unitTests: true, - e2eTests: false -} as const; +const PROMPT_DETECTION_BUFFER_SIZE = 1000; + +/** + * Delay between sending answers to allow terminal to process. + */ +const ANSWER_SEND_DELAY_MS = 200; /** * Local state for tracking generator process progress. @@ -56,14 +56,15 @@ interface GeneratorLocalState { answerIndex: number; promptMatchedIndex: number; allPromptsAnswered: boolean; + lastActivityTime: number; } /** * Builds widget options from input arguments with defaults applied. + * Takes the schema-validated input (with optional fields) and returns + * fully resolved options (all fields required). */ -export function buildWidgetOptions( - args: Partial & Pick -): WidgetOptions { +export function buildWidgetOptions(args: WidgetOptionsInput): WidgetOptions { return { name: args.name, description: args.description, @@ -72,31 +73,49 @@ export function buildWidgetOptions( license: args.license ?? DEFAULT_WIDGET_OPTIONS.license, organization: args.organization ?? DEFAULT_WIDGET_OPTIONS.organization, template: args.template ?? DEFAULT_WIDGET_OPTIONS.template, - programmingLanguage: DEFAULT_WIDGET_OPTIONS.programmingLanguage, + programmingLanguage: args.programmingLanguage ?? DEFAULT_WIDGET_OPTIONS.programmingLanguage, unitTests: args.unitTests ?? DEFAULT_WIDGET_OPTIONS.unitTests, e2eTests: args.e2eTests ?? DEFAULT_WIDGET_OPTIONS.e2eTests }; } +/** + * Arrow key escape sequence for navigating interactive prompts. + */ +const ARROW_DOWN = "\x1b[B"; + +/** + * Maps programming language option to the key sequence needed. + * TypeScript is the first option (just Enter), JavaScript needs arrow down first. + */ +function getLanguageKeySequence(language: "typescript" | "javascript"): string { + return language === "javascript" ? ARROW_DOWN : ""; +} + /** * Builds the answers array for the generator prompts. + * @param options - Fully resolved widget options (all fields required) + * @param outputDir - Output directory (used for project path calculation) */ -export function buildGeneratorAnswers(options: WidgetOptions): string[] { +export function buildGeneratorAnswers(options: WidgetOptions, outputDir?: string): string[] { + // Calculate relative project path from widget folder to parent directory + const projectPath = outputDir ? "../" : "../"; + return [ "", // Widget name - already passed as CLI arg options.description, - options.organization ?? DEFAULT_WIDGET_OPTIONS.organization, + options.organization, "© Mendix Technology BV 2025", // Copyright options.license, options.version, options.author, - "../", // Project path (relative to widget folder inside generations/) - "", // Programming language - Enter for TypeScript (default) + projectPath, // Project path (relative to widget folder) + getLanguageKeySequence(options.programmingLanguage), // Programming language selection "", // Component type - Enter for Function Components (default) "", // Platform - Enter for web (default) - options.template ?? DEFAULT_WIDGET_OPTIONS.template, - options.unitTests !== false ? "yes" : "no", - options.e2eTests === true ? "yes" : "no" + options.template, + options.unitTests ? "yes" : "no", + options.e2eTests ? "yes" : "no" ]; } @@ -126,6 +145,7 @@ export function cleanTerminalOutput(data: string): string { /** * Handles generator output and sends answers when prompts are detected. + * Uses a larger buffer and improved logging for reliability. */ function handleGeneratorOutput( state: GeneratorLocalState, @@ -133,6 +153,9 @@ function handleGeneratorOutput( sendNextAnswer: () => void, onAllPromptsAnswered: () => void ): void { + // Update activity timestamp for stuck detection + state.lastActivityTime = Date.now(); + if (state.answerIndex < GENERATOR_PROMPTS.length) { // Skip if we've already matched this prompt if (state.promptMatchedIndex >= state.answerIndex) { @@ -140,7 +163,7 @@ function handleGeneratorOutput( } const expectedPattern = GENERATOR_PROMPTS[state.answerIndex]; - const recentOutput = state.output.slice(-500).toLowerCase(); + const recentOutput = state.output.slice(-PROMPT_DETECTION_BUFFER_SIZE).toLowerCase(); if (recentOutput.includes(expectedPattern.toLowerCase())) { state.promptMatchedIndex = state.answerIndex; @@ -158,7 +181,15 @@ function handleGeneratorOutput( }) .catch(() => undefined); - setTimeout(sendNextAnswer, 150); + setTimeout(sendNextAnswer, ANSWER_SEND_DELAY_MS); + } else { + // Debug logging for unmatched prompts (only log occasionally to avoid spam) + const cleanedRecent = cleanTerminalOutput(recentOutput.slice(-200)); + if (cleanedRecent.length > 0 && state.output.length % 500 < 50) { + console.error( + `[create-widget] Waiting for prompt "${expectedPattern}" (index ${state.answerIndex}), recent: "${cleanedRecent.slice(-100)}"` + ); + } } } else { onAllPromptsAnswered(); @@ -167,16 +198,24 @@ function handleGeneratorOutput( /** * Runs the Mendix widget generator using node-pty for terminal interaction. + * @param options - Widget configuration options + * @param tracker - Progress tracker for notifications + * @param outputDir - Directory where the widget will be created (defaults to GENERATIONS_DIR) */ -export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTracker): Promise { - const answers = buildGeneratorAnswers(options); +export function runWidgetGenerator( + options: WidgetOptions, + tracker: ProgressTracker, + outputDir: string = GENERATIONS_DIR +): Promise { + const answers = buildGeneratorAnswers(options, outputDir); return new Promise((resolve, reject) => { const state: GeneratorLocalState = { output: "", answerIndex: 0, promptMatchedIndex: -1, - allPromptsAnswered: false + allPromptsAnswered: false, + lastActivityTime: Date.now() }; tracker.start("initializing"); @@ -185,14 +224,15 @@ export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTrac name: "xterm-color", cols: 120, rows: 30, - cwd: GENERATIONS_DIR, + cwd: outputDir, env: { ...process.env, FORCE_COLOR: "0" } }); const sendNextAnswer = (): void => { if (state.answerIndex < answers.length) { const answer = answers[state.answerIndex]; - const displayAnswer = answer === "" ? "(Enter)" : `"${answer}"`; + const displayAnswer = + answer === "" ? "(Enter)" : answer.startsWith("\x1b") ? "(Arrow+Enter)" : `"${answer}"`; const idx = state.answerIndex + 1; console.error(`[create-widget] [${idx}/${answers.length}] Sending: ${displayAnswer}`); state.answerIndex++; @@ -200,6 +240,19 @@ export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTrac } }; + // Stuck detection: if no progress for 30 seconds, try resending current answer + const stuckCheckInterval = setInterval(() => { + const timeSinceActivity = Date.now() - state.lastActivityTime; + if (timeSinceActivity > 30000 && !state.allPromptsAnswered && state.answerIndex > 0) { + console.error( + `[create-widget] No progress for ${Math.round(timeSinceActivity / 1000)}s at step ${state.answerIndex}, retrying...` + ); + // Resend Enter to potentially unstick the process + ptyProcess.write("\r"); + state.lastActivityTime = Date.now(); + } + }, 10000); + ptyProcess.onData(data => { state.output += data; handleGeneratorOutput(state, tracker, sendNextAnswer, () => { @@ -215,6 +268,7 @@ export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTrac }); ptyProcess.onExit(({ exitCode }) => { + clearInterval(stuckCheckInterval); tracker.stop(); if (exitCode === 0) { const widgetFolder = `${options.name.toLowerCase()}-web`; @@ -233,6 +287,7 @@ export function runWidgetGenerator(options: WidgetOptions, tracker: ProgressTrac }); const timeout = setTimeout(() => { + clearInterval(stuckCheckInterval); tracker.stop(); console.error("[create-widget] Widget scaffold timed out after 5 minutes"); tracker From e2a6b1b7d863899d306950a255e800ae7128bc34 Mon Sep 17 00:00:00 2001 From: Rahman Date: Tue, 30 Dec 2025 11:59:54 +0100 Subject: [PATCH 03/36] feat(pluggable-widgets-mcp): wip --- packages/pluggable-widgets-mcp/package.json | 2 +- packages/pluggable-widgets-mcp/src/config.ts | 6 + packages/pluggable-widgets-mcp/src/index.ts | 26 +- .../src/resources/guidelines.ts | 98 ++++++ .../src/resources/index.ts | 44 +++ .../pluggable-widgets-mcp/src/server/http.ts | 10 +- .../src/server/server.ts | 4 +- .../src/tools/file-operations.tools.ts | 281 ++++++++++++++++++ .../pluggable-widgets-mcp/src/tools/index.ts | 6 + .../src/tools/scaffolding.tools.ts | 36 ++- .../src/tools/utils/generator.ts | 37 ++- 11 files changed, 518 insertions(+), 32 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/resources/guidelines.ts create mode 100644 packages/pluggable-widgets-mcp/src/resources/index.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index ce8f39a6fd..4e0dffa324 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@types/cors": "^2.8.19", - "@types/express": "^5.0.2", + "@types/express": "^5.0.6", "@types/node": "^24.10.1", "tsc-alias": "^1.8.16", "typescript": "^5.9.3" diff --git a/packages/pluggable-widgets-mcp/src/config.ts b/packages/pluggable-widgets-mcp/src/config.ts index 6dceeb505d..8d6e557332 100644 --- a/packages/pluggable-widgets-mcp/src/config.ts +++ b/packages/pluggable-widgets-mcp/src/config.ts @@ -21,5 +21,11 @@ const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)) export const PACKAGE_ROOT = join(__dirname, "../"); export const GENERATIONS_DIR = join(PACKAGE_ROOT, "generations"); +// Path to docs/requirements (relative to monorepo root) +export const DOCS_DIR = join(PACKAGE_ROOT, "../../docs/requirements"); + +// Allowed file extensions for widget file operations +export const ALLOWED_EXTENSIONS = [".tsx", ".ts", ".xml", ".scss", ".css", ".json", ".md", ".editorConfig.ts"]; + // Timeouts export const SCAFFOLD_TIMEOUT_MS = 300000; // 5 minutes diff --git a/packages/pluggable-widgets-mcp/src/index.ts b/packages/pluggable-widgets-mcp/src/index.ts index 553f709399..7ffcf600e7 100644 --- a/packages/pluggable-widgets-mcp/src/index.ts +++ b/packages/pluggable-widgets-mcp/src/index.ts @@ -5,19 +5,15 @@ type TransportMode = "http" | "stdio"; const mode = (process.argv[2] as TransportMode) || "http"; -async function main(): Promise { - switch (mode) { - case "stdio": - await startStdioServer(); - break; - case "http": - default: - await startHttpServer(); - break; - } +switch (mode) { + case "stdio": + startStdioServer().catch(err => { + console.error("Fatal error:", err); + process.exit(1); + }); + break; + case "http": + default: + startHttpServer(); + break; } - -main().catch(err => { - console.error("Fatal error:", err); - process.exit(1); -}); diff --git a/packages/pluggable-widgets-mcp/src/resources/guidelines.ts b/packages/pluggable-widgets-mcp/src/resources/guidelines.ts new file mode 100644 index 0000000000..e88918d001 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/resources/guidelines.ts @@ -0,0 +1,98 @@ +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { DOCS_DIR } from "@/config"; + +/** + * Definition for a guideline resource. + */ +export interface GuidelineResource { + /** Unique resource name */ + name: string; + /** Resource URI (e.g., mendix://guidelines/frontend) */ + uri: string; + /** Human-readable title */ + title: string; + /** Description of what this guideline covers */ + description: string; + /** Source markdown file name */ + filename: string; +} + +/** + * All available guideline resources. + */ +export const GUIDELINE_RESOURCES: GuidelineResource[] = [ + { + name: "frontend-guidelines", + uri: "mendix://guidelines/frontend", + title: "Frontend Guidelines", + description: "CSS/SCSS styling, naming conventions, component best practices, and Atlas UI integration", + filename: "frontend-guidelines.md" + }, + { + name: "implementation-plan", + uri: "mendix://guidelines/implementation", + title: "Implementation Plan", + description: "Step-by-step guide for creating new widgets, including PR templates and testing requirements", + filename: "implementation-plan.md" + }, + { + name: "app-flow", + uri: "mendix://guidelines/app-flow", + title: "Application Flow", + description: "Complete widget development lifecycle from scaffolding to Studio Pro integration", + filename: "app-flow.md" + }, + { + name: "backend-structure", + uri: "mendix://guidelines/backend-structure", + title: "Backend Structure", + description: + "Widget-to-Mendix runtime integration, data handling with EditableValue/ActionValue, and event management", + filename: "backend-structure.md" + }, + { + name: "tech-stack", + uri: "mendix://guidelines/tech-stack", + title: "Technology Stack", + description: "Core technologies (TypeScript, React, SCSS), monorepo structure, and development tools", + filename: "tech-stack.md" + } +]; + +/** + * Cache for loaded guideline content to avoid repeated file reads. + */ +const guidelineCache = new Map(); + +/** + * Loads the content of a guideline file. + * Caches content after first load for performance. + * + * @param filename - The markdown filename to load + * @returns The file content as a string + */ +export async function loadGuidelineContent(filename: string): Promise { + // Check cache first + if (guidelineCache.has(filename)) { + return guidelineCache.get(filename)!; + } + + const filePath = join(DOCS_DIR, filename); + + try { + const content = await readFile(filePath, "utf-8"); + guidelineCache.set(filename, content); + return content; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to load guideline ${filename}: ${message}`); + } +} + +/** + * Clears the guideline cache. Useful for testing or hot-reloading. + */ +export function clearGuidelineCache(): void { + guidelineCache.clear(); +} diff --git a/packages/pluggable-widgets-mcp/src/resources/index.ts b/packages/pluggable-widgets-mcp/src/resources/index.ts new file mode 100644 index 0000000000..2ef570bfb3 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/resources/index.ts @@ -0,0 +1,44 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { GUIDELINE_RESOURCES, loadGuidelineContent } from "./guidelines"; + +/** + * Registers all MCP resources with the server. + * + * Resources are read-only data sources that clients can fetch on-demand. + * We expose the Mendix widget development guidelines as resources so LLMs + * can access them when implementing widget functionality. + */ +export function registerResources(server: McpServer): void { + registerGuidelineResources(server); +} + +/** + * Registers guideline documentation as MCP resources. + */ +function registerGuidelineResources(server: McpServer): void { + for (const resource of GUIDELINE_RESOURCES) { + server.registerResource( + resource.name, + resource.uri, + { + title: resource.title, + description: resource.description, + mimeType: "text/markdown" + }, + async uri => { + const content = await loadGuidelineContent(resource.filename); + return { + contents: [ + { + uri: uri.href, + mimeType: "text/markdown", + text: content + } + ] + }; + } + ); + } + + console.error(`[resources] Registered ${GUIDELINE_RESOURCES.length} guideline resources`); +} diff --git a/packages/pluggable-widgets-mcp/src/server/http.ts b/packages/pluggable-widgets-mcp/src/server/http.ts index c1b5fed9c6..0486078512 100644 --- a/packages/pluggable-widgets-mcp/src/server/http.ts +++ b/packages/pluggable-widgets-mcp/src/server/http.ts @@ -8,26 +8,22 @@ import { sessionManager } from "./session"; * Starts the MCP server with HTTP/Streamable transport. * Supports multiple concurrent sessions via Express. */ -export async function startHttpServer(): Promise { +export function startHttpServer(): void { const app = createMcpExpressApp(); app.use(cors()); setupRoutes(app); - app.listen(PORT, () => { + const server = app.listen(PORT, () => { console.log(`[HTTP] MCP Server started on port ${PORT}`); console.log(`[HTTP] Health check: http://localhost:${PORT}/health`); console.log(`[HTTP] MCP endpoint: http://localhost:${PORT}/mcp`); }); - setupGracefulShutdown(); -} - -function setupGracefulShutdown(): void { const shutdown = async (): Promise => { console.log("\n[HTTP] Shutting down server..."); await sessionManager.closeAll(); - process.exit(0); + server.close(() => process.exit(0)); }; process.on("SIGINT", shutdown); diff --git a/packages/pluggable-widgets-mcp/src/server/server.ts b/packages/pluggable-widgets-mcp/src/server/server.ts index 42029e0d8a..44c0ec1f05 100644 --- a/packages/pluggable-widgets-mcp/src/server/server.ts +++ b/packages/pluggable-widgets-mcp/src/server/server.ts @@ -1,9 +1,10 @@ import { SERVER_ICON, SERVER_INSTRUCTIONS, SERVER_NAME, SERVER_VERSION, SERVER_WEBSITE_URL } from "@/config"; +import { registerResources } from "@/resources"; import { getAllTools } from "@/tools"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; /** - * Creates and configures a new MCP server instance with all registered tools. + * Creates and configures a new MCP server instance with all registered tools and resources. */ export function createMcpServer(): McpServer { const server = new McpServer( @@ -25,6 +26,7 @@ export function createMcpServer(): McpServer { ); registerTools(server); + registerResources(server); return server; } diff --git a/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts new file mode 100644 index 0000000000..8e865b591c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts @@ -0,0 +1,281 @@ +import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises"; +import { dirname, extname, join, resolve } from "node:path"; +import { z } from "zod"; +import { ALLOWED_EXTENSIONS } from "@/config"; +import type { AnyToolDefinition, ToolResponse } from "@/tools/types"; +import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; + +// ============================================================================= +// Path Validation Utilities +// ============================================================================= + +/** + * Validates that a file path is within the allowed widget directory. + * Prevents directory traversal attacks. + * + * @param basePath - The base widget directory path + * @param relativePath - The relative file path to validate + * @returns true if the path is safe, false otherwise + */ +function isPathWithinDirectory(basePath: string, relativePath: string): boolean { + // Resolve both paths to absolute paths + const resolvedBase = resolve(basePath); + const resolvedFull = resolve(basePath, relativePath); + + // Check that the resolved path starts with the base path + // This prevents ../ traversal attacks + return resolvedFull.startsWith(resolvedBase + "/") || resolvedFull === resolvedBase; +} + +/** + * Validates that a file extension is allowed for write operations. + * + * @param filePath - The file path to check + * @returns true if the extension is allowed, false otherwise + */ +function isExtensionAllowed(filePath: string): boolean { + const ext = extname(filePath).toLowerCase(); + // Also allow files without extension (like .gitignore patterns) + // and special config files + if (ext === "") { + const filename = filePath.split("/").pop() || ""; + // Allow common config files without extensions + return ["package", "tsconfig", "eslintrc", ".gitignore", ".prettierrc"].some( + name => filename.includes(name) || filename.startsWith(".") + ); + } + return ALLOWED_EXTENSIONS.includes(ext); +} + +/** + * Validates widget path and file path for security. + * Throws an error if validation fails. + */ +function validatePaths(widgetPath: string, filePath: string, checkExtension = false): void { + // Check for obvious path traversal attempts + if (filePath.includes("..")) { + throw new Error("Path traversal not allowed: '..' detected in file path"); + } + + // Validate path is within widget directory + if (!isPathWithinDirectory(widgetPath, filePath)) { + throw new Error("File path must be within the widget directory"); + } + + // For write operations, check extension + if (checkExtension && !isExtensionAllowed(filePath)) { + throw new Error(`File extension not allowed. Allowed extensions: ${ALLOWED_EXTENSIONS.join(", ")}`); + } +} + +// ============================================================================= +// Schemas +// ============================================================================= + +const listWidgetFilesSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory (returned by create-widget tool)") +}); + +const readWidgetFileSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), + filePath: z + .string() + .min(1) + .describe("Relative path to the file within the widget directory (e.g., 'src/MyWidget.tsx')") +}); + +const writeWidgetFileSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), + filePath: z + .string() + .min(1) + .describe("Relative path to the file within the widget directory (e.g., 'src/components/MyComponent.tsx')"), + content: z.string().describe("The content to write to the file") +}); + +type ListWidgetFilesInput = z.infer; +type ReadWidgetFileInput = z.infer; +type WriteWidgetFileInput = z.infer; + +// ============================================================================= +// Tool Handlers +// ============================================================================= + +/** + * Recursively lists all files in a directory. + */ +async function listFilesRecursive( + dir: string, + basePath: string, + files: Array<{ path: string; type: string }> = [] +): Promise> { + const entries = await readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(dir, entry.name); + const relativePath = fullPath.replace(basePath + "/", ""); + + if (entry.isDirectory()) { + // Skip node_modules and other common non-source directories + if (["node_modules", ".git", "dist", "build"].includes(entry.name)) { + continue; + } + await listFilesRecursive(fullPath, basePath, files); + } else { + const ext = extname(entry.name).toLowerCase(); + files.push({ + path: relativePath, + type: ext || "file" + }); + } + } + + return files; +} + +async function handleListWidgetFiles(args: ListWidgetFilesInput): Promise { + try { + // Verify the directory exists + const stats = await stat(args.widgetPath); + if (!stats.isDirectory()) { + return createErrorResponse(`Path is not a directory: ${args.widgetPath}`); + } + + const files = await listFilesRecursive(args.widgetPath, args.widgetPath); + + // Group files by type for better readability + const byType = files.reduce>((acc, file) => { + const type = file.type || "other"; + if (!acc[type]) acc[type] = []; + acc[type].push(file.path); + return acc; + }, {}); + + const output = [`Widget files in ${args.widgetPath}:`, "", `Total: ${files.length} files`, ""]; + + // Sort types for consistent output + const sortedTypes = Object.keys(byType).sort(); + for (const type of sortedTypes) { + output.push(`${type} files (${byType[type].length}):`); + for (const path of byType[type].sort()) { + output.push(` - ${path}`); + } + output.push(""); + } + + return createToolResponse(output.join("\n")); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(`Failed to list widget files: ${message}`); + } +} + +async function handleReadWidgetFile(args: ReadWidgetFileInput): Promise { + try { + validatePaths(args.widgetPath, args.filePath); + + const fullPath = join(args.widgetPath, args.filePath); + const content = await readFile(fullPath, "utf-8"); + + return createToolResponse( + [`File: ${args.filePath}`, `Path: ${fullPath}`, "", "Content:", "```", content, "```"].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(`Failed to read file: ${message}`); + } +} + +async function handleWriteWidgetFile(args: WriteWidgetFileInput): Promise { + try { + validatePaths(args.widgetPath, args.filePath, true); + + const fullPath = join(args.widgetPath, args.filePath); + + // Ensure parent directory exists + const parentDir = dirname(fullPath); + await mkdir(parentDir, { recursive: true }); + + // Write the file + await writeFile(fullPath, args.content, "utf-8"); + + console.error(`[file-operations] Wrote file: ${fullPath}`); + + return createToolResponse( + [ + `Successfully wrote file: ${args.filePath}`, + `Full path: ${fullPath}`, + `Size: ${args.content.length} characters` + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(`Failed to write file: ${message}`); + } +} + +// ============================================================================= +// Tool Definitions +// ============================================================================= + +const LIST_WIDGET_FILES_DESCRIPTION = `Lists all files in a widget directory. + +Use this tool after scaffolding a widget to understand its structure. +Returns files grouped by type (.tsx, .xml, .scss, etc.). + +Excludes: node_modules, .git, dist, build directories.`; + +const READ_WIDGET_FILE_DESCRIPTION = `Reads the contents of a file from a widget directory. + +Use this to inspect existing code before making modifications. +The file path should be relative to the widget directory. + +Examples: + - src/MyWidget.tsx (main component) + - src/MyWidget.xml (properties definition) + - src/components/Header.tsx (sub-component)`; + +const WRITE_WIDGET_FILE_DESCRIPTION = `Writes content to a file in a widget directory. + +Use this to implement widget functionality after scaffolding. +Creates parent directories if they don't exist. + +IMPORTANT: Follow Mendix widget development guidelines: + - Use TypeScript and React + - Follow Atlas UI styling conventions + - Use proper Mendix API types (EditableValue, ActionValue, etc.) + - Fetch mendix://guidelines/* resources for detailed instructions + +Allowed file types: ${ALLOWED_EXTENSIONS.join(", ")}`; + +/** + * Returns file operation tools for reading and writing widget files. + * + * These tools enable LLMs to implement widget functionality after scaffolding + * by reading existing code and writing new/updated files. + */ +export function getFileOperationTools(): AnyToolDefinition[] { + return [ + { + name: "list-widget-files", + title: "List Widget Files", + description: LIST_WIDGET_FILES_DESCRIPTION, + inputSchema: listWidgetFilesSchema, + handler: handleListWidgetFiles + }, + { + name: "read-widget-file", + title: "Read Widget File", + description: READ_WIDGET_FILE_DESCRIPTION, + inputSchema: readWidgetFileSchema, + handler: handleReadWidgetFile + }, + { + name: "write-widget-file", + title: "Write Widget File", + description: WRITE_WIDGET_FILE_DESCRIPTION, + inputSchema: writeWidgetFileSchema, + handler: handleWriteWidgetFile + } + ]; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts index e80ee7c511..651692c514 100644 --- a/packages/pluggable-widgets-mcp/src/tools/index.ts +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -1,13 +1,19 @@ import type { AnyToolDefinition } from "@/tools/types"; +import { getFileOperationTools } from "./file-operations.tools"; import { getScaffoldingTools } from "./scaffolding.tools"; /** * Gets all tool definitions for registration with the MCP server. + * + * Tools are organized by category: + * - Scaffolding: Widget creation (create-widget) + * - File Operations: Read/write widget files (list-widget-files, read-widget-file, write-widget-file) */ export function getAllTools(): AnyToolDefinition[] { const tools: AnyToolDefinition[] = []; tools.push(...getScaffoldingTools()); + tools.push(...getFileOperationTools()); return tools; } diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 59f35930c9..71f0f8fe83 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -1,16 +1,16 @@ -import { mkdir } from "node:fs/promises"; -import { z } from "zod"; import { GENERATIONS_DIR } from "@/config"; import { DEFAULT_WIDGET_OPTIONS, - widgetOptionsSchema, type ToolContext, type ToolDefinition, - type ToolResponse + type ToolResponse, + widgetOptionsSchema } from "@/tools/types"; import { buildWidgetOptions, GENERATOR_PROMPTS, runWidgetGenerator, SCAFFOLD_PROGRESS } from "@/tools/utils/generator"; import { ProgressTracker } from "@/tools/utils/progress-tracker"; import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; +import { mkdir } from "node:fs/promises"; +import { z } from "zod"; /** * Schema for create-widget tool input. @@ -110,10 +110,34 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) "", `Location: ${widgetPath}`, "", - "Next steps:", + "=== TO IMPLEMENT WIDGET FUNCTIONALITY ===", + "", + "1. FETCH GUIDELINES (MCP Resources):", + " - mendix://guidelines/frontend (CSS/SCSS, Atlas UI, naming conventions)", + " - mendix://guidelines/implementation (step-by-step widget development)", + " - mendix://guidelines/backend-structure (Mendix data API: EditableValue, ActionValue)", + "", + "2. EXPLORE WIDGET STRUCTURE:", + ` Use list-widget-files tool with widgetPath: "${widgetPath}"`, + "", + "3. READ EXISTING CODE:", + ` Use read-widget-file tool to inspect:`, + ` - src/${options.name}.tsx (main component entry point)`, + ` - src/${options.name}.xml (widget properties definition)`, + ` - src/components/ (UI components - create if needed)`, + "", + "4. IMPLEMENT CHANGES:", + ` Use write-widget-file tool to create/update files`, + "", + "=== KEY FILES ===", + `- ${widgetPath}/src/${options.name}.tsx - Main widget component`, + `- ${widgetPath}/src/${options.name}.xml - Properties configuration`, + `- ${widgetPath}/src/${options.name}.editorPreview.tsx - Studio Pro preview`, + "", + "=== BUILD & TEST ===", `1. cd ${widgetPath}`, "2. pnpm install", - "3. pnpm start (to build and watch for changes)", + "3. pnpm start (builds and watches for changes)", "", "The widget will be available in Mendix Studio Pro after syncing the app directory." ].join("\n") diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts index d6152428a4..c791191739 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -1,4 +1,4 @@ -import * as pty from "node-pty"; +import type * as NodePty from "node-pty"; import { GENERATIONS_DIR, SCAFFOLD_TIMEOUT_MS } from "@/config"; import { DEFAULT_WIDGET_OPTIONS, type WidgetOptions, type WidgetOptionsInput } from "@/tools/types"; import { ProgressTracker } from "./progress-tracker"; @@ -143,6 +143,38 @@ export function cleanTerminalOutput(data: string): string { ); } +type NodePtyModule = typeof NodePty; + +async function loadNodePty(): Promise { + try { + // NOTE: node-pty is a native addon. Import it lazily so the MCP server can still start + // in environments where the addon is not available/built (e.g. missing toolchain). + const mod: any = await import("node-pty"); + const pty = (mod?.default ?? mod) as NodePtyModule; + + if (typeof pty?.spawn !== "function") { + throw new Error("node-pty loaded but does not expose spawn()"); + } + + return pty; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + + throw new Error( + [ + "Failed to load `node-pty` (native addon). This is required for the `create-widget` tool.", + "", + "Fix (macOS):", + "- Install Xcode Command Line Tools: `xcode-select --install`", + "- Rebuild the addon: `pnpm -w rebuild node-pty` (or run it from the repo root)", + "- If you're on Node.js 22+, consider upgrading `node-pty` to a version that supports your Node version", + "", + `Original error: ${message}` + ].join("\n") + ); + } +} + /** * Handles generator output and sends answers when prompts are detected. * Uses a larger buffer and improved logging for reliability. @@ -202,11 +234,12 @@ function handleGeneratorOutput( * @param tracker - Progress tracker for notifications * @param outputDir - Directory where the widget will be created (defaults to GENERATIONS_DIR) */ -export function runWidgetGenerator( +export async function runWidgetGenerator( options: WidgetOptions, tracker: ProgressTracker, outputDir: string = GENERATIONS_DIR ): Promise { + const pty = await loadNodePty(); const answers = buildGeneratorAnswers(options, outputDir); return new Promise((resolve, reject) => { From 1338456a3cb5f9e1cf5219e08d87cd2cef4b9296 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 14 Jan 2026 09:45:12 +0100 Subject: [PATCH 04/36] feat(pluggable-widgets-mcp): addition of resources and build tools, default to stdio --- package.json | 1 + packages/pluggable-widgets-mcp/.prettierrc.js | 1 - packages/pluggable-widgets-mcp/README.md | 71 ++- .../docs/property-types.md | 588 ++++++++++++++++++ .../docs/widget-patterns.md | 556 +++++++++++++++++ packages/pluggable-widgets-mcp/package.json | 7 +- .../pluggable-widgets-mcp/src/api/handlers.ts | 0 packages/pluggable-widgets-mcp/src/config.ts | 6 +- .../src/generators/types.ts | 161 +++++ .../src/generators/xml-generator.ts | 256 ++++++++ packages/pluggable-widgets-mcp/src/index.ts | 21 +- .../src/resources/guidelines.ts | 42 +- .../pluggable-widgets-mcp/src/server/http.ts | 10 +- .../src/server/routes.ts | 24 +- .../src/server/server.ts | 23 +- .../src/tools/build.tools.ts | 442 +++++++++++++ .../src/tools/file-operations.tools.ts | 130 +++- .../pluggable-widgets-mcp/src/tools/index.ts | 26 +- .../src/tools/scaffolding.tools.ts | 65 +- .../pluggable-widgets-mcp/src/tools/types.ts | 21 +- .../src/tools/utils/generator.ts | 14 +- .../src/tools/utils/response.ts | 103 +++ pnpm-lock.yaml | 158 ++--- 23 files changed, 2477 insertions(+), 249 deletions(-) delete mode 100644 packages/pluggable-widgets-mcp/.prettierrc.js create mode 100644 packages/pluggable-widgets-mcp/docs/property-types.md create mode 100644 packages/pluggable-widgets-mcp/docs/widget-patterns.md delete mode 100644 packages/pluggable-widgets-mcp/src/api/handlers.ts create mode 100644 packages/pluggable-widgets-mcp/src/generators/types.ts create mode 100644 packages/pluggable-widgets-mcp/src/generators/xml-generator.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/build.tools.ts diff --git a/package.json b/package.json index 655c02503a..741f82e8c2 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "create-gh-release": "turbo run create-gh-release --concurrency 1", "create-translation": "turbo run create-translation", "include-oss-in-artifact": "pnpm --filter @mendix/automation-utils run include-oss-in-artifact", + "start:mcp": "pnpm --filter pluggable-widgets-mcp run start", "lint": "turbo run lint --continue --concurrency 1", "merge-changelogs-pr": "pnpm --filter @mendix/automation-utils run merge-changelogs-pr", "oss-clearance": "pnpm --filter @mendix/automation-utils run oss-clearance", diff --git a/packages/pluggable-widgets-mcp/.prettierrc.js b/packages/pluggable-widgets-mcp/.prettierrc.js deleted file mode 100644 index 0892704ab0..0000000000 --- a/packages/pluggable-widgets-mcp/.prettierrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("@mendix/prettier-config-web-widgets"); diff --git a/packages/pluggable-widgets-mcp/README.md b/packages/pluggable-widgets-mcp/README.md index d3c8baeded..20d3555f3f 100644 --- a/packages/pluggable-widgets-mcp/README.md +++ b/packages/pluggable-widgets-mcp/README.md @@ -8,10 +8,26 @@ A Model Context Protocol (MCP) server that enables AI assistants to scaffold Men ```bash pnpm install +pnpm build # Build the server pnpm start # HTTP mode (default) pnpm start:stdio # STDIO mode ``` +## Global Installation + +For use with MCP clients (Cursor, Claude Desktop, LMStudio), install globally: + +```bash +# Build first +pnpm build + +# Link globally using npm (NOT pnpm - better MCP client compatibility) +npm link + +# Verify installation +which pluggable-widgets-mcp +``` + ## Transport Modes ### HTTP Mode (default) @@ -53,6 +69,21 @@ pnpm start:stdio **_Some client setups like Claude Desktop support STDIO only (for now)_** +**Option 1: Global command (after `npm link`)** + +```json +{ + "mcpServers": { + "pluggable-widgets-mcp": { + "command": "pluggable-widgets-mcp", + "args": ["stdio"] + } + } +} +``` + +**Option 2: Absolute path (more reliable during development)** + ```json { "mcpServers": { @@ -64,6 +95,8 @@ pnpm start:stdio } ``` +> **Note:** After rebuilding the server, you may need to restart/reconnect your MCP client to pick up changes. + ## Available Tools ### create-widget @@ -85,6 +118,34 @@ Scaffolds a new Mendix pluggable widget using `@mendix/generator-widget`. Generated widgets are placed in `generations/` directory within this package. +### File Operation Tools + +| Tool | Description | +| -------------------------- | ------------------------------------------------------------ | +| `list-widget-files` | Lists all files in a widget directory, grouped by type | +| `read-widget-file` | Reads the contents of a file from a widget directory | +| `write-widget-file` | Writes content to a file (creates parent dirs automatically) | +| `batch-write-widget-files` | Writes multiple files atomically | + +**Security:** Path traversal is blocked; only allowed extensions: `.tsx`, `.ts`, `.xml`, `.scss`, `.css`, `.json`, `.md` + +### build-widget + +Builds a widget using `pluggable-widgets-tools`, producing an `.mpk` file. + +| Parameter | Required | Description | +| ------------ | -------- | ------------------------------------- | +| `widgetPath` | Yes | Absolute path to the widget directory | + +Returns structured errors for TypeScript, XML, or dependency issues. + +## Available Resources + +| URI | Description | +| ------------------------------------- | -------------------------------------------------------------------------- | +| `mendix://guidelines/property-types` | Complete reference for all Mendix widget property types | +| `mendix://guidelines/widget-patterns` | Reusable patterns for common widget types (Button, Input, Container, etc.) | + ## Development ```bash @@ -135,13 +196,15 @@ This is useful for verifying tool behavior without needing a full AI client inte ## Roadmap -- [x] Widget scaffolding +- [x] Widget scaffolding (`create-widget`) - [x] HTTP transport - [x] STDIO transport - [x] Progress notifications -- [ ] Widget editing and modification -- [ ] Property management -- [ ] Build and deployment tools +- [x] File operations (list, read, write, batch-write) +- [x] Build tool (`build-widget`) +- [x] Guideline resources (property-types, widget-patterns) +- [ ] Widget property editing (XML manipulation) +- [ ] TypeScript error recovery suggestions ## License diff --git a/packages/pluggable-widgets-mcp/docs/property-types.md b/packages/pluggable-widgets-mcp/docs/property-types.md new file mode 100644 index 0000000000..c2936c7890 --- /dev/null +++ b/packages/pluggable-widgets-mcp/docs/property-types.md @@ -0,0 +1,588 @@ +# Mendix Widget Property Types Reference + +This document defines all available property types for Mendix pluggable widgets. Use this reference when defining properties in the JSON schema for XML generation. + +## Property Definition Schema + +When defining properties for the XML generator, use this JSON structure: + +```json +{ + "key": "propertyName", + "type": "string", + "caption": "Display Caption", + "description": "Optional description shown in Studio Pro", + "required": false, + "defaultValue": "optional default" +} +``` + +--- + +## Basic Types + +### string + +Simple text input. + +```json +{ + "key": "label", + "type": "string", + "caption": "Label", + "description": "Text label for the widget", + "defaultValue": "Click me" +} +``` + +**XML Output:** + +```xml + + Label + Text label for the widget + +``` + +--- + +### boolean + +True/false toggle. + +```json +{ + "key": "showIcon", + "type": "boolean", + "caption": "Show icon", + "description": "Display an icon next to the text", + "defaultValue": true +} +``` + +**XML Output:** + +```xml + + Show icon + Display an icon next to the text + +``` + +--- + +### integer + +Whole number input. + +```json +{ + "key": "maxItems", + "type": "integer", + "caption": "Maximum items", + "description": "Maximum number of items to display", + "defaultValue": 10 +} +``` + +**XML Output:** + +```xml + + Maximum items + Maximum number of items to display + +``` + +--- + +### decimal + +Decimal number input. + +```json +{ + "key": "opacity", + "type": "decimal", + "caption": "Opacity", + "description": "Opacity level (0-1)", + "defaultValue": 0.8 +} +``` + +--- + +## Text Types + +### textTemplate + +Text with parameter substitution. Allows dynamic text with placeholders. + +```json +{ + "key": "legend", + "type": "textTemplate", + "caption": "Legend", + "description": "Text template with parameters", + "required": false +} +``` + +**XML Output:** + +```xml + + Legend + Text template with parameters + +``` + +--- + +### expression + +Dynamic expression that can reference attributes and return computed values. + +```json +{ + "key": "visibleExpression", + "type": "expression", + "caption": "Visible", + "description": "Expression to determine visibility", + "defaultValue": "true" +} +``` + +**With return type:** + +```json +{ + "key": "valueExpression", + "type": "expression", + "caption": "Value", + "returnType": "String" +} +``` + +**XML Output (with returnType):** + +```xml + + Value + + +``` + +--- + +## Action Types + +### action + +Event handler that triggers actions (microflows, nanoflows, etc.). + +```json +{ + "key": "onClick", + "type": "action", + "caption": "On click", + "description": "Action to execute when clicked", + "required": false +} +``` + +**XML Output:** + +```xml + + On click + Action to execute when clicked + +``` + +--- + +## Data Types + +### attribute + +Links to an entity attribute. Must specify allowed attribute types. + +```json +{ + "key": "value", + "type": "attribute", + "caption": "Value", + "description": "Attribute to store the value", + "required": true, + "attributeTypes": ["String"] +} +``` + +**Multiple attribute types:** + +```json +{ + "key": "numberValue", + "type": "attribute", + "attributeTypes": ["Integer", "Decimal", "Long"] +} +``` + +**XML Output:** + +```xml + + Value + Attribute to store the value + + + + +``` + +**Valid attributeTypes:** + +- `String` +- `Integer` +- `Long` +- `Decimal` +- `Boolean` +- `DateTime` +- `Enum` +- `HashString` +- `Binary` +- `AutoNumber` + +--- + +### datasource + +Data source for list-based widgets. + +```json +{ + "key": "dataSource", + "type": "datasource", + "caption": "Data source", + "description": "Source of items to display", + "isList": true, + "required": false +} +``` + +**XML Output:** + +```xml + + Data source + Source of items to display + +``` + +--- + +### association + +Links to an entity association. + +```json +{ + "key": "parent", + "type": "association", + "caption": "Parent association", + "required": false +} +``` + +--- + +### entity + +Entity selector. + +```json +{ + "key": "targetEntity", + "type": "entity", + "caption": "Target entity" +} +``` + +--- + +## Selection Types + +### enumeration + +Dropdown with predefined options. Must include `enumValues` array. + +```json +{ + "key": "alignment", + "type": "enumeration", + "caption": "Alignment", + "defaultValue": "left", + "enumValues": [ + { "key": "left", "caption": "Left" }, + { "key": "center", "caption": "Center" }, + { "key": "right", "caption": "Right" } + ] +} +``` + +**XML Output:** + +```xml + + Alignment + + Left + Center + Right + + +``` + +--- + +### icon + +Icon picker. + +```json +{ + "key": "icon", + "type": "icon", + "caption": "Icon", + "required": false +} +``` + +--- + +### image + +Image picker. + +```json +{ + "key": "image", + "type": "image", + "caption": "Image", + "required": false +} +``` + +--- + +### file + +File selector. + +```json +{ + "key": "document", + "type": "file", + "caption": "Document" +} +``` + +--- + +## Container Types + +### widgets + +Container for child widgets. Used to create widget slots. + +```json +{ + "key": "content", + "type": "widgets", + "caption": "Content", + "description": "Widgets to display inside" +} +``` + +**With datasource reference:** + +```json +{ + "key": "content", + "type": "widgets", + "caption": "Content", + "dataSource": "dataSource" +} +``` + +**XML Output:** + +```xml + + Content + Widgets to display inside + +``` + +--- + +### object + +Complex nested property with sub-properties. Used for repeating structures. + +```json +{ + "key": "columns", + "type": "object", + "caption": "Columns", + "isList": true, + "properties": [ + { + "key": "header", + "type": "textTemplate", + "caption": "Header" + }, + { + "key": "width", + "type": "integer", + "caption": "Width", + "defaultValue": 100 + } + ] +} +``` + +**XML Output:** + +```xml + + Columns + + + + Header + + + Width + + + + +``` + +--- + +## System Properties + +System properties are predefined by Mendix. Reference them by key only. + +```json +{ + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +**Available system properties:** + +- `Name` - Widget name in Studio Pro +- `TabIndex` - Tab order for accessibility +- `Visibility` - Conditional visibility settings + +**XML Output:** + +```xml + + + + + + + +``` + +--- + +## Property Groups + +Properties can be organized into groups for better Studio Pro UI. + +```json +{ + "propertyGroups": [ + { + "caption": "General", + "properties": ["label", "showIcon"] + }, + { + "caption": "Events", + "properties": ["onClick", "onHover"] + } + ] +} +``` + +--- + +## Full Widget Definition Example + +```json +{ + "name": "TooltipButton", + "description": "A button with tooltip on hover", + "properties": [ + { + "key": "buttonText", + "type": "textTemplate", + "caption": "Button text", + "description": "Text to display on the button" + }, + { + "key": "tooltipText", + "type": "textTemplate", + "caption": "Tooltip text", + "description": "Text to show on hover" + }, + { + "key": "buttonStyle", + "type": "enumeration", + "caption": "Style", + "defaultValue": "primary", + "enumValues": [ + { "key": "primary", "caption": "Primary" }, + { "key": "secondary", "caption": "Secondary" }, + { "key": "danger", "caption": "Danger" } + ] + }, + { + "key": "onClick", + "type": "action", + "caption": "On click", + "required": false + } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +--- + +## Type Quick Reference + +| Type | Use Case | Requires | +| -------------- | ---------------- | ----------------------- | +| `string` | Simple text | - | +| `boolean` | Toggle | `defaultValue` | +| `integer` | Whole numbers | - | +| `decimal` | Decimal numbers | - | +| `textTemplate` | Dynamic text | - | +| `expression` | Computed values | `returnType` (optional) | +| `action` | Event handlers | - | +| `attribute` | Entity binding | `attributeTypes` | +| `datasource` | List data | `isList: true` | +| `enumeration` | Dropdown | `enumValues` | +| `widgets` | Child widgets | - | +| `object` | Nested structure | `properties`, `isList` | +| `icon` | Icon picker | - | +| `image` | Image picker | - | +| `association` | Entity relation | - | diff --git a/packages/pluggable-widgets-mcp/docs/widget-patterns.md b/packages/pluggable-widgets-mcp/docs/widget-patterns.md new file mode 100644 index 0000000000..6cf76aa6c4 --- /dev/null +++ b/packages/pluggable-widgets-mcp/docs/widget-patterns.md @@ -0,0 +1,556 @@ +# Mendix Widget Patterns + +This document provides reusable patterns for common widget types. Use these as templates when implementing widget components. + +--- + +## Pattern: Display Widget + +Display widgets show read-only data. Examples: Badge, Progress Bar, Label. + +### Typical Properties + +```json +{ + "properties": [ + { "key": "value", "type": "textTemplate", "caption": "Value" }, + { "key": "type", "type": "enumeration", "caption": "Style", "enumValues": [...] }, + { "key": "onClick", "type": "action", "caption": "On click", "required": false } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +### TSX Structure + +```tsx +import { ReactNode, useCallback } from "react"; +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { MyWidgetContainerProps } from "../typings/MyWidgetProps"; +import "./ui/MyWidget.scss"; + +export default function MyWidget(props: MyWidgetContainerProps): ReactNode { + const { value, type, onClick, tabIndex, class: className, style } = props; + + const handleClick = useCallback(() => { + executeAction(onClick); + }, [onClick]); + + const isClickable = onClick?.canExecute; + + return ( +
+ {value?.value ?? ""} +
+ ); +} +``` + +### SCSS Structure + +```scss +.widget-mywidget { + display: inline-block; + padding: 4px 8px; + border-radius: 4px; + + &-primary { + background-color: var(--brand-primary); + color: white; + } + + &-secondary { + background-color: var(--brand-secondary); + color: white; + } +} +``` + +--- + +## Pattern: Button Widget + +Button widgets trigger actions on click. May include icons, loading states. + +### Typical Properties + +```json +{ + "properties": [ + { "key": "caption", "type": "textTemplate", "caption": "Caption" }, + { "key": "icon", "type": "icon", "caption": "Icon", "required": false }, + { + "key": "buttonStyle", + "type": "enumeration", + "caption": "Style", + "defaultValue": "primary", + "enumValues": [ + { "key": "primary", "caption": "Primary" }, + { "key": "secondary", "caption": "Secondary" }, + { "key": "danger", "caption": "Danger" } + ] + }, + { "key": "onClick", "type": "action", "caption": "On click" } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +### TSX Structure + +```tsx +import { ReactNode, useCallback, KeyboardEvent } from "react"; +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { MyButtonContainerProps } from "../typings/MyButtonProps"; +import "./ui/MyButton.scss"; + +export default function MyButton(props: MyButtonContainerProps): ReactNode { + const { caption, icon, buttonStyle, onClick, tabIndex, class: className, style } = props; + + const handleClick = useCallback(() => { + executeAction(onClick); + }, [onClick]); + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + handleClick(); + } + }, + [handleClick] + ); + + const isDisabled = !onClick?.canExecute; + + return ( + + ); +} +``` + +### SCSS Structure + +```scss +.widget-mybutton { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &-primary { + background-color: var(--brand-primary); + color: white; + } + + &-secondary { + background-color: transparent; + border: 1px solid var(--brand-primary); + color: var(--brand-primary); + } + + &-danger { + background-color: var(--brand-danger); + color: white; + } +} +``` + +--- + +## Pattern: Input Widget + +Input widgets bind to entity attributes for data entry. + +### Typical Properties + +```json +{ + "properties": [ + { + "key": "value", + "type": "attribute", + "caption": "Value", + "attributeTypes": ["String"], + "required": true + }, + { "key": "placeholder", "type": "textTemplate", "caption": "Placeholder", "required": false }, + { "key": "readOnly", "type": "boolean", "caption": "Read-only", "defaultValue": false }, + { "key": "onChange", "type": "action", "caption": "On change", "required": false }, + { "key": "onEnter", "type": "action", "caption": "On enter", "required": false } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +### TSX Structure + +```tsx +import { ReactNode, useCallback, ChangeEvent, KeyboardEvent } from "react"; +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { MyInputContainerProps } from "../typings/MyInputProps"; +import "./ui/MyInput.scss"; + +export default function MyInput(props: MyInputContainerProps): ReactNode { + const { value, placeholder, readOnly, onChange, onEnter, tabIndex, class: className, style } = props; + + const handleChange = useCallback( + (event: ChangeEvent) => { + if (value?.status === "available" && !value.readOnly) { + value.setValue(event.target.value); + executeAction(onChange); + } + }, + [value, onChange] + ); + + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === "Enter") { + executeAction(onEnter); + } + }, + [onEnter] + ); + + const isReadOnly = readOnly || value?.readOnly; + + return ( + + ); +} +``` + +### SCSS Structure + +```scss +.widget-myinput { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--border-color); + border-radius: 4px; + font-size: 14px; + + &:focus { + outline: none; + border-color: var(--brand-primary); + box-shadow: 0 0 0 2px rgba(var(--brand-primary-rgb), 0.2); + } + + &:read-only { + background-color: var(--bg-color-secondary); + } +} +``` + +--- + +## Pattern: Container Widget + +Container widgets hold child widgets. Examples: Fieldset, Card, Accordion. + +### Typical Properties + +```json +{ + "properties": [ + { "key": "content", "type": "widgets", "caption": "Content" }, + { "key": "header", "type": "textTemplate", "caption": "Header", "required": false }, + { "key": "collapsible", "type": "boolean", "caption": "Collapsible", "defaultValue": false } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} +``` + +### TSX Structure + +```tsx +import { ReactNode, useState, useCallback } from "react"; +import { MyContainerContainerProps } from "../typings/MyContainerProps"; +import "./ui/MyContainer.scss"; + +export default function MyContainer(props: MyContainerContainerProps): ReactNode { + const { content, header, collapsible, tabIndex, class: className, style } = props; + const [isOpen, setIsOpen] = useState(true); + + const handleToggle = useCallback(() => { + if (collapsible) { + setIsOpen(prev => !prev); + } + }, [collapsible]); + + return ( +
+ {header?.value && ( +
+ {header.value} + {collapsible && } +
+ )} + {isOpen &&
{content}
} +
+ ); +} +``` + +### SCSS Structure + +```scss +.widget-mycontainer { + border: 1px solid var(--border-color); + border-radius: 4px; + overflow: hidden; + + &-header { + padding: 12px 16px; + background-color: var(--bg-color-secondary); + font-weight: 600; + display: flex; + justify-content: space-between; + align-items: center; + + &[role="button"] { + cursor: pointer; + } + } + + &-toggle { + transition: transform 0.2s; + + &.open { + transform: rotate(180deg); + } + } + + &-content { + padding: 16px; + } +} +``` + +--- + +## Pattern: Data List Widget + +Data list widgets display items from a datasource. + +### Typical Properties + +```json +{ + "properties": [ + { "key": "dataSource", "type": "datasource", "caption": "Data source", "isList": true }, + { "key": "content", "type": "widgets", "caption": "Content", "dataSource": "dataSource" }, + { "key": "emptyMessage", "type": "textTemplate", "caption": "Empty message", "required": false }, + { "key": "onItemClick", "type": "action", "caption": "On item click", "required": false } + ], + "systemProperties": ["Name", "Visibility"] +} +``` + +### TSX Structure + +```tsx +import { ReactNode, useCallback } from "react"; +import { ValueStatus, ObjectItem } from "mendix"; +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { MyListContainerProps } from "../typings/MyListProps"; +import "./ui/MyList.scss"; + +export default function MyList(props: MyListContainerProps): ReactNode { + const { dataSource, content, emptyMessage, onItemClick, class: className, style } = props; + + // Loading state + if (dataSource?.status !== ValueStatus.Available) { + return ( +
+ Loading... +
+ ); + } + + const items = dataSource?.items ?? []; + + // Empty state + if (items.length === 0) { + return ( +
+ {emptyMessage?.value ?? "No items"} +
+ ); + } + + return ( +
+ {items.map((item: ObjectItem) => ( +
executeAction(onItemClick)}> + {content?.get(item)} +
+ ))} +
+ ); +} +``` + +### SCSS Structure + +```scss +.widget-mylist { + display: flex; + flex-direction: column; + gap: 8px; + + &-loading, + &-empty { + padding: 16px; + text-align: center; + color: var(--text-color-secondary); + } + + &-item { + padding: 12px; + border: 1px solid var(--border-color); + border-radius: 4px; + + &:hover { + background-color: var(--bg-color-hover); + } + } +} +``` + +--- + +## Common Imports + +Every widget typically needs these imports: + +```tsx +// React +import { ReactNode, useCallback, useState } from "react"; + +// Mendix helpers +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { ValueStatus } from "mendix"; + +// Generated types (from XML via pwt build) +import { MyWidgetContainerProps } from "../typings/MyWidgetProps"; + +// Styles +import "./ui/MyWidget.scss"; +``` + +--- + +## Key Patterns + +### Action Execution + +Always use `executeAction` from the platform helpers: + +```tsx +import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; + +const handleClick = useCallback(() => { + executeAction(props.onClick); +}, [props.onClick]); + +// Check if action can execute +const isClickable = props.onClick?.canExecute; +``` + +### Attribute Value Handling + +Check status before reading/writing: + +```tsx +// Reading +const displayValue = props.value?.value ?? ""; + +// Writing +if (props.value?.status === "available" && !props.value.readOnly) { + props.value.setValue(newValue); +} +``` + +### Loading States + +Handle datasource loading: + +```tsx +if (props.dataSource?.status !== ValueStatus.Available) { + return
Loading...
; +} +``` + +### Accessibility + +Always include proper accessibility attributes: + +```tsx + + ); +} +`; +} + +/** + * Generates the Input pattern component. + */ +function generateInputPattern(widgetName: string, properties: PropertyDefinition[]): string { + const imports = generateImports(widgetName, properties, "input"); + + // Find relevant properties + const attributeProps = properties.filter(p => p.type === "attribute"); + const mainAttribute = attributeProps[0]; + const actionProps = properties.filter(p => p.type === "action"); + const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); + + // Generate destructuring + const allProps = [...attributeProps, ...actionProps, ...textProps]; + const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; + + // Determine input type based on attribute type + const attrType = mainAttribute?.attributeTypes?.[0] ?? "String"; + let inputType = "text"; + if (attrType === "Integer" || attrType === "Long" || attrType === "Decimal") { + inputType = "number"; + } else if (attrType === "Boolean") { + inputType = "checkbox"; + } + + const mainKey = mainAttribute?.key ?? "value"; + + // Find change action + const changeAction = actionProps.find(p => p.key.toLowerCase().includes("change")); + const changeHandler = changeAction ? true : false; + + // Determine if we need Big conversion for numeric attributes + const usesBig = inputType === "number"; + const valueExtraction = usesBig ? `${mainKey}?.value?.toNumber() ?? 0` : `${mainKey}?.value ?? ""`; + const valueConversion = usesBig ? `new Big(Number(event.target.value))` : `event.target.value`; + + return `${imports} + +export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { + const { ${propsToDestructure.join(", ")} } = props; + + const currentValue = ${valueExtraction}; + const isReadOnly = ${mainKey}?.readOnly ?? false; + + const handleInputChange = useCallback((event: React.ChangeEvent) => { + if (${mainKey}?.status === "available" && !${mainKey}.readOnly) { + ${mainKey}.setValue(${valueConversion}); + }${changeHandler ? `\n executeAction(${changeAction?.key});` : ""} + }, [${mainKey}${changeHandler ? `, ${changeAction?.key}` : ""}]); + + return ( + + ); +} +`; +} + +/** + * Generates the Container pattern component. + */ +function generateContainerPattern(widgetName: string, properties: PropertyDefinition[]): string { + const imports = generateImports(widgetName, properties, "container"); + + // Find relevant properties + const widgetProps = properties.filter(p => p.type === "widgets"); + const mainContent = widgetProps[0]; + const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); + const headerProp = textProps.find(p => p.key === "header" || p.key === "title") || textProps[0]; + const boolProps = properties.filter(p => p.type === "boolean"); + const collapsibleProp = boolProps.find(p => p.key === "collapsible"); + + // Generate destructuring + const allProps = [...widgetProps, ...textProps, ...boolProps]; + const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; + + const contentKey = mainContent?.key ?? "content"; + const headerValue = headerProp ? `${headerProp.key}?.value` : "undefined"; + const isCollapsible = collapsibleProp?.key ?? "false"; + + return `${imports} + +export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { + const { ${propsToDestructure.join(", ")} } = props; + + const [isOpen, setIsOpen] = useState(true); + + const handleToggle = useCallback(() => { + if (${isCollapsible}) { + setIsOpen(prev => !prev); + } + }, [${isCollapsible}]); + + const headerValue = ${headerValue}; + + return ( +
+ {headerValue && ( +
+ {headerValue} + {${isCollapsible} && } +
+ )} + {isOpen &&
{${contentKey}}
} +
+ ); +} +`; +} + +/** + * Generates the Data List pattern component. + */ +function generateDataListPattern(widgetName: string, properties: PropertyDefinition[]): string { + const imports = generateImports(widgetName, properties, "dataList"); + + // Find relevant properties + const datasourceProp = properties.find(p => p.type === "datasource"); + const widgetProps = properties.filter(p => p.type === "widgets"); + const contentProp = widgetProps.find(p => p.dataSource) || widgetProps[0]; + const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); + const emptyMessageProp = textProps.find(p => p.key.toLowerCase().includes("empty")); + const actionProps = properties.filter(p => p.type === "action"); + const itemClickAction = actionProps.find(p => p.key.toLowerCase().includes("item")); + + // Generate destructuring + const allProps = [datasourceProp, contentProp, emptyMessageProp, itemClickAction].filter( + Boolean + ) as PropertyDefinition[]; + const propsToDestructure = ["class: className", "style", ...allProps.map(p => p.key)]; + + // Generate action handlers + const actionHandlers = itemClickAction ? [generateActionHandler(itemClickAction)] : []; + + const dsKey = datasourceProp?.key ?? "dataSource"; + const contentKey = contentProp?.key ?? "content"; + const emptyMessage = emptyMessageProp ? `${emptyMessageProp.key}?.value ?? "No items"` : '"No items"'; + const itemHandler = itemClickAction + ? `handle${itemClickAction.key.charAt(0).toUpperCase() + itemClickAction.key.slice(1)}` + : "undefined"; + + return `${imports} +import { ObjectItem } from "mendix"; + +export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { + const { ${propsToDestructure.join(", ")} } = props; + + ${actionHandlers.join("\n\n ")} + + // Loading state + if (${dsKey}?.status !== ValueStatus.Available) { + return ( +
+ Loading... +
+ ); + } + + const items = ${dsKey}?.items ?? []; + + // Empty state + if (items.length === 0) { + return ( +
+ {${emptyMessage}} +
+ ); + } + + return ( +
+ {items.map((item: ObjectItem) => ( +
+ {${contentKey}?.get(item)} +
+ ))} +
+ ); +} +`; +} + +/** + * Generates the complete widget TSX from a widget definition. + */ +export function generateWidgetTsx( + widgetName: string, + properties: PropertyDefinition[], + pattern?: WidgetPattern +): TsxGeneratorResult { + try { + // Detect pattern if not specified + const detectedPattern = pattern ?? detectWidgetPattern(properties); + + let mainComponent: string; + + switch (detectedPattern) { + case "display": + mainComponent = generateDisplayPattern(widgetName, properties); + break; + case "button": + mainComponent = generateButtonPattern(widgetName, properties); + break; + case "input": + mainComponent = generateInputPattern(widgetName, properties); + break; + case "container": + mainComponent = generateContainerPattern(widgetName, properties); + break; + case "dataList": + mainComponent = generateDataListPattern(widgetName, properties); + break; + default: + mainComponent = generateDisplayPattern(widgetName, properties); + } + + return { + success: true, + mainComponent, + pattern: detectedPattern + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error) + }; + } +} diff --git a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts new file mode 100644 index 0000000000..a458c7dd96 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts @@ -0,0 +1,459 @@ +/** + * Code Generation Tools for Mendix Pluggable Widgets. + * + * Provides the `generate-widget-code` tool that transforms widget descriptions + * and property definitions into working XML and TSX code. + */ + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { mkdir, stat, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +import { z } from "zod"; +import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; +import { detectWidgetPattern, generateWidgetTsx, type WidgetPattern } from "@/generators/tsx-generator"; +import type { PropertyDefinition, WidgetDefinition } from "@/generators/types"; +import { validateFilePath } from "@/security"; +import type { ToolResponse } from "@/tools/types"; +import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; + +// ============================================================================= +// Schemas +// ============================================================================= + +/** + * Schema for enumeration values. + */ +const enumValueSchema = z.object({ + key: z.string().min(1).describe("Unique identifier for this enum value"), + caption: z.string().min(1).describe("Display caption shown in Studio Pro") +}); + +/** + * Schema for property definitions. + * Matches the PropertyDefinition type from generators/types.ts + */ +const propertyDefinitionSchema = z.object({ + key: z + .string() + .min(1) + .regex(/^[a-z][a-zA-Z0-9]*$/, "Must be camelCase (e.g., 'myProperty')") + .describe("Property key in camelCase"), + type: z + .enum([ + "string", + "boolean", + "integer", + "decimal", + "textTemplate", + "expression", + "action", + "attribute", + "datasource", + "association", + "entity", + "enumeration", + "icon", + "image", + "file", + "widgets", + "object" + ]) + .describe("Mendix property type"), + caption: z.string().min(1).describe("Display caption shown in Studio Pro"), + description: z.string().optional().describe("Help text shown in Studio Pro"), + required: z.boolean().optional().describe("Whether this property is required"), + defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for this property"), + enumValues: z.array(enumValueSchema).optional().describe("Allowed values for enumeration type"), + attributeTypes: z + .array( + z.enum([ + "String", + "Integer", + "Long", + "Decimal", + "Boolean", + "DateTime", + "Enum", + "HashString", + "Binary", + "AutoNumber" + ]) + ) + .optional() + .describe("Allowed attribute types for attribute property"), + isList: z.boolean().optional().describe("Whether datasource returns a list"), + dataSource: z.string().optional().describe("Reference to datasource property key (for widgets type)"), + returnType: z + .enum(["String", "Integer", "Decimal", "Boolean", "DateTime"]) + .optional() + .describe("Return type for expression property") +}); + +/** + * Schema for the generate-widget-code tool input. + */ +const generateWidgetCodeSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the scaffolded widget directory"), + description: z.string().min(1).describe("Description of what the widget should do"), + properties: z + .array(propertyDefinitionSchema) + .optional() + .describe("Array of property definitions. If not provided, returns suggestions."), + widgetPattern: z + .enum(["display", "button", "input", "container", "dataList"]) + .optional() + .describe("Optional hint for TSX generation pattern") +}); + +type GenerateWidgetCodeInput = z.infer; + +// ============================================================================= +// Helper Functions +// ============================================================================= + +/** + * Extracts widget name from path (e.g., /path/to/MyWidget -> MyWidget) + */ +function extractWidgetName(widgetPath: string): string { + const base = basename(widgetPath); + // Convert to PascalCase if needed + return base.charAt(0).toUpperCase() + base.slice(1); +} + +/** + * Generates property suggestions based on widget description. + */ +function generatePropertySuggestions(description: string): string { + const descLower = description.toLowerCase(); + + // Common patterns to suggest + const suggestions: Array<{ + key: string; + type: string; + caption: string; + purpose: string; + }> = []; + + // Counter-like widgets + if (descLower.includes("counter") || descLower.includes("count") || descLower.includes("increment")) { + suggestions.push( + { + key: "value", + type: "attribute", + caption: "Value", + purpose: "Current counter value (bind to Integer attribute)" + }, + { key: "step", type: "integer", caption: "Step", purpose: "Amount to increment/decrement (default: 1)" }, + { key: "minValue", type: "integer", caption: "Minimum", purpose: "Lower bound (optional)" }, + { key: "maxValue", type: "integer", caption: "Maximum", purpose: "Upper bound (optional)" }, + { key: "onIncrement", type: "action", caption: "On Increment", purpose: "Action when value increases" }, + { key: "onDecrement", type: "action", caption: "On Decrement", purpose: "Action when value decreases" } + ); + } + + // Display/badge-like widgets + if ( + descLower.includes("display") || + descLower.includes("show") || + descLower.includes("badge") || + descLower.includes("label") + ) { + suggestions.push( + { key: "value", type: "textTemplate", caption: "Value", purpose: "Text to display" }, + { key: "type", type: "enumeration", caption: "Style", purpose: "Visual style variant" }, + { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } + ); + } + + // Button-like widgets + if (descLower.includes("button") || descLower.includes("click") || descLower.includes("trigger")) { + suggestions.push( + { key: "caption", type: "textTemplate", caption: "Caption", purpose: "Button text" }, + { key: "icon", type: "icon", caption: "Icon", purpose: "Button icon (optional)" }, + { key: "buttonStyle", type: "enumeration", caption: "Style", purpose: "Button appearance variant" }, + { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } + ); + } + + // Input-like widgets + if ( + descLower.includes("input") || + descLower.includes("edit") || + descLower.includes("enter") || + descLower.includes("form") + ) { + suggestions.push( + { key: "value", type: "attribute", caption: "Value", purpose: "Bound attribute for data entry" }, + { key: "placeholder", type: "textTemplate", caption: "Placeholder", purpose: "Hint text when empty" }, + { key: "onChange", type: "action", caption: "On Change", purpose: "Action when value changes" }, + { key: "onEnter", type: "action", caption: "On Enter", purpose: "Action when Enter key pressed" } + ); + } + + // List-like widgets + if ( + descLower.includes("list") || + descLower.includes("items") || + descLower.includes("collection") || + descLower.includes("data") + ) { + suggestions.push( + { key: "dataSource", type: "datasource", caption: "Data Source", purpose: "Source of items to display" }, + { key: "content", type: "widgets", caption: "Content", purpose: "Template for each item" }, + { key: "emptyMessage", type: "textTemplate", caption: "Empty Message", purpose: "Text when no items" }, + { key: "onItemClick", type: "action", caption: "On Item Click", purpose: "Action when item clicked" } + ); + } + + // Container-like widgets + if ( + descLower.includes("container") || + descLower.includes("card") || + descLower.includes("panel") || + descLower.includes("section") + ) { + suggestions.push( + { key: "content", type: "widgets", caption: "Content", purpose: "Child widgets" }, + { key: "header", type: "textTemplate", caption: "Header", purpose: "Container title" }, + { key: "collapsible", type: "boolean", caption: "Collapsible", purpose: "Allow expand/collapse" } + ); + } + + // Default suggestions if nothing matched + if (suggestions.length === 0) { + suggestions.push( + { key: "value", type: "textTemplate", caption: "Value", purpose: "Main display value" }, + { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } + ); + } + + // Detect pattern from suggestions + let suggestedPattern: WidgetPattern = "display"; + const types = suggestions.map(s => s.type); + if (types.includes("datasource") && types.includes("widgets")) { + suggestedPattern = "dataList"; + } else if (types.includes("widgets")) { + suggestedPattern = "container"; + } else if (types.includes("attribute")) { + suggestedPattern = "input"; + } else if (suggestions.length <= 4 && types.includes("action")) { + suggestedPattern = "button"; + } + + // Build markdown table + const table = [ + "| Property | Type | Caption | Purpose |", + "|----------|------|---------|---------|", + ...suggestions.map(s => `| ${s.key} | ${s.type} | ${s.caption} | ${s.purpose} |`) + ].join("\n"); + + return `📋 Widget requirements analysis needed + +Based on your description "${description}", suggested properties: + +${table} + +Suggested pattern: **${suggestedPattern}** (${getPatternDescription(suggestedPattern)}) + +Please call generate-widget-code again with the properties array to generate the widget code. + +Example: +\`\`\`json +{ + "widgetPath": "", + "description": "${description}", + "properties": [ + { "key": "value", "type": "textTemplate", "caption": "Value" }, + { "key": "onClick", "type": "action", "caption": "On Click" } + ] +} +\`\`\``; +} + +/** + * Returns a human-readable description of a widget pattern. + */ +function getPatternDescription(pattern: WidgetPattern): string { + switch (pattern) { + case "display": + return "read-only data display"; + case "button": + return "action trigger with click handler"; + case "input": + return "data entry with attribute binding"; + case "container": + return "holds child widgets"; + case "dataList": + return "renders items from datasource"; + default: + return "general purpose"; + } +} + +// ============================================================================= +// Tool Handler +// ============================================================================= + +async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise { + const { widgetPath, description, properties, widgetPattern } = args; + + try { + // Verify widget directory exists + const pathStats = await stat(widgetPath); + if (!pathStats.isDirectory()) { + return createErrorResponse(`Widget path is not a directory: ${widgetPath}`); + } + + // If no properties provided, return suggestions + if (!properties || properties.length === 0) { + console.error(`[code-generation] No properties provided, returning suggestions`); + return createToolResponse(generatePropertySuggestions(description)); + } + + // Extract widget name from path + const widgetName = extractWidgetName(widgetPath); + + console.error(`[code-generation] Generating code for ${widgetName} with ${properties.length} properties`); + + // Build widget definition for XML generator + const widgetDefinition: WidgetDefinition = { + name: widgetName, + description, + properties: properties as PropertyDefinition[], + systemProperties: ["Name", "TabIndex", "Visibility"] + }; + + // Validate widget definition + const validationErrors = validateWidgetDefinition(widgetDefinition); + if (validationErrors.length > 0) { + return createErrorResponse( + [ + "❌ Widget definition validation failed:", + "", + ...validationErrors.map(e => ` • ${e}`), + "", + "Please fix the above issues and try again." + ].join("\n") + ); + } + + // Generate XML + console.error(`[code-generation] Generating XML...`); + const xmlResult = generateWidgetXml(widgetDefinition); + if (!xmlResult.success || !xmlResult.xml) { + return createErrorResponse(`XML generation failed: ${xmlResult.error}`); + } + + // Detect or use provided pattern + const pattern = widgetPattern ?? detectWidgetPattern(properties as PropertyDefinition[]); + console.error(`[code-generation] Using pattern: ${pattern}`); + + // Generate TSX + console.error(`[code-generation] Generating TSX...`); + const tsxResult = generateWidgetTsx(widgetName, properties as PropertyDefinition[], pattern); + if (!tsxResult.success || !tsxResult.mainComponent) { + return createErrorResponse(`TSX generation failed: ${tsxResult.error}`); + } + + // Prepare files to write + const filesToWrite = [ + { path: `src/${widgetName}.xml`, content: xmlResult.xml }, + { path: `src/${widgetName}.tsx`, content: tsxResult.mainComponent } + ]; + + // Validate and write files + const writtenFiles: string[] = []; + for (const file of filesToWrite) { + try { + validateFilePath(widgetPath, file.path, true); + const fullPath = join(widgetPath, file.path); + + // Ensure parent directory exists + const parentDir = dirname(fullPath); + await mkdir(parentDir, { recursive: true }); + + // Write file + await writeFile(fullPath, file.content, "utf-8"); + writtenFiles.push(file.path); + console.error(`[code-generation] Wrote: ${fullPath}`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(`Failed to write ${file.path}: ${message}`); + } + } + + // Build success response + const propSummary = properties.map(p => p.key).join(", "); + + return createToolResponse( + [ + `✅ Widget code generated successfully!`, + "", + `📁 Files modified:`, + ` • src/${widgetName}.xml - Added ${properties.length} properties (${propSummary})`, + ` • src/${widgetName}.tsx - Implemented using ${pattern} pattern`, + "", + `🔨 Next steps:`, + ` 1. Run build-widget to compile and validate`, + ` 2. Review generated code for customization`, + ` 3. Test in Mendix Studio Pro` + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`[code-generation] Error: ${message}`); + return createErrorResponse(`Widget code generation failed: ${message}`); + } +} + +// ============================================================================= +// Tool Registration +// ============================================================================= + +const GENERATE_WIDGET_CODE_DESCRIPTION = `Generates XML properties and TSX component code for a Mendix pluggable widget. + +**Usage:** + +1. **With properties (generates code):** + Provide widgetPath, description, and properties array to generate XML + TSX files. + +2. **Without properties (gets suggestions):** + Provide only widgetPath and description to receive suggested properties based on your description. + +**Supported property types:** +- Basic: string, boolean, integer, decimal +- Dynamic: textTemplate, expression +- Interactive: action, attribute (for data binding) +- Complex: datasource, widgets (for containers/lists), enumeration + +**Pattern detection:** +The tool automatically detects the appropriate widget pattern (display, button, input, container, dataList) based on property types, or you can specify it explicitly. + +**Example - Counter widget:** +\`\`\`json +{ + "widgetPath": "/path/to/CounterWidget", + "description": "A counter that increments and decrements", + "properties": [ + { "key": "value", "type": "attribute", "caption": "Value", "attributeTypes": ["Integer"] }, + { "key": "onIncrement", "type": "action", "caption": "On Increment" } + ] +} +\`\`\``; + +/** + * Registers code generation tools for creating widget XML and TSX. + */ +export function registerCodeGenerationTools(server: McpServer): void { + server.registerTool( + "generate-widget-code", + { + title: "Generate Widget Code", + description: GENERATE_WIDGET_CODE_DESCRIPTION, + inputSchema: generateWidgetCodeSchema + }, + handleGenerateWidgetCode + ); + + console.error("[code-generation] Registered 1 tool"); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts index 584e00d6c2..31e508e651 100644 --- a/packages/pluggable-widgets-mcp/src/tools/index.ts +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -1,5 +1,6 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerBuildTools } from "./build.tools"; +import { registerCodeGenerationTools } from "./code-generation.tools"; import { registerFileOperationTools } from "./file-operations.tools"; import { registerScaffoldingTools } from "./scaffolding.tools"; @@ -10,6 +11,7 @@ import { registerScaffoldingTools } from "./scaffolding.tools"; * - Scaffolding: Widget creation (create-widget) * - File Operations: Read/write widget files (list-widget-files, read-widget-file, write-widget-file, batch-write-widget-files) * - Build: Widget building and validation (build-widget) + * - Code Generation: Generate widget XML and TSX (generate-widget-code) * * Each category registers its tools directly with the server, preserving * full type safety through the SDK's generic inference. @@ -18,4 +20,5 @@ export function registerAllTools(server: McpServer): void { registerScaffoldingTools(server); registerFileOperationTools(server); registerBuildTools(server); + registerCodeGenerationTools(server); } diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 9434792ae6..640e0a8e25 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -9,7 +9,8 @@ import { createToolResponse, type ErrorCode } from "@/tools/utils/response"; -import { mkdir } from "node:fs/promises"; +import { access, mkdir } from "node:fs/promises"; +import { dirname } from "node:path"; import { z } from "zod"; /** @@ -81,6 +82,23 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) }); try { + // Pre-validate ONLY for default path (catches Claude Desktop's non-existent cwd) + // For user-provided paths, let mkdir try and give a specific error if it fails + if (!args.outputPath) { + const parentDir = dirname(outputDir); + try { + await access(parentDir); + } catch { + return createStructuredErrorResponse( + createStructuredError("ERR_OUTPUT_PATH_REQUIRED", "Cannot create widget in default location", { + suggestion: + "The default output directory is not accessible (common in Claude Desktop). Please provide an explicit 'outputPath' parameter with a valid directory path on your system (e.g., '/Users/yourname/Projects/widgets', '~/widgets', or '/tmp/widgets').", + rawOutput: `Default path "${outputDir}" is not accessible. The working directory may not exist in this environment.` + }) + ); + } + } + console.error(`[create-widget] Starting widget scaffolding for "${options.name}"...`); await tracker.progress(SCAFFOLD_PROGRESS.START, `Starting widget scaffolding for "${options.name}"...`); await tracker.info(`Starting widget scaffolding for "${options.name}"...`, { @@ -167,8 +185,13 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) "The generator prompts may have changed. This could be a version mismatch. Please report this issue."; } else if (message.includes("ENOENT") || message.includes("not found")) { code = "ERR_NOT_FOUND"; - suggestion = - "A required file or command was not found. Ensure node, npm, and npx are installed and in PATH."; + // Check if this is a path issue vs a command issue + if (message.includes("mkdir") || message.includes(outputDir)) { + suggestion = `Cannot create directory "${outputDir}". Try a different 'outputPath' that you have write access to.`; + } else { + suggestion = + "Node.js, npm, or npx was not found. This tool requires a local development environment with npm installed. It cannot run in sandboxed environments like Claude Desktop's artifact sandbox."; + } } return createStructuredErrorResponse( diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts index d61a43361d..0a968776f6 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts @@ -14,7 +14,9 @@ export type ErrorCode = | "ERR_SCAFFOLD_FAILED" // Generic scaffold failure | "ERR_FILE_PATH" // Invalid file path | "ERR_FILE_WRITE" // File write failure - | "ERR_NOT_FOUND"; // Resource not found + | "ERR_NOT_FOUND" // Resource not found + | "ERR_OUTPUT_PATH_REQUIRED" // Output path required (e.g., in Claude Desktop) + | "ERR_OUTPUT_PATH_INVALID"; // Output path is not accessible /** * Structured error with code, message, and optional details. From 53801f524fe70a06dbb78dfda0878ad3a79671aa Mon Sep 17 00:00:00 2001 From: Rahman Date: Tue, 20 Jan 2026 02:13:26 +0100 Subject: [PATCH 08/36] feat(pluggable-widgets-mcp): update readme and agents.md, add security.md, clarify notifications --- packages/pluggable-widgets-mcp/AGENTS.md | 268 ++++-------------- packages/pluggable-widgets-mcp/README.md | 53 ++++ .../docs/agent/security.md | 20 ++ .../pluggable-widgets-mcp/package-lock.json | 126 +++++++- packages/pluggable-widgets-mcp/package.json | 2 +- .../src/tools/utils/notifications.ts | 29 ++ 6 files changed, 276 insertions(+), 222 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/docs/agent/security.md diff --git a/packages/pluggable-widgets-mcp/AGENTS.md b/packages/pluggable-widgets-mcp/AGENTS.md index 26b90fd4ab..9b34567841 100644 --- a/packages/pluggable-widgets-mcp/AGENTS.md +++ b/packages/pluggable-widgets-mcp/AGENTS.md @@ -1,257 +1,113 @@ -# Pluggable Widgets MCP Server - AI Agent Guide +# Pluggable Widgets MCP Server -This document provides context for AI development assistants working on the MCP (Model Context Protocol) server for Mendix pluggable widgets. +MCP server enabling AI assistants to scaffold and manage Mendix pluggable widgets via STDIO (default) or HTTP transport. -## Overview +## Quick Reference -This package implements an MCP server that enables AI assistants to scaffold and manage Mendix pluggable widgets programmatically. It supports both HTTP and STDIO transports for flexible integration with various MCP clients. - -### Key Characteristics - -- **MCP SDK**: Built on `@modelcontextprotocol/sdk` for standardized AI tool integration -- **Dual Transport**: HTTP (Express) for web clients, STDIO for CLI clients (Claude Desktop, etc.) -- **TypeScript**: Fully typed with Zod schemas for runtime validation -- **Widget Generator**: Wraps `@mendix/generator-widget` via PTY for interactive scaffolding +```bash +pnpm dev # Development with hot reload +pnpm build # TypeScript compilation + path alias resolution +pnpm start # Build and run (STDIO mode, default) +pnpm start:http # Build and run (HTTP mode, port 3100) +pnpm lint # ESLint check +``` ## Project Structure ``` src/ -├── index.ts # Entry point - transport mode selection -├── config.ts # Server configuration and constants -├── security/ -│ ├── guardrails.ts # Security validation (path traversal, extension whitelist) -│ └── index.ts # Security module exports -├── server/ -│ ├── server.ts # MCP server factory and tool/resource registration -│ ├── http.ts # HTTP transport setup (Express) -│ ├── stdio.ts # STDIO transport setup -│ ├── routes.ts # Express route handlers -│ └── session.ts # HTTP session management -├── resources/ -│ ├── index.ts # Resource registration -│ └── guidelines.ts # Widget development guidelines -└── tools/ - ├── index.ts # Tool registration aggregation - ├── types.ts # MCP tool type definitions - ├── scaffolding.tools.ts # Widget creation (create-widget) - ├── file-operations.tools.ts # File read/write/list operations - ├── build.tools.ts # Widget building and validation - └── utils/ - ├── generator.ts # Widget generator PTY wrapper - ├── progress-tracker.ts # Progress/logging helper - ├── notifications.ts # MCP notification utilities - └── response.ts # Tool response helpers -``` - -## Architecture - -### Transport Layer - -The server supports two transport modes selected via CLI argument: - -- **STDIO** (default): Single-session stdin/stdout for CLI integration (Claude Code, Claude Desktop) -- **HTTP**: Multi-session Express server on port 3100 for web clients and testing - -### Tool Registration - -Tools are registered directly with the MCP server using the SDK's `server.tool()` method. The current architecture uses category-based registration functions: - -```typescript -// src/tools/index.ts -export function registerAllTools(server: McpServer): void { - registerScaffoldingTools(server); // Widget creation - registerFileOperationTools(server); // File operations - registerBuildTools(server); // Building & validation -} -``` - -**Available Tools**: - -- **Scaffolding**: `create-widget` - Scaffolds new widgets via PTY interaction -- **File Operations**: - - `list-widget-files` - Lists files in widget directory - - `read-widget-file` - Reads widget file contents - - `write-widget-file` - Writes single file - - `batch-write-widget-files` - Writes multiple files atomically -- **Build**: `build-widget` - Compiles widget and parses errors (TypeScript, XML, dependencies) - -### Resources - -MCP resources provide read-only documentation that clients can fetch on-demand: - -```typescript -// src/resources/index.ts -export function registerResources(server: McpServer): void { - registerGuidelineResources(server); // Widget development guidelines -} -``` - -Resources are loaded from `docs/` directory and exposed via URIs like `resource://guidelines/property-types`. - -### Widget Generator Integration - -The `create-widget` tool uses `node-pty` to interact with the Mendix widget generator CLI. Key implementation details: - -- **PTY Simulation**: Required because the generator uses interactive prompts -- **Prompt Detection**: Matches expected prompts in terminal output -- **Answer Automation**: Sends pre-configured answers based on user input -- **Progress Tracking**: Reports progress via MCP notifications - -## Development Commands - -```bash -pnpm dev # Development mode with hot reload (tsx watch) -pnpm build # TypeScript compilation + path alias resolution (preserves shebang) -pnpm start # Build and run (HTTP mode on port 3100) -pnpm start:stdio # Build and run (STDIO mode) -pnpm lint # ESLint check +├── index.ts # Entry point - transport mode selection +├── config.ts # Server configuration and constants +├── security/ # Path traversal & extension validation +├── server/ # HTTP and STDIO transport setup +├── resources/ # MCP resources (guidelines) +├── generators/ # XML and TSX code generators +└── tools/ # MCP tool implementations ``` -## Adding New Tools +## Adding Tools -1. **Create tool file**: `src/tools/my-feature.tools.ts` +1. Create `src/tools/my-feature.tools.ts`: ```typescript import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; -const myToolSchema = z.object({ - param: z.string().describe("Parameter description for LLM") -}); - export function registerMyTools(server: McpServer): void { server.tool( - "my-tool", // Tool name - "Description shown to LLM", // Tool description - myToolSchema, // Input validation schema - async ({ param }) => { - // Handler with typed args - // Implementation - return { - content: [ - { - type: "text", - text: "Success message" - } - ] - }; - } + "my-tool", + "Description shown to LLM", + z.object({ param: z.string().describe("Parameter description") }), + async ({ param }) => ({ + content: [{ type: "text", text: "Success" }] + }) ); - - console.error("[my-feature] Registered 1 tool"); } ``` -2. **Register in index**: Update `src/tools/index.ts` +2. Register in `src/tools/index.ts`: ```typescript import { registerMyTools } from "./my-feature.tools"; export function registerAllTools(server: McpServer): void { - registerScaffoldingTools(server); - registerFileOperationTools(server); - registerBuildTools(server); - registerMyTools(server); // Add here + // ... existing registrations + registerMyTools(server); } ``` -## Code Conventions +## Code Patterns -### Imports +- **Imports**: Use `@/` path alias for absolute imports from `src/` +- **Schemas**: All tool inputs require Zod schemas +- **Errors**: Use `createErrorResponse()` from `@/tools/utils/response` +- **Long operations**: Use `ProgressTracker` from `@/tools/utils/progress-tracker` -- Use `@/` path alias for absolute imports from `src/` -- Prefer specific file imports over barrel exports when dealing with circular dependencies -- Group imports: node builtins → external packages → internal modules +## Notification Behavior (Important for AI Agents) -### Error Handling +When using this MCP server, understand where different types of output appear: -- Use `createErrorResponse()` for user-facing errors -- Log to `console.error` (not stdout) in STDIO mode -- Use `ProgressTracker` for long-running operations +| Output Type | Visibility | Purpose | +| -------------------------- | -------------------------- | ------------------------------------------------------- | +| **Tool Results** | ✅ Visible in conversation | Final outcomes, structured data, success/error messages | +| **Progress Notifications** | ❌ Not in conversation | Client UI indicators only (spinners, progress bars) | +| **Log Messages** | ❌ Not in conversation | Debug console/MCP Inspector only | -### Type Safety +**Key Implications for AI Agents:** -- All tool inputs must have Zod schemas -- Tool handlers receive fully typed arguments via Zod inference -- Use `McpServer` methods directly for type-safe tool registration +1. **Don't expect intermediate progress in chat**: Long operations (scaffolding, building) will show results only when complete. The conversation won't contain step-by-step progress updates. -## Testing +2. **Tool results are authoritative**: Only tool result content appears in the conversation history. Use this for: -Use MCP Inspector for interactive testing: + - Success confirmations with file paths + - Structured error messages with suggestions + - Any information the AI needs to continue the workflow -```bash -# STDIO mode -npx @modelcontextprotocol/inspector node dist/index.js stdio +3. **Progress tracking is for humans**: `sendProgress()` and `sendLogMessage()` are for human observers using MCP Inspector or UI indicators, not for AI decision-making. -# HTTP mode -pnpm start -npx @modelcontextprotocol/inspector -# Connect to http://localhost:3100/mcp -``` +4. **When debugging**: + - If operations seem to "hang", check MCP Inspector's Notifications/Logs panels + - Progress notifications confirm the server is working, even if the chat is quiet + - This is per MCP specification, not a bug -## Security - -All security validation is centralized in `src/security/guardrails.ts` for easy auditing: +**Example Workflow:** ```typescript -import { validateFilePath, ALLOWED_EXTENSIONS } from "@/security"; +// ❌ This progress won't appear in AI's context +await sendProgress(context, 50, "Scaffolding widget..."); -// Validates path traversal and extension whitelist -validateFilePath(widgetPath, filePath, true); // true = check extension +// ✅ This result WILL appear in AI's context +return createToolResponse(`Widget created at ${widgetPath}`); ``` -### Security Measures - -| Protection | Function | Description | -| ------------------- | ------------------------- | ------------------------------------------------------------------- | -| Path Traversal | `validateFilePath()` | Blocks `..` sequences and resolved path escapes | -| Extension Whitelist | `isExtensionAllowed()` | Only allows: `.tsx`, `.ts`, `.xml`, `.scss`, `.css`, `.json`, `.md` | -| Directory Boundary | `isPathWithinDirectory()` | Ensures files stay within widget directory | - -When adding file operation tools, always use `validateFilePath()` from the security module. - -## Key Files Reference - -| File | Purpose | -| -------------------------- | ------------------------------------------------ | -| `config.ts` | Server constants (ports, timeouts, paths) | -| `security/guardrails.ts` | Security validation (path traversal, extensions) | -| `tools/index.ts` | Tool registration aggregation | -| `tools/utils/generator.ts` | Widget generator PTY prompts and defaults | -| `resources/guidelines.ts` | Widget development guideline resources | -| `server/session.ts` | HTTP session lifecycle management | -| `server/server.ts` | MCP server factory and registration entry point | - -## Common Patterns - -### Progress Notifications - -```typescript -const tracker = new ProgressTracker({ - context, - logger: "my-tool", - totalSteps: 5 -}); +## Testing -tracker.start("initializing"); -await tracker.progress(25, "Step 1 complete"); -await tracker.info("Detailed log message", { key: "value" }); -tracker.stop(); +```bash +npx @modelcontextprotocol/inspector node dist/index.js ``` -### Long-Running Operations - -- Use `ProgressTracker` for heartbeat and stuck detection -- Set appropriate timeouts (see `SCAFFOLD_TIMEOUT_MS`) -- Call `tracker.markComplete()` before expected long waits (e.g., npm install) - -## Roadmap Context - -Current focus is widget scaffolding. Planned additions: +## Security -- Widget property editing -- XML configuration management -- Build and deployment automation +**Read before implementing file operations**: [docs/agent/security.md](docs/agent/security.md) -When adding features, maintain the existing patterns for tool registration, progress tracking, and transport-agnostic design. +All file operation tools must use `validateFilePath()` from `@/security` to prevent path traversal attacks. diff --git a/packages/pluggable-widgets-mcp/README.md b/packages/pluggable-widgets-mcp/README.md index f00a08b245..39ac713ce2 100644 --- a/packages/pluggable-widgets-mcp/README.md +++ b/packages/pluggable-widgets-mcp/README.md @@ -197,6 +197,59 @@ npx @modelcontextprotocol/inspector This is useful for verifying tool behavior without needing a full AI client integration. +## Understanding Feedback and Notifications + +This server uses MCP's notification system to provide progress updates and logging. However, **different types of feedback appear in different places**—not all feedback shows up in your chat conversation. + +### Where Different Types of Feedback Appear + +| Feedback Type | Where It Appears | Example | +| -------------------------- | -------------------------------------- | ----------------------------------------------------------------- | +| **Tool Results** | ✅ Chat conversation | Widget created at `/path/to/widget`, Build completed successfully | +| **Progress Notifications** | ⚙️ Client UI (spinners, progress bars) | "Scaffolding widget...", "Building widget..." | +| **Log Messages** | 🔍 Debug console (MCP Inspector) | Detailed operation logs, debug info | + +### Why Progress Doesn't Show in Chat + +**This is by design per the MCP specification**, not a bug. The MCP architecture separates concerns: + +- **`notifications/progress`** → Routed to client UI indicators (loading spinners, status bars) +- **`notifications/message`** → Routed to debug/inspector consoles for developers +- **Tool results** → Returned to the conversation when operations complete + +This means: + +- Long operations (scaffolding, building) will show **results** when complete +- You won't see intermediate progress steps in the chat history +- MCP Inspector shows all notifications in real-time (bottom-right panel) + +### Viewing Debug Output + +**With MCP Inspector:** + +1. Run: `npx @modelcontextprotocol/inspector node dist/index.js stdio` +2. Execute a tool (e.g., `create-widget`) +3. Watch the **Notifications panel** (bottom-right) for progress updates +4. Check the **Logs panel** for detailed debug output + +**With Claude Desktop:** + +- Progress notifications may appear as UI indicators (client-dependent) +- Check Claude Desktop's developer console for log messages (if available) +- Tool results will always appear in the conversation + +### Expected Behavior Examples + +**During widget scaffolding:** + +- Chat shows: "Starting scaffolding..." → (wait) → "Widget created at `/path`" +- Inspector shows: Step-by-step progress notifications for all 14 prompts + +**During widget building:** + +- Chat shows: "Building..." → (wait) → "Build successful" or structured error +- Inspector shows: TypeScript compilation progress, dependency resolution + ## Roadmap - [x] Widget scaffolding (`create-widget`) diff --git a/packages/pluggable-widgets-mcp/docs/agent/security.md b/packages/pluggable-widgets-mcp/docs/agent/security.md new file mode 100644 index 0000000000..816e26ff25 --- /dev/null +++ b/packages/pluggable-widgets-mcp/docs/agent/security.md @@ -0,0 +1,20 @@ +# Security + +All security validation is centralized in `src/security/guardrails.ts`: + +```typescript +import { validateFilePath, ALLOWED_EXTENSIONS } from "@/security"; + +// Validates path traversal and extension whitelist +validateFilePath(widgetPath, filePath, true); // true = check extension +``` + +## Security Measures + +| Protection | Function | Description | +| ------------------- | ------------------------- | ------------------------------------------------------------------- | +| Path Traversal | `validateFilePath()` | Blocks `..` sequences and resolved path escapes | +| Extension Whitelist | `isExtensionAllowed()` | Only allows: `.tsx`, `.ts`, `.xml`, `.scss`, `.css`, `.json`, `.md` | +| Directory Boundary | `isPathWithinDirectory()` | Ensures files stay within widget directory | + +**Rule**: When adding file operation tools, always use `validateFilePath()` from the security module. diff --git a/packages/pluggable-widgets-mcp/package-lock.json b/packages/pluggable-widgets-mcp/package-lock.json index 4de60372f3..8f138be2ec 100644 --- a/packages/pluggable-widgets-mcp/package-lock.json +++ b/packages/pluggable-widgets-mcp/package-lock.json @@ -1,27 +1,33 @@ { - "name": "pluggable-widgets-mcp", + "name": "@mendix/pluggable-widgets-mcp", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "pluggable-widgets-mcp", + "name": "@mendix/pluggable-widgets-mcp", "version": "0.1.0", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.24.2", - "node-pty": "^1.0.0", + "cors": "^2.8.5", + "express": "^5.1.0", + "node-pty": "1.2.0-beta.7", "tsx": "^4.21.0", "zod": "^4.1.13" }, + "bin": { + "pluggable-widgets-mcp": "dist/index.js" + }, "devDependencies": { "@types/cors": "^2.8.19", - "@types/node": "^24.10.1", + "@types/express": "^5.0.6", + "@types/node": "*", "tsc-alias": "^1.8.16", "typescript": "^5.9.3" }, "engines": { - "node": ">=22" + "node": ">=20" } }, "node_modules/@esbuild/aix-ppc64": { @@ -515,6 +521,27 @@ "node": ">= 8" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -525,6 +552,38 @@ "@types/node": "*" } }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", @@ -535,6 +594,41 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -975,6 +1069,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -1504,12 +1599,6 @@ "url": "https://github.com/sponsors/raouldeheer" } }, - "node_modules/nan": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "license": "MIT" - }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1519,14 +1608,20 @@ "node": ">= 0.6" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, "node_modules/node-pty": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz", - "integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==", + "version": "1.2.0-beta.7", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.7.tgz", + "integrity": "sha512-gHvC2HkwXDTqX931r7wBas2WISl7N26g6uOPHItA1OZmPlDwWZqTCWAjO8V3UShE9CEtd+VDawRr/0c8Uf+xiQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "nan": "^2.17.0" + "node-addon-api": "^7.1.0" } }, "node_modules/normalize-path": { @@ -2122,6 +2217,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index bd45aa5eb2..c38318be34 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -26,7 +26,7 @@ "@modelcontextprotocol/sdk": "^1.24.2", "cors": "^2.8.5", "express": "^5.1.0", - "node-pty": "^1.0.0", + "node-pty": "1.2.0-beta.7", "tsx": "^4.21.0", "zod": "^4.1.13" }, diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts b/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts index f214154620..7ecbef307f 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/notifications.ts @@ -3,6 +3,19 @@ import type { LogLevel, ToolContext } from "@/tools/types"; /** * Sends a progress notification to the MCP client. * Only sends if the client provided a progressToken in the request. + * + * **Where this appears:** + * - ⚙️ Client UI indicators (spinners, progress bars, status indicators) + * - 🔍 MCP Inspector's Notifications panel (for debugging) + * - ❌ NOT in the chat conversation history + * + * **MCP Specification Behavior:** + * Progress notifications are routed to the client's UI layer, not the conversation. + * This is by design—use tool results for chat-visible output. + * + * @param context - Tool execution context with notification sender + * @param progress - Progress value (0-100) + * @param message - Optional progress description */ export async function sendProgress(context: ToolContext, progress: number, message?: string): Promise { const progressToken = context._meta?.progressToken; @@ -17,6 +30,22 @@ export async function sendProgress(context: ToolContext, progress: number, messa /** * Sends a logging message notification to the MCP client. * Works independently of progressToken and provides detailed context. + * + * **Where this appears:** + * - 🔍 MCP Inspector's Logs panel (for debugging) + * - 🛠️ Client developer consoles (if supported) + * - ❌ NOT in the chat conversation history + * + * **MCP Specification Behavior:** + * Log notifications are routed to debug/inspector layers, not the conversation. + * These are intended for developers debugging MCP servers, not end-user feedback. + * For chat-visible messages, use tool result content instead. + * + * @param context - Tool execution context with notification sender + * @param level - Log severity level (debug, info, warning, error) + * @param message - Human-readable log message + * @param data - Additional structured data for debugging + * @param logger - Logger name/category (defaults to "mcp-tools") */ export async function sendLogMessage( context: ToolContext, From 8141f42c77954714826f91cd8e5a2b6483d42487 Mon Sep 17 00:00:00 2001 From: Rahman Date: Tue, 20 Jan 2026 09:26:23 +0100 Subject: [PATCH 09/36] feat(pluggable-widgets-mcp): add changelog --- packages/pluggable-widgets-mcp/CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/pluggable-widgets-mcp/CHANGELOG.md diff --git a/packages/pluggable-widgets-mcp/CHANGELOG.md b/packages/pluggable-widgets-mcp/CHANGELOG.md new file mode 100644 index 0000000000..8c63b9710f --- /dev/null +++ b/packages/pluggable-widgets-mcp/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this MCP server will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- We introduce pluggable-widgets-mcp. From 424be984900df622067379b5e09474c66c0c5277 Mon Sep 17 00:00:00 2001 From: Rahman Date: Tue, 20 Jan 2026 09:35:47 +0100 Subject: [PATCH 10/36] feat(pluggable-widgets-mcp): remove redundant prettier config --- packages/pluggable-widgets-mcp/.prettierrc.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 packages/pluggable-widgets-mcp/.prettierrc.js diff --git a/packages/pluggable-widgets-mcp/.prettierrc.js b/packages/pluggable-widgets-mcp/.prettierrc.js deleted file mode 100644 index 0892704ab0..0000000000 --- a/packages/pluggable-widgets-mcp/.prettierrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require("@mendix/prettier-config-web-widgets"); From 884de2007bc359c8a9f5eed2122e3ae68b2751d4 Mon Sep 17 00:00:00 2001 From: Rahman Date: Mon, 23 Feb 2026 13:33:28 +0100 Subject: [PATCH 11/36] fix(pluggable-widgets-mcp): fix property types, resource URIs, and add update-widget-prop tool --- packages/pluggable-widgets-mcp/AGENTS.md | 9 +- packages/pluggable-widgets-mcp/README.md | 31 +- .../docs/property-types.md | 175 +- .../pluggable-widgets-mcp/package-lock.json | 2235 ----------------- packages/pluggable-widgets-mcp/package.json | 13 +- .../pluggable-widgets-mcp/src/api/handlers.ts | 0 packages/pluggable-widgets-mcp/src/config.ts | 5 +- .../src/generators/tsx-generator.ts | 2 +- .../src/generators/types.ts | 2 +- .../src/generators/xml-generator.ts | 46 +- .../src/security/guardrails.ts | 17 +- .../src/tools/build.tools.ts | 23 +- .../src/tools/code-generation.tools.ts | 44 +- .../src/tools/file-operations.tools.ts | 114 +- .../pluggable-widgets-mcp/src/tools/index.ts | 5 +- .../src/tools/property-update.tools.ts | 303 +++ .../src/tools/scaffolding.tools.ts | 53 +- .../pluggable-widgets-mcp/src/tools/types.ts | 6 - .../src/tools/utils/response.ts | 3 +- packages/pluggable-widgets-mcp/tsconfig.json | 4 +- 20 files changed, 565 insertions(+), 2525 deletions(-) delete mode 100644 packages/pluggable-widgets-mcp/package-lock.json delete mode 100644 packages/pluggable-widgets-mcp/src/api/handlers.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts diff --git a/packages/pluggable-widgets-mcp/AGENTS.md b/packages/pluggable-widgets-mcp/AGENTS.md index 9b34567841..e6af325552 100644 --- a/packages/pluggable-widgets-mcp/AGENTS.md +++ b/packages/pluggable-widgets-mcp/AGENTS.md @@ -34,10 +34,13 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; export function registerMyTools(server: McpServer): void { - server.tool( + server.registerTool( "my-tool", - "Description shown to LLM", - z.object({ param: z.string().describe("Parameter description") }), + { + title: "My Tool", + description: "Description shown to LLM", + inputSchema: z.object({ param: z.string().describe("Parameter description") }) + }, async ({ param }) => ({ content: [{ type: "text", text: "Success" }] }) diff --git a/packages/pluggable-widgets-mcp/README.md b/packages/pluggable-widgets-mcp/README.md index 39ac713ce2..b6bedabee5 100644 --- a/packages/pluggable-widgets-mcp/README.md +++ b/packages/pluggable-widgets-mcp/README.md @@ -10,7 +10,7 @@ A Model Context Protocol (MCP) server that enables AI assistants to scaffold Men pnpm install pnpm build # Build the server pnpm start # STDIO mode (default) -pnpm start:stdio # HTTP mode +pnpm start:stdio # STDIO mode ``` ## Global Installation @@ -120,18 +120,24 @@ Generated widgets are placed in `generations/` directory within this package. ### File Operation Tools -| Tool | Description | -| -------------------------- | ------------------------------------------------------------ | -| `list-widget-files` | Lists all files in a widget directory, grouped by type | -| `read-widget-file` | Reads the contents of a file from a widget directory | -| `write-widget-file` | Writes content to a file (creates parent dirs automatically) | -| `batch-write-widget-files` | Writes multiple files atomically | +| Tool | Description | +| ------------------- | ------------------------------------------------------------------------------------- | +| `list-widget-files` | Lists all files in a widget directory, grouped by type | +| `read-widget-file` | Reads the contents of a file from a widget directory | +| `write-widget-file` | Writes content to a file (creates parent dirs). Supports single-file and batch modes. | **Security:** All file operations are protected by `src/security/guardrails.ts`: - Path traversal is blocked (no `..` escapes) - Extension whitelist: `.tsx`, `.ts`, `.xml`, `.scss`, `.css`, `.json`, `.md` +### Code Generation Tools + +| Tool | Description | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| `generate-widget-code` | Generates widget XML + TSX + SCSS from property definitions. Saves a `.widget-definition.json` snapshot. | +| `update-widget-properties` | Incrementally adds, removes, or modifies widget properties. Requires `generate-widget-code` to have been run first. | + ### build-widget Builds a widget using `pluggable-widgets-tools`, producing an `.mpk` file. @@ -193,7 +199,7 @@ npx @modelcontextprotocol/inspector } ``` 4. Click "Execute" and watch progress notifications as the widget is scaffolded -5. Check `generations/testwidget/` for the created widget +5. Check `generations/testWidget/` for the created widget This is useful for verifying tool behavior without needing a full AI client integration. @@ -243,7 +249,7 @@ This means: **During widget scaffolding:** - Chat shows: "Starting scaffolding..." → (wait) → "Widget created at `/path`" -- Inspector shows: Step-by-step progress notifications for all 14 prompts +- Inspector shows: Progress notifications (start → installing dependencies → complete) **During widget building:** @@ -256,10 +262,13 @@ This means: - [x] HTTP transport - [x] STDIO transport - [x] Progress notifications -- [x] File operations (list, read, write, batch-write) +- [x] File operations (list, read, write) - [x] Build tool (`build-widget`) - [x] Guideline resources (property-types, widget-patterns) -- [ ] Widget property editing (XML manipulation) +- [x] Code generation (`generate-widget-code`) +- [x] Incremental property update tool (`update-widget-properties`) +- [ ] Batch widget generation +- [ ] Widget testing helpers - [ ] TypeScript error recovery suggestions ## License diff --git a/packages/pluggable-widgets-mcp/docs/property-types.md b/packages/pluggable-widgets-mcp/docs/property-types.md index c2936c7890..792334849a 100644 --- a/packages/pluggable-widgets-mcp/docs/property-types.md +++ b/packages/pluggable-widgets-mcp/docs/property-types.md @@ -2,6 +2,8 @@ This document defines all available property types for Mendix pluggable widgets. Use this reference when defining properties in the JSON schema for XML generation. +> **Note:** XML is generated automatically by the `generate-widget-code` tool. You only need to provide JSON property definitions — no XML knowledge required. + ## Property Definition Schema When defining properties for the XML generator, use this JSON structure: @@ -35,15 +37,6 @@ Simple text input. } ``` -**XML Output:** - -```xml - - Label - Text label for the widget - -``` - --- ### boolean @@ -60,15 +53,6 @@ True/false toggle. } ``` -**XML Output:** - -```xml - - Show icon - Display an icon next to the text - -``` - --- ### integer @@ -85,15 +69,6 @@ Whole number input. } ``` -**XML Output:** - -```xml - - Maximum items - Maximum number of items to display - -``` - --- ### decimal @@ -128,15 +103,6 @@ Text with parameter substitution. Allows dynamic text with placeholders. } ``` -**XML Output:** - -```xml - - Legend - Text template with parameters - -``` - --- ### expression @@ -164,15 +130,6 @@ Dynamic expression that can reference attributes and return computed values. } ``` -**XML Output (with returnType):** - -```xml - - Value - - -``` - --- ## Action Types @@ -191,15 +148,6 @@ Event handler that triggers actions (microflows, nanoflows, etc.). } ``` -**XML Output:** - -```xml - - On click - Action to execute when clicked - -``` - --- ## Data Types @@ -229,18 +177,6 @@ Links to an entity attribute. Must specify allowed attribute types. } ``` -**XML Output:** - -```xml - - Value - Attribute to store the value - - - - -``` - **Valid attributeTypes:** - `String` @@ -271,15 +207,6 @@ Data source for list-based widgets. } ``` -**XML Output:** - -```xml - - Data source - Source of items to display - -``` - --- ### association @@ -297,15 +224,25 @@ Links to an entity association. --- -### entity +### selection -Entity selector. +Represents the selection mode for a widget (e.g., for data grids). + +| Field | Type | Required | Description | +| ----------- | ----------- | -------- | --------------------------- | +| key | string | ✅ | camelCase identifier | +| type | "selection" | ✅ | Must be "selection" | +| caption | string | ✅ | Display label in Studio Pro | +| description | string | | Help text | +| required | boolean | | Whether required | + +**Example:** ```json { - "key": "targetEntity", - "type": "entity", - "caption": "Target entity" + "key": "selection", + "type": "selection", + "caption": "Selection" } ``` @@ -331,19 +268,6 @@ Dropdown with predefined options. Must include `enumValues` array. } ``` -**XML Output:** - -```xml - - Alignment - - Left - Center - Right - - -``` - --- ### icon @@ -416,15 +340,6 @@ Container for child widgets. Used to create widget slots. } ``` -**XML Output:** - -```xml - - Content - Widgets to display inside - -``` - --- ### object @@ -453,24 +368,6 @@ Complex nested property with sub-properties. Used for repeating structures. } ``` -**XML Output:** - -```xml - - Columns - - - - Header - - - Width - - - - -``` - --- ## System Properties @@ -489,18 +386,6 @@ System properties are predefined by Mendix. Reference them by key only. - `TabIndex` - Tab order for accessibility - `Visibility` - Conditional visibility settings -**XML Output:** - -```xml - - - - - - - -``` - --- ## Property Groups @@ -524,6 +409,31 @@ Properties can be organized into groups for better Studio Pro UI. --- +## Property Organization + +### Auto-Grouping Behavior + +If `propertyGroups` is omitted from the widget definition, the `generate-widget-code` tool applies automatic grouping: + +- Non-action properties (all types except `action`) are placed in a **"General"** group. +- Action properties (`type: "action"`) are placed in an **"Events"** group. + +This means you rarely need to define `propertyGroups` explicitly for simple widgets. Only add it when you need custom group names or a specific ordering of groups. + +### Incrementally Updating Properties + +Once a widget has been generated with `generate-widget-code`, you do not need to regenerate all files to change the property list. Use the `update-widget-properties` tool to: + +- **Add** new properties to an existing widget +- **Remove** properties that are no longer needed +- **Modify** property attributes (e.g., change a caption or default value) + +The `update-widget-properties` tool reads the `.widget-definition.json` snapshot saved during `generate-widget-code` and applies only the requested delta, then regenerates the affected files. + +> **Requirement:** `generate-widget-code` must have been run at least once before `update-widget-properties` can be used, because it depends on the `.widget-definition.json` snapshot. + +--- + ## Full Widget Definition Example ```json @@ -586,3 +496,4 @@ Properties can be organized into groups for better Studio Pro UI. | `icon` | Icon picker | - | | `image` | Image picker | - | | `association` | Entity relation | - | +| `selection` | Selection mode | - | diff --git a/packages/pluggable-widgets-mcp/package-lock.json b/packages/pluggable-widgets-mcp/package-lock.json deleted file mode 100644 index 8f138be2ec..0000000000 --- a/packages/pluggable-widgets-mcp/package-lock.json +++ /dev/null @@ -1,2235 +0,0 @@ -{ - "name": "@mendix/pluggable-widgets-mcp", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@mendix/pluggable-widgets-mcp", - "version": "0.1.0", - "license": "Apache-2.0", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.24.2", - "cors": "^2.8.5", - "express": "^5.1.0", - "node-pty": "1.2.0-beta.7", - "tsx": "^4.21.0", - "zod": "^4.1.13" - }, - "bin": { - "pluggable-widgets-mcp": "dist/index.js" - }, - "devDependencies": { - "@types/cors": "^2.8.19", - "@types/express": "^5.0.6", - "@types/node": "*", - "tsc-alias": "^1.8.16", - "typescript": "^5.9.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz", - "integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz", - "integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz", - "integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz", - "integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz", - "integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz", - "integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz", - "integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz", - "integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz", - "integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz", - "integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz", - "integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz", - "integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz", - "integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz", - "integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz", - "integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz", - "integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz", - "integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz", - "integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz", - "integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz", - "integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz", - "integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz", - "integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz", - "integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz", - "integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz", - "integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz", - "integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.24.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.2.tgz", - "integrity": "sha512-hS/kzSfchqzvUeJUsdiDHi84/kNhLIZaZ6coGQVwbYIelOBbcAwUohUfaQTLa1MvFOK/jbTnGFzraHSFwB7pjQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "jose": "^6.1.1", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", - "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", - "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.1", - "@esbuild/android-arm": "0.27.1", - "@esbuild/android-arm64": "0.27.1", - "@esbuild/android-x64": "0.27.1", - "@esbuild/darwin-arm64": "0.27.1", - "@esbuild/darwin-x64": "0.27.1", - "@esbuild/freebsd-arm64": "0.27.1", - "@esbuild/freebsd-x64": "0.27.1", - "@esbuild/linux-arm": "0.27.1", - "@esbuild/linux-arm64": "0.27.1", - "@esbuild/linux-ia32": "0.27.1", - "@esbuild/linux-loong64": "0.27.1", - "@esbuild/linux-mips64el": "0.27.1", - "@esbuild/linux-ppc64": "0.27.1", - "@esbuild/linux-riscv64": "0.27.1", - "@esbuild/linux-s390x": "0.27.1", - "@esbuild/linux-x64": "0.27.1", - "@esbuild/netbsd-arm64": "0.27.1", - "@esbuild/netbsd-x64": "0.27.1", - "@esbuild/openbsd-arm64": "0.27.1", - "@esbuild/openbsd-x64": "0.27.1", - "@esbuild/openharmony-arm64": "0.27.1", - "@esbuild/sunos-x64": "0.27.1", - "@esbuild/win32-arm64": "0.27.1", - "@esbuild/win32-ia32": "0.27.1", - "@esbuild/win32-x64": "0.27.1" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", - "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "peer": true, - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", - "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mylas": { - "version": "2.1.14", - "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.14.tgz", - "integrity": "sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/raouldeheer" - } - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/node-pty": { - "version": "1.2.0-beta.7", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.2.0-beta.7.tgz", - "integrity": "sha512-gHvC2HkwXDTqX931r7wBas2WISl7N26g6uOPHItA1OZmPlDwWZqTCWAjO8V3UShE9CEtd+VDawRr/0c8Uf+xiQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^7.1.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkce-challenge": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/plimit-lit": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", - "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "queue-lit": "^1.5.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-lit": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", - "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tsc-alias": { - "version": "1.8.16", - "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.16.tgz", - "integrity": "sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.3", - "commander": "^9.0.0", - "get-tsconfig": "^4.10.0", - "globby": "^11.0.4", - "mylas": "^2.1.9", - "normalize-path": "^3.0.0", - "plimit-lit": "^1.2.6" - }, - "bin": { - "tsc-alias": "dist/bin/index.js" - }, - "engines": { - "node": ">=16.20.2" - } - }, - "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", - "license": "MIT", - "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/zod": { - "version": "4.1.13", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", - "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", - "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - } - } -} diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index c38318be34..5e208a8f01 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -2,7 +2,7 @@ "name": "@mendix/pluggable-widgets-mcp", "version": "0.1.0", "description": "MCP server for Mendix Pluggable Widgets", - "copyright": "© Mendix Technology BV 2025. All rights reserved.", + "copyright": "© Mendix Technology BV 2026. All rights reserved.", "author": "Mendix", "license": "Apache-2.0", "bin": { @@ -18,23 +18,22 @@ "dev": "tsx watch src/index.ts", "generate-source-map": "tsc --sourceMap --declaration --declarationMap", "lint": "eslint src/ package.json", - "start": "npm run build && node dist/index.js", - "start:http": "npm run build && node dist/index.js http", - "start:stdio": "npm run build && node dist/index.js stdio" + "start": "pnpm run build && node dist/index.js", + "start:http": "pnpm run build && node dist/index.js http", + "start:stdio": "pnpm run build && node dist/index.js stdio" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.24.2", "cors": "^2.8.5", "express": "^5.1.0", - "node-pty": "1.2.0-beta.7", - "tsx": "^4.21.0", "zod": "^4.1.13" }, "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^5.0.6", - "@types/node": "*", + "@types/node": "^22.0.0", "tsc-alias": "^1.8.16", + "tsx": "^4.21.0", "typescript": "^5.9.3" }, "keywords": [], diff --git a/packages/pluggable-widgets-mcp/src/api/handlers.ts b/packages/pluggable-widgets-mcp/src/api/handlers.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/pluggable-widgets-mcp/src/config.ts b/packages/pluggable-widgets-mcp/src/config.ts index 776c11ed17..c474863c55 100644 --- a/packages/pluggable-widgets-mcp/src/config.ts +++ b/packages/pluggable-widgets-mcp/src/config.ts @@ -1,9 +1,9 @@ +import { readFileSync } from "node:fs"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; // Server configuration export const SERVER_NAME = "pluggable-widgets-mcp"; -export const SERVER_VERSION = "0.1.0"; export const PORT = parseInt(process.env.PORT || "3100", 10); // Server metadata @@ -21,6 +21,9 @@ const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)) export const PACKAGE_ROOT = join(__dirname, "../"); export const GENERATIONS_DIR = join(process.cwd(), "generations"); +const _pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8")) as { version: string }; +export const SERVER_VERSION = _pkg.version; + // Path to local docs folder export const DOCS_DIR = join(PACKAGE_ROOT, "docs"); diff --git a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts index 582c389922..8778a1855e 100644 --- a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts @@ -122,7 +122,7 @@ function generateImports(widgetName: string, properties: PropertyDefinition[], p imports.push(`import { ${widgetName}ContainerProps } from "../typings/${widgetName}Props";`); // Styles import - imports.push(`import "./ui/${widgetName}.css";`); + imports.push(`import "./ui/${widgetName}.scss";`); return imports.join("\n"); } diff --git a/packages/pluggable-widgets-mcp/src/generators/types.ts b/packages/pluggable-widgets-mcp/src/generators/types.ts index efab114623..96ad1172a6 100644 --- a/packages/pluggable-widgets-mcp/src/generators/types.ts +++ b/packages/pluggable-widgets-mcp/src/generators/types.ts @@ -18,7 +18,7 @@ export type MendixPropertyType = | "attribute" | "datasource" | "association" - | "entity" + | "selection" | "enumeration" | "icon" | "image" diff --git a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts index c1ac043dc3..9b299a9d25 100644 --- a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts @@ -144,36 +144,34 @@ export function generateWidgetXml(widget: WidgetDefinition): GeneratorResult { } lines.push(` `); - lines.push(` `); // Group properties if grouping is specified if (widget.propertyGroups && widget.propertyGroups.length > 0) { for (const group of widget.propertyGroups) { - lines.push(` `); + lines.push(` `); for (const propKey of group.properties) { const prop = widget.properties.find(p => p.key === propKey); if (prop) { - lines.push(generateProperty(prop, " ")); + lines.push(generateProperty(prop, " ")); } } - lines.push(` `); + lines.push(` `); } } else { - // Default grouping: General for all properties - lines.push(` `); + // Default grouping: General for non-action properties, Events for actions + lines.push(` `); for (const prop of widget.properties.filter(p => p.type !== "action")) { - lines.push(generateProperty(prop, " ")); + lines.push(generateProperty(prop, " ")); } - lines.push(` `); + lines.push(` `); - // Events group for actions const actionProps = widget.properties.filter(p => p.type === "action"); if (actionProps.length > 0) { - lines.push(` `); + lines.push(` `); for (const prop of actionProps) { - lines.push(generateProperty(prop, " ")); + lines.push(generateProperty(prop, " ")); } - lines.push(` `); + lines.push(` `); } } @@ -183,23 +181,22 @@ export function generateWidgetXml(widget: WidgetDefinition): GeneratorResult { const commonProps = widget.systemProperties.filter(p => p !== "Visibility"); if (visibilityProps.length > 0) { - lines.push(` `); + lines.push(` `); for (const sysProp of visibilityProps) { - lines.push(generateSystemProperty(sysProp, " ")); + lines.push(generateSystemProperty(sysProp, " ")); } - lines.push(` `); + lines.push(` `); } if (commonProps.length > 0) { - lines.push(` `); + lines.push(` `); for (const sysProp of commonProps) { - lines.push(generateSystemProperty(sysProp, " ")); + lines.push(generateSystemProperty(sysProp, " ")); } - lines.push(` `); + lines.push(` `); } } - lines.push(` `); lines.push(` `); lines.push(``); @@ -252,5 +249,16 @@ export function validateWidgetDefinition(widget: WidgetDefinition): string[] { } } + if (widget.propertyGroups) { + const propertyKeys = new Set(widget.properties.map(p => p.key)); + for (const group of widget.propertyGroups) { + for (const key of group.properties) { + if (!propertyKeys.has(key)) { + errors.push(`Property group "${group.caption}" references unknown property key: "${key}"`); + } + } + } + } + return errors; } diff --git a/packages/pluggable-widgets-mcp/src/security/guardrails.ts b/packages/pluggable-widgets-mcp/src/security/guardrails.ts index b538b18af3..07f9515483 100644 --- a/packages/pluggable-widgets-mcp/src/security/guardrails.ts +++ b/packages/pluggable-widgets-mcp/src/security/guardrails.ts @@ -19,12 +19,17 @@ import { extname, resolve } from "node:path"; * Allowed file extensions for write operations. * Only widget-related source files are permitted. */ -export const ALLOWED_EXTENSIONS = [".tsx", ".ts", ".xml", ".scss", ".css", ".json", ".md", ".editorConfig.ts"]; +export const ALLOWED_EXTENSIONS = [".tsx", ".ts", ".xml", ".scss", ".css", ".json", ".md"]; /** - * Config files allowed without extensions (e.g., .gitignore) + * Config files allowed without extensions (e.g., tsconfig, package) */ -const ALLOWED_EXTENSIONLESS_PATTERNS = ["package", "tsconfig", "eslintrc", ".gitignore", ".prettierrc"]; +const ALLOWED_EXTENSIONLESS_PATTERNS = ["package", "tsconfig", "eslintrc"]; + +/** + * Specific dot-files allowed (explicit allowlist to prevent arbitrary dotfile access). + */ +const ALLOWED_DOT_FILES = [".gitignore", ".prettierrc", ".eslintrc", ".editorconfig"]; // ============================================================================= // Path Traversal Prevention @@ -76,8 +81,10 @@ export function isExtensionAllowed(filePath: string): boolean { // and special config files if (ext === "") { const filename = filePath.split("/").pop() || ""; - // Allow common config files without extensions - return ALLOWED_EXTENSIONLESS_PATTERNS.some(name => filename.includes(name) || filename.startsWith(".")); + // Allow common config files without extensions, or specific dot-files + return ( + ALLOWED_EXTENSIONLESS_PATTERNS.some(name => filename.includes(name)) || ALLOWED_DOT_FILES.includes(filename) + ); } return ALLOWED_EXTENSIONS.includes(ext); } diff --git a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts index b3faba539f..2773a52ca6 100644 --- a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts @@ -6,8 +6,9 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { spawn } from "node:child_process"; import { existsSync, readdirSync } from "node:fs"; -import { join } from "node:path"; +import { join, resolve } from "node:path"; import { z } from "zod"; +import { GENERATIONS_DIR } from "@/config"; import type { ToolContext, ToolResponse } from "./types"; import { ProgressTracker } from "./utils/progress-tracker"; import { @@ -348,6 +349,26 @@ async function handleBuildWidget(args: BuildWidgetInput, context: ToolContext): ); } + // Validate path is within allowed directories + const resolvedWidgetPath = resolve(widgetPath); + const allowedBuildPaths = [ + resolve(GENERATIONS_DIR), + ...(process.env.MCP_ALLOWED_BUILD_PATHS ?? "") + .split(":") + .filter(Boolean) + .map(p => resolve(p)) + ]; + const isAllowedPath = allowedBuildPaths.some( + allowed => resolvedWidgetPath.startsWith(allowed + "/") || resolvedWidgetPath === allowed + ); + if (!isAllowedPath) { + return createStructuredErrorResponse( + createStructuredError("ERR_NOT_FOUND", `Widget path is not within an allowed directory: ${widgetPath}`, { + suggestion: `Widget must be within ${GENERATIONS_DIR} or set MCP_ALLOWED_BUILD_PATHS env var (colon-separated paths).` + }) + ); + } + // Check for package.json const packageJsonPath = join(widgetPath, "package.json"); if (!existsSync(packageJsonPath)) { diff --git a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts index a458c7dd96..7d39394228 100644 --- a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts @@ -50,7 +50,7 @@ const propertyDefinitionSchema = z.object({ "attribute", "datasource", "association", - "entity", + "selection", "enumeration", "icon", "image", @@ -89,6 +89,14 @@ const propertyDefinitionSchema = z.object({ .describe("Return type for expression property") }); +/** + * Schema for property group definitions. + */ +const propertyGroupSchema = z.object({ + caption: z.string().min(1).describe("Group caption displayed in Studio Pro"), + properties: z.array(z.string().min(1)).min(1).describe("Property keys in this group") +}); + /** * Schema for the generate-widget-code tool input. */ @@ -102,7 +110,19 @@ const generateWidgetCodeSchema = z.object({ widgetPattern: z .enum(["display", "button", "input", "container", "dataList"]) .optional() - .describe("Optional hint for TSX generation pattern") + .describe("Optional hint for TSX generation pattern"), + systemProperties: z + .array(z.enum(["Name", "TabIndex", "Visibility"])) + .optional() + .describe( + 'System properties to include. Defaults to ["Name", "TabIndex", "Visibility"]. Pass empty array to include none.' + ), + propertyGroups: z + .array(propertyGroupSchema) + .optional() + .describe( + "Optional property grouping. If not provided, non-action properties go in 'General' and action properties go in 'Events' automatically." + ) }); type GenerateWidgetCodeInput = z.infer; @@ -320,7 +340,8 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< name: widgetName, description, properties: properties as PropertyDefinition[], - systemProperties: ["Name", "TabIndex", "Visibility"] + systemProperties: args.systemProperties ?? ["Name", "TabIndex", "Visibility"], + propertyGroups: args.propertyGroups }; // Validate widget definition @@ -358,7 +379,9 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< // Prepare files to write const filesToWrite = [ { path: `src/${widgetName}.xml`, content: xmlResult.xml }, - { path: `src/${widgetName}.tsx`, content: tsxResult.mainComponent } + { path: `src/${widgetName}.tsx`, content: tsxResult.mainComponent }, + { path: `src/ui/${widgetName}.scss`, content: `.widget-${widgetName.toLowerCase()} {\n}\n` }, + { path: `src/.widget-definition.json`, content: JSON.stringify(widgetDefinition, null, 2) } ]; // Validate and write files @@ -389,14 +412,17 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< [ `✅ Widget code generated successfully!`, "", - `📁 Files modified:`, - ` • src/${widgetName}.xml - Added ${properties.length} properties (${propSummary})`, - ` • src/${widgetName}.tsx - Implemented using ${pattern} pattern`, + `📁 Files written:`, + ` • src/${widgetName}.xml - Widget definition with ${properties.length} properties (${propSummary})`, + ` • src/${widgetName}.tsx - Component using ${pattern} pattern`, + ` • src/ui/${widgetName}.scss - Empty SCSS placeholder`, + ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)`, "", `🔨 Next steps:`, ` 1. Run build-widget to compile and validate`, - ` 2. Review generated code for customization`, - ` 3. Test in Mendix Studio Pro` + ` 2. Review and customize generated code`, + ` 3. Update src/${widgetName}.editorPreview.tsx for Studio Pro design mode preview`, + ` 4. Test in Mendix Studio Pro` ].join("\n") ); } catch (error) { diff --git a/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts index 72104ce7ef..b3aec8856d 100644 --- a/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts @@ -22,29 +22,36 @@ const readWidgetFileSchema = z.object({ .describe("Relative path to the file within the widget directory (e.g., 'src/MyWidget.tsx')") }); -const writeWidgetFileSchema = z.object({ - widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), - filePath: z - .string() - .min(1) - .describe("Relative path to the file within the widget directory (e.g., 'src/components/MyComponent.tsx')"), - content: z.string().describe("The content to write to the file") -}); - -const fileEntrySchema = z.object({ - relativePath: z.string().min(1).describe("Relative path within the widget directory"), - content: z.string().describe("File content to write") -}); - -const batchWriteWidgetFilesSchema = z.object({ - widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), - files: z.array(fileEntrySchema).min(1).describe("Array of files to write") -}); +const writeWidgetFileSchema = z + .object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), + // Single file mode + filePath: z + .string() + .optional() + .describe("Relative path to the file (single file mode, e.g., 'src/components/MyComponent.tsx')"), + content: z.string().optional().describe("File content (single file mode)"), + // Batch mode + files: z + .array( + z.object({ + relativePath: z.string().min(1).describe("Relative path within the widget directory"), + content: z.string().describe("File content to write") + }) + ) + .optional() + .describe("Array of files to write (batch mode)") + }) + .refine( + data => + (data.filePath !== undefined && data.content !== undefined) || + (data.files !== undefined && data.files.length > 0), + { message: "Either provide (filePath + content) for single file mode, or files array for batch mode" } + ); type ListWidgetFilesInput = z.infer; type ReadWidgetFileInput = z.infer; type WriteWidgetFileInput = z.infer; -type BatchWriteWidgetFilesInput = z.infer; // ============================================================================= // Tool Handlers @@ -136,38 +143,16 @@ async function handleReadWidgetFile(args: ReadWidgetFileInput): Promise { - try { - validateFilePath(args.widgetPath, args.filePath, true); - - const fullPath = join(args.widgetPath, args.filePath); - - // Ensure parent directory exists - const parentDir = dirname(fullPath); - await mkdir(parentDir, { recursive: true }); - - // Write the file - await writeFile(fullPath, args.content, "utf-8"); - - console.error(`[file-operations] Wrote file: ${fullPath}`); - - return createToolResponse( - [ - `Successfully wrote file: ${args.filePath}`, - `Full path: ${fullPath}`, - `Size: ${args.content.length} characters` - ].join("\n") - ); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - return createErrorResponse(`Failed to write file: ${message}`); - } -} + // Normalize to array: single file mode → wrap in array; batch mode → use directly + const filesToWrite = + args.filePath !== undefined && args.content !== undefined + ? [{ relativePath: args.filePath, content: args.content }] + : (args.files ?? []); -async function handleBatchWriteWidgetFiles(args: BatchWriteWidgetFilesInput): Promise { const results: Array<{ path: string; success: boolean; error?: string }> = []; // Validate all paths first before writing anything - for (const file of args.files) { + for (const file of filesToWrite) { try { validateFilePath(args.widgetPath, file.relativePath, true); } catch (error) { @@ -177,7 +162,7 @@ async function handleBatchWriteWidgetFiles(args: BatchWriteWidgetFilesInput): Pr } // Write all files - for (const file of args.files) { + for (const file of filesToWrite) { try { const fullPath = join(args.widgetPath, file.relativePath); @@ -201,11 +186,11 @@ async function handleBatchWriteWidgetFiles(args: BatchWriteWidgetFilesInput): Pr if (failed.length === 0) { return createToolResponse( - [`Successfully wrote ${successful.length} files:`, "", ...successful.map(r => ` - ${r.path}`)].join("\n") + [`Successfully wrote ${successful.length} file(s):`, "", ...successful.map(r => ` - ${r.path}`)].join("\n") ); } else if (successful.length === 0) { return createErrorResponse( - [`Failed to write all ${failed.length} files:`, "", ...failed.map(r => ` - ${r.path}: ${r.error}`)].join( + [`Failed to write all ${failed.length} file(s):`, "", ...failed.map(r => ` - ${r.path}: ${r.error}`)].join( "\n" ) ); @@ -245,10 +230,12 @@ Examples: - src/MyWidget.xml (properties definition) - src/components/Header.tsx (sub-component)`; -const WRITE_WIDGET_FILE_DESCRIPTION = `Writes content to a file in a widget directory. +const WRITE_WIDGET_FILE_DESCRIPTION = `Writes one or more files to a widget directory. -Use this to implement widget functionality after scaffolding. -Creates parent directories if they don't exist. +**Single file mode:** provide filePath + content. +**Batch mode:** provide files array (each with relativePath + content). + +Validates all paths before writing. Creates parent directories if needed. IMPORTANT: Follow Mendix widget development guidelines: - Use TypeScript and React @@ -258,17 +245,6 @@ IMPORTANT: Follow Mendix widget development guidelines: Allowed file types: ${ALLOWED_EXTENSIONS.join(", ")}`; -const BATCH_WRITE_WIDGET_FILES_DESCRIPTION = `Writes multiple files to a widget directory in a single operation. - -Use this for atomic writes when updating XML, TSX, and SCSS together. -Validates all paths before writing to ensure consistency. -Creates parent directories if they don't exist. - -Example use case: After generating XML from a widget definition, -write the XML, TSX component, and SCSS files together. - -Allowed file types: ${ALLOWED_EXTENSIONS.join(", ")}`; - /** * Registers file operation tools for reading and writing widget files. * @@ -305,14 +281,4 @@ export function registerFileOperationTools(server: McpServer): void { }, handleWriteWidgetFile ); - - server.registerTool( - "batch-write-widget-files", - { - title: "Batch Write Widget Files", - description: BATCH_WRITE_WIDGET_FILES_DESCRIPTION, - inputSchema: batchWriteWidgetFilesSchema - }, - handleBatchWriteWidgetFiles - ); } diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts index 31e508e651..0941d00c7c 100644 --- a/packages/pluggable-widgets-mcp/src/tools/index.ts +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -2,6 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerBuildTools } from "./build.tools"; import { registerCodeGenerationTools } from "./code-generation.tools"; import { registerFileOperationTools } from "./file-operations.tools"; +import { registerPropertyUpdateTools } from "./property-update.tools"; import { registerScaffoldingTools } from "./scaffolding.tools"; /** @@ -9,9 +10,10 @@ import { registerScaffoldingTools } from "./scaffolding.tools"; * * Tools are organized by category: * - Scaffolding: Widget creation (create-widget) - * - File Operations: Read/write widget files (list-widget-files, read-widget-file, write-widget-file, batch-write-widget-files) + * - File Operations: Read/write widget files (list-widget-files, read-widget-file, write-widget-file) * - Build: Widget building and validation (build-widget) * - Code Generation: Generate widget XML and TSX (generate-widget-code) + * - Property Update: Incremental property updates (update-widget-properties) * * Each category registers its tools directly with the server, preserving * full type safety through the SDK's generic inference. @@ -21,4 +23,5 @@ export function registerAllTools(server: McpServer): void { registerFileOperationTools(server); registerBuildTools(server); registerCodeGenerationTools(server); + registerPropertyUpdateTools(server); } diff --git a/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts b/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts new file mode 100644 index 0000000000..79e7704318 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts @@ -0,0 +1,303 @@ +/** + * Property Update Tool for Mendix Pluggable Widgets. + * + * Provides the `update-widget-properties` tool that incrementally modifies + * widget property definitions without full regeneration. + */ + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { z } from "zod"; +import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; +import type { PropertyDefinition, PropertyGroup, SystemProperty, WidgetDefinition } from "@/generators/types"; +import { validateFilePath } from "@/security"; +import type { ToolResponse } from "@/tools/types"; +import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; + +// ============================================================================= +// Schemas +// ============================================================================= + +/** + * Schema for a single property definition (reuse same structure as code-generation.tools.ts). + */ +const propertyDefinitionSchema = z.object({ + key: z + .string() + .min(1) + .regex(/^[a-z][a-zA-Z0-9]*$/, "Must be camelCase (e.g., 'myProperty')") + .describe("Property key in camelCase"), + type: z + .enum([ + "string", + "boolean", + "integer", + "decimal", + "textTemplate", + "expression", + "action", + "attribute", + "datasource", + "association", + "selection", + "enumeration", + "icon", + "image", + "file", + "widgets", + "object" + ]) + .describe("Mendix property type"), + caption: z.string().min(1).describe("Display caption shown in Studio Pro"), + description: z.string().optional().describe("Help text shown in Studio Pro"), + required: z.boolean().optional().describe("Whether this property is required"), + defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for this property"), + enumValues: z + .array(z.object({ key: z.string().min(1), caption: z.string().min(1) })) + .optional() + .describe("Allowed values for enumeration type"), + attributeTypes: z + .array( + z.enum([ + "String", + "Integer", + "Long", + "Decimal", + "Boolean", + "DateTime", + "Enum", + "HashString", + "Binary", + "AutoNumber" + ]) + ) + .optional() + .describe("Allowed attribute types for attribute property"), + isList: z.boolean().optional().describe("Whether datasource returns a list"), + dataSource: z.string().optional().describe("Reference to datasource property key (for widgets type)"), + returnType: z + .enum(["String", "Integer", "Decimal", "Boolean", "DateTime"]) + .optional() + .describe("Return type for expression property") +}); + +const operationSchema = z.discriminatedUnion("action", [ + z.object({ + action: z.literal("add"), + property: propertyDefinitionSchema.describe("Property definition to add") + }), + z.object({ + action: z.literal("remove"), + propertyKey: z.string().min(1).describe("Key of the property to remove") + }), + z.object({ + action: z.literal("modify"), + propertyKey: z.string().min(1).describe("Key of the property to modify"), + updates: z.record(z.string(), z.unknown()).describe("Fields to merge into the existing property definition") + }) +]); + +const updateWidgetPropertiesSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), + operations: z + .array(operationSchema) + .min(1) + .describe("List of operations to apply sequentially (add/remove/modify)"), + systemProperties: z + .array(z.enum(["Name", "TabIndex", "Visibility"])) + .optional() + .describe("Replaces current system properties if provided"), + propertyGroups: z + .array( + z.object({ + caption: z.string().min(1).describe("Group caption"), + properties: z.array(z.string().min(1)).min(1).describe("Property keys in this group") + }) + ) + .optional() + .describe("Replaces current property groups if provided") +}); + +type UpdateWidgetPropertiesInput = z.infer; + +// ============================================================================= +// Tool Handler +// ============================================================================= + +async function handleUpdateWidgetProperties(args: UpdateWidgetPropertiesInput): Promise { + const { widgetPath, operations, systemProperties, propertyGroups } = args; + + try { + // Read the widget definition snapshot + const snapshotPath = "src/.widget-definition.json"; + let widgetDefinition: WidgetDefinition; + + try { + validateFilePath(widgetPath, snapshotPath); + const fullSnapshotPath = join(widgetPath, snapshotPath); + const raw = await readFile(fullSnapshotPath, "utf-8"); + widgetDefinition = JSON.parse(raw) as WidgetDefinition; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + if (message.includes("ENOENT") || message.includes("no such file")) { + return createErrorResponse( + [ + `❌ Widget definition snapshot not found at ${widgetPath}/src/.widget-definition.json`, + "", + "The snapshot is created by the generate-widget-code tool. Please run it first to generate the initial widget code.", + "", + "Example: call generate-widget-code with your widgetPath, description, and properties." + ].join("\n") + ); + } + return createErrorResponse(`Failed to read widget definition: ${message}`); + } + + // Apply operations sequentially + const changeLog: string[] = []; + + for (const op of operations) { + if (op.action === "add") { + const existing = widgetDefinition.properties.find(p => p.key === op.property.key); + if (existing) { + return createErrorResponse( + `Cannot add property: key "${op.property.key}" already exists. Use action "modify" to update it.` + ); + } + widgetDefinition.properties.push(op.property as PropertyDefinition); + changeLog.push(`+ Added property "${op.property.key}" (${op.property.type})`); + } else if (op.action === "remove") { + const idx = widgetDefinition.properties.findIndex(p => p.key === op.propertyKey); + if (idx === -1) { + return createErrorResponse(`Cannot remove property: key "${op.propertyKey}" not found.`); + } + widgetDefinition.properties.splice(idx, 1); + changeLog.push(`- Removed property "${op.propertyKey}"`); + } else if (op.action === "modify") { + const prop = widgetDefinition.properties.find(p => p.key === op.propertyKey); + if (!prop) { + return createErrorResponse(`Cannot modify property: key "${op.propertyKey}" not found.`); + } + Object.assign(prop, op.updates); + changeLog.push(`~ Modified property "${op.propertyKey}": ${Object.keys(op.updates).join(", ")}`); + } + } + + // Replace systemProperties / propertyGroups if provided + if (systemProperties !== undefined) { + widgetDefinition.systemProperties = systemProperties as SystemProperty[]; + changeLog.push(`~ Updated systemProperties: [${systemProperties.join(", ")}]`); + } + + if (propertyGroups !== undefined) { + widgetDefinition.propertyGroups = propertyGroups as PropertyGroup[]; + changeLog.push(`~ Updated propertyGroups (${propertyGroups.length} groups)`); + } + + // Validate updated definition + const validationErrors = validateWidgetDefinition(widgetDefinition); + if (validationErrors.length > 0) { + return createErrorResponse( + [ + "❌ Updated widget definition is invalid:", + "", + ...validationErrors.map(e => ` • ${e}`), + "", + "Please fix the above issues. Operations were NOT saved." + ].join("\n") + ); + } + + // Regenerate XML + const xmlResult = generateWidgetXml(widgetDefinition); + if (!xmlResult.success || !xmlResult.xml) { + return createErrorResponse(`XML regeneration failed: ${xmlResult.error}`); + } + + // Write updated XML and snapshot + const xmlPath = `src/${widgetDefinition.name}.xml`; + const filesToWrite = [ + { path: xmlPath, content: xmlResult.xml }, + { path: snapshotPath, content: JSON.stringify(widgetDefinition, null, 2) } + ]; + + for (const file of filesToWrite) { + try { + validateFilePath(widgetPath, file.path, true); + const fullPath = join(widgetPath, file.path); + await writeFile(fullPath, file.content, "utf-8"); + console.error(`[property-update] Wrote: ${fullPath}`); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createErrorResponse(`Failed to write ${file.path}: ${message}`); + } + } + + return createToolResponse( + [ + `✅ Widget properties updated successfully!`, + "", + `📝 Changes applied (${changeLog.length}):`, + ...changeLog.map(c => ` ${c}`), + "", + `📁 Files updated:`, + ` • ${xmlPath} - Regenerated with ${widgetDefinition.properties.length} properties`, + ` • ${snapshotPath} - Snapshot updated`, + "", + `🔨 Next steps:`, + ` 1. Run build-widget to compile and validate`, + ` 2. Update src/${widgetDefinition.name}.tsx if new properties need to be wired up` + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + console.error(`[property-update] Error: ${message}`); + return createErrorResponse(`Widget property update failed: ${message}`); + } +} + +// ============================================================================= +// Tool Registration +// ============================================================================= + +const UPDATE_WIDGET_PROPERTIES_DESCRIPTION = `Incrementally updates widget properties without full regeneration. + +Reads the widget definition snapshot (src/.widget-definition.json created by generate-widget-code), +applies the specified operations, validates the result, and regenerates the XML. + +**Operations:** +- \`add\`: Add a new property +- \`remove\`: Remove an existing property by key +- \`modify\`: Merge updates into an existing property + +**Prerequisites:** +- Widget must have been generated with generate-widget-code first (creates the snapshot) + +**Example — add a property and remove another:** +\`\`\`json +{ + "widgetPath": "/path/to/MyWidget", + "operations": [ + { "action": "add", "property": { "key": "label", "type": "textTemplate", "caption": "Label" } }, + { "action": "remove", "propertyKey": "oldProp" } + ] +} +\`\`\``; + +/** + * Registers the property update tool with the MCP server. + */ +export function registerPropertyUpdateTools(server: McpServer): void { + server.registerTool( + "update-widget-properties", + { + title: "Update Widget Properties", + description: UPDATE_WIDGET_PROPERTIES_DESCRIPTION, + inputSchema: updateWidgetPropertiesSchema + }, + handleUpdateWidgetProperties + ); + + console.error("[property-update] Registered 1 tool"); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 640e0a8e25..6c216eb0f2 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -1,7 +1,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { GENERATIONS_DIR } from "@/config"; import { DEFAULT_WIDGET_OPTIONS, type ToolContext, type ToolResponse, widgetOptionsSchema } from "@/tools/types"; -import { buildWidgetOptions, GENERATOR_PROMPTS, runWidgetGenerator, SCAFFOLD_PROGRESS } from "@/tools/utils/generator"; +import { buildWidgetOptions, runWidgetGenerator, SCAFFOLD_PROGRESS } from "@/tools/utils/generator"; import { ProgressTracker } from "@/tools/utils/progress-tracker"; import { createStructuredError, @@ -10,7 +10,7 @@ import { type ErrorCode } from "@/tools/utils/response"; import { access, mkdir } from "node:fs/promises"; -import { dirname } from "node:path"; +import { dirname, resolve } from "node:path"; import { z } from "zod"; /** @@ -75,10 +75,37 @@ export function registerScaffoldingTools(server: McpServer): void { async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext): Promise { const options = buildWidgetOptions(args); const outputDir = args.outputPath ?? GENERATIONS_DIR; + + // Validate user-provided outputPath is within allowed directories + if (args.outputPath) { + const resolvedOutputPath = resolve(args.outputPath); + const allowedOutputPaths = [ + resolve(GENERATIONS_DIR), + ...(process.env.MCP_ALLOWED_OUTPUT_PATHS ?? "") + .split(":") + .filter(Boolean) + .map(p => resolve(p)) + ]; + const isAllowedPath = allowedOutputPaths.some( + allowed => resolvedOutputPath.startsWith(allowed + "/") || resolvedOutputPath === allowed + ); + if (!isAllowedPath) { + return createStructuredErrorResponse( + createStructuredError( + "ERR_OUTPUT_PATH_INVALID", + `Output path is not within an allowed directory: ${args.outputPath}`, + { + suggestion: `Output must be within ${GENERATIONS_DIR} or set MCP_ALLOWED_OUTPUT_PATHS env var (colon-separated paths).` + } + ) + ); + } + } + const tracker = new ProgressTracker({ context, logger: "scaffolding", - totalSteps: GENERATOR_PROMPTS.length + totalSteps: 3 }); try { @@ -111,13 +138,12 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) // Ensure output directory exists await mkdir(outputDir, { recursive: true }); - // Create widget folder - we control the folder name (matches user's input) - const widgetFolder = options.name; + // The generator creates the widget folder itself (camelCase: first letter lowered) + const widgetFolder = options.name.charAt(0).toLowerCase() + options.name.slice(1); const widgetPath = `${outputDir}/${widgetFolder}`; - await mkdir(widgetPath, { recursive: true }); - // Run generator inside the widget folder (it outputs files directly there) - await runWidgetGenerator(options, tracker, widgetPath); + // Run generator inside outputDir — it creates the widget subfolder + await runWidgetGenerator(options, tracker, outputDir); console.error(`[create-widget] Widget created successfully at ${widgetPath}`); await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget created successfully!"); @@ -135,9 +161,8 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) "=== TO IMPLEMENT WIDGET FUNCTIONALITY ===", "", "1. FETCH GUIDELINES (MCP Resources):", - " - mendix://guidelines/frontend (CSS/SCSS, Atlas UI, naming conventions)", - " - mendix://guidelines/implementation (step-by-step widget development)", - " - mendix://guidelines/backend-structure (Mendix data API: EditableValue, ActionValue)", + " - mendix://guidelines/property-types (all widget property types with JSON schema)", + " - mendix://guidelines/widget-patterns (reusable TSX/SCSS patterns for common widget types)", "", "2. EXPLORE WIDGET STRUCTURE:", ` Use list-widget-files tool with widgetPath: "${widgetPath}"`, @@ -179,10 +204,6 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) code = "ERR_SCAFFOLD_TIMEOUT"; suggestion = "The generator took too long. Check your network connection and npm registry access. Try running 'npx @mendix/generator-widget' manually."; - } else if (message.includes("prompt") || message.includes("expected")) { - code = "ERR_SCAFFOLD_PROMPT"; - suggestion = - "The generator prompts may have changed. This could be a version mismatch. Please report this issue."; } else if (message.includes("ENOENT") || message.includes("not found")) { code = "ERR_NOT_FOUND"; // Check if this is a path issue vs a command issue @@ -190,7 +211,7 @@ async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext) suggestion = `Cannot create directory "${outputDir}". Try a different 'outputPath' that you have write access to.`; } else { suggestion = - "Node.js, npm, or npx was not found. This tool requires a local development environment with npm installed. It cannot run in sandboxed environments like Claude Desktop's artifact sandbox."; + "The generator-widget binary was not found. Run: cd /path/to/widgets-tools/packages/generator-widget && npm link. Then ensure the MCP server runs under the same Node.js version."; } } diff --git a/packages/pluggable-widgets-mcp/src/tools/types.ts b/packages/pluggable-widgets-mcp/src/tools/types.ts index ab5084024f..b89f2087c4 100644 --- a/packages/pluggable-widgets-mcp/src/tools/types.ts +++ b/packages/pluggable-widgets-mcp/src/tools/types.ts @@ -20,12 +20,6 @@ export interface ToolResponse { */ export type ToolContext = RequestHandlerExtra; -/** - * Type for tool handler functions. - * Used for typing handlers that are passed to McpServer.registerTool(). - */ -export type ToolHandler = (args: T, context: ToolContext) => Promise; - /** * Log levels supported by MCP logging notifications. */ diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts index 0a968776f6..e436113080 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts @@ -10,7 +10,6 @@ export type ErrorCode = | "ERR_BUILD_MISSING_DEP" // Missing dependency | "ERR_BUILD_UNKNOWN" // Unknown build error | "ERR_SCAFFOLD_TIMEOUT" // Scaffolding timed out - | "ERR_SCAFFOLD_PROMPT" // Generator prompt mismatch | "ERR_SCAFFOLD_FAILED" // Generic scaffold failure | "ERR_FILE_PATH" // Invalid file path | "ERR_FILE_WRITE" // File write failure @@ -48,6 +47,7 @@ export function createToolResponse(text: string): ToolResponse { */ export function createErrorResponse(message: string): ToolResponse { return { + isError: true, content: [{ type: "text", text: message }] }; } @@ -89,6 +89,7 @@ export function createStructuredErrorResponse(error: StructuredError): ToolRespo } return { + isError: true, content: [{ type: "text", text: lines.join("\n") }] }; } diff --git a/packages/pluggable-widgets-mcp/tsconfig.json b/packages/pluggable-widgets-mcp/tsconfig.json index 91fd5af0b7..75d03a23fa 100644 --- a/packages/pluggable-widgets-mcp/tsconfig.json +++ b/packages/pluggable-widgets-mcp/tsconfig.json @@ -6,8 +6,8 @@ "paths": { "@/*": ["./src/*"] }, - "module": "esnext", - "moduleResolution": "node", + "module": "preserve", + "moduleResolution": "bundler", "target": "ES2022", "outDir": "./dist", "rootDir": "./src", From f069b3bd561ac82eff3b7ff284b7b1a38e641f9e Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 25 Feb 2026 16:12:49 +0100 Subject: [PATCH 12/36] feat(pluggable-widgets-mcp): use custom generator-widget with non-interactive defaults --- packages/pluggable-widgets-mcp/package.json | 1 + .../src/tools/utils/generator.ts | 350 ++++-------------- pnpm-lock.yaml | 36 +- 3 files changed, 111 insertions(+), 276 deletions(-) diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index 5e208a8f01..28260f69db 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -23,6 +23,7 @@ "start:stdio": "pnpm run build && node dist/index.js stdio" }, "dependencies": { + "@mendix/generator-widget": "github:rahmanunver/widgets-tools#generator-widget-noninteractive-defaults&path:packages/generator-widget", "@modelcontextprotocol/sdk": "^1.24.2", "cors": "^2.8.5", "express": "^5.1.0", diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts index f6317ee4ea..be7b338ffa 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -1,64 +1,21 @@ -import type * as NodePty from "node-pty"; -import { GENERATIONS_DIR, SCAFFOLD_TIMEOUT_MS } from "@/config"; +import { spawn } from "node:child_process"; +import { resolve } from "node:path"; +import { GENERATIONS_DIR, PACKAGE_ROOT, SCAFFOLD_TIMEOUT_MS } from "@/config"; import { DEFAULT_WIDGET_OPTIONS, type WidgetOptions, type WidgetOptionsInput } from "@/tools/types"; import { ProgressTracker } from "./progress-tracker"; // Re-export for backward compatibility with existing imports export { DEFAULT_WIDGET_OPTIONS }; -/** - * Generator prompt patterns in order - must match answers array. - */ -export const GENERATOR_PROMPTS = [ - "What is the name", - "Enter a description", - "organization", - "copyright", - "license", - "version", - "author", - "path", - "programming language", - "type of components", - "type of widget", - "template", - "unit tests", - "end-to-end" -] as const; - /** * Progress milestones for widget scaffolding. */ export const SCAFFOLD_PROGRESS = { START: 0, - PROMPTS_START: 5, - PROMPTS_END: 70, - INSTALLING: 75, + INSTALLING: 50, COMPLETE: 100 } as const; -/** - * Buffer size for prompt detection in terminal output. - * Increased from 500 to improve reliability with terminal buffering. - */ -const PROMPT_DETECTION_BUFFER_SIZE = 1000; - -/** - * Delay between sending answers to allow terminal to process. - */ -const ANSWER_SEND_DELAY_MS = 200; - -/** - * Local state for tracking generator process progress. - */ -interface GeneratorLocalState { - output: string; - answerIndex: number; - promptMatchedIndex: number; - allPromptsAnswered: boolean; - lastActivityTime: number; -} - /** * Builds widget options from input arguments with defaults applied. * Takes the schema-validated input (with optional fields) and returns @@ -80,272 +37,123 @@ export function buildWidgetOptions(args: WidgetOptionsInput): WidgetOptions { } /** - * Arrow key escape sequence for navigating interactive prompts. + * Returns the path to the generator-widget binary installed in this package's node_modules. + * Using a direct path (rather than npx) ensures we always use the correct version + * regardless of the spawn cwd (which is set to outputDir for widget placement). */ -const ARROW_DOWN = "\x1b[B"; - -/** - * Maps programming language option to the key sequence needed. - * TypeScript is the first option (just Enter), JavaScript needs arrow down first. - */ -function getLanguageKeySequence(language: "typescript" | "javascript"): string { - return language === "javascript" ? ARROW_DOWN : ""; +function getGeneratorBinPath(): string { + return resolve(PACKAGE_ROOT, "node_modules/.bin/generator-widget"); } /** - * Builds the answers array for the generator prompts. - * @param options - Fully resolved widget options (all fields required) - * @param outputDir - Output directory (used for project path calculation) + * Maps WidgetOptions to CLI flags for the non-interactive generator. + * Requires @mendix/generator-widget with --default flag support (commit 16cf75e). */ -export function buildGeneratorAnswers(options: WidgetOptions, outputDir?: string): string[] { - // Calculate relative project path from widget folder to parent directory - const projectPath = outputDir ? "../" : "../"; - +function buildWidgetFlags(options: WidgetOptions): string[] { return [ - "", // Widget name - already passed as CLI arg + "--default", + "--description", options.description, + "--organization", options.organization, - "© Mendix Technology BV 2025", // Copyright + "--copyright", + "© Mendix Technology BV 2026", + "--license", options.license, + "--version", options.version, + "--author", options.author, - projectPath, // Project path (relative to widget folder) - getLanguageKeySequence(options.programmingLanguage), // Programming language selection - "", // Component type - Enter for Function Components (default) - "", // Platform - Enter for web (default) + "--projectPath", + "../", + "--programmingLanguage", + options.programmingLanguage, + "--programmingStyle", + "function", + "--platform", + "web", + "--boilerplate", options.template, - options.unitTests ? "yes" : "no", - options.e2eTests ? "yes" : "no" + ...(options.unitTests ? ["--hasUnitTests"] : []), + ...(options.e2eTests ? ["--hasE2eTests"] : []) ]; } /** - * Calculates progress percentage for a given prompt index. - */ -export function calculatePromptProgress(promptIndex: number): number { - const progressRange = SCAFFOLD_PROGRESS.PROMPTS_END - SCAFFOLD_PROGRESS.PROMPTS_START; - const promptProgress = (promptIndex / GENERATOR_PROMPTS.length) * progressRange; - return Math.round(SCAFFOLD_PROGRESS.PROMPTS_START + promptProgress); -} - -/** - * Removes ANSI escape codes and spinner characters from terminal output. - */ -export function cleanTerminalOutput(data: string): string { - return ( - data - // eslint-disable-next-line no-control-regex -- Intentionally matching ANSI escape sequences - .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "") - .replace(/[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/g, "") - .replace(/[\r\n]+/g, " ") - .replace(/\[[\dD\dC\dK\dG]+/g, "") - .trim() - ); -} - -type NodePtyModule = typeof NodePty; - -async function loadNodePty(): Promise { - try { - // NOTE: node-pty is a native addon. Import it lazily so the MCP server can still start - // in environments where the addon is not available/built (e.g. missing toolchain). - const mod: any = await import("node-pty"); - const pty = (mod?.default ?? mod) as NodePtyModule; - - if (typeof pty?.spawn !== "function") { - throw new Error("node-pty loaded but does not expose spawn()"); - } - - return pty; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - - throw new Error( - [ - "Failed to load `node-pty` (native addon). This is required for the `create-widget` tool.", - "", - "Fix (macOS):", - "- Install Xcode Command Line Tools: `xcode-select --install`", - "- Rebuild the addon: `pnpm -w rebuild node-pty` (or run it from the repo root)", - "- If you're on Node.js 22+, consider upgrading `node-pty` to a version that supports your Node version", - "", - `Original error: ${message}` - ].join("\n") - ); - } -} - -/** - * Handles generator output and sends answers when prompts are detected. - * Uses a larger buffer and improved logging for reliability. - */ -function handleGeneratorOutput( - state: GeneratorLocalState, - tracker: ProgressTracker, - sendNextAnswer: () => void, - onAllPromptsAnswered: () => void -): void { - // Update activity timestamp for stuck detection - state.lastActivityTime = Date.now(); - - if (state.answerIndex < GENERATOR_PROMPTS.length) { - // Skip if we've already matched this prompt - if (state.promptMatchedIndex >= state.answerIndex) { - return; - } - - const expectedPattern = GENERATOR_PROMPTS[state.answerIndex]; - const recentOutput = state.output.slice(-PROMPT_DETECTION_BUFFER_SIZE).toLowerCase(); - - if (recentOutput.includes(expectedPattern.toLowerCase())) { - state.promptMatchedIndex = state.answerIndex; - tracker.updateStep(expectedPattern, state.answerIndex + 1); - - const progress = calculatePromptProgress(state.answerIndex + 1); - const message = `Configuring: ${expectedPattern}`; - - tracker.progress(progress, message).catch(() => undefined); - tracker - .info(message, { - step: expectedPattern, - promptIndex: state.answerIndex + 1, - totalPrompts: GENERATOR_PROMPTS.length - }) - .catch(() => undefined); - - setTimeout(sendNextAnswer, ANSWER_SEND_DELAY_MS); - } else { - // Debug logging for unmatched prompts (only log occasionally to avoid spam) - const cleanedRecent = cleanTerminalOutput(recentOutput.slice(-200)); - if (cleanedRecent.length > 0 && state.output.length % 500 < 50) { - console.error( - `[create-widget] Waiting for prompt "${expectedPattern}" (index ${state.answerIndex}), recent: "${cleanedRecent.slice(-100)}"` - ); - } - } - } else { - onAllPromptsAnswered(); - } -} - -/** - * Gets the path to npx based on the current node executable. - * This ensures we use the correct npx even when PATH is not fully available. - */ -function getNpxPath(): string { - const nodePath = process.execPath; - const nodeDir = nodePath.substring(0, nodePath.lastIndexOf("/")); - return `${nodeDir}/npx`; -} - -/** - * Runs the Mendix widget generator using node-pty for terminal interaction. + * Runs the Mendix widget generator using non-interactive CLI flags. + * Replaces the previous node-pty / interactive-prompt approach. + * * @param options - Widget configuration options * @param tracker - Progress tracker for notifications - * @param outputDir - Directory where the widget will be created (defaults to GENERATIONS_DIR) + * @param outputDir - Directory where the widget folder will be created */ export async function runWidgetGenerator( options: WidgetOptions, tracker: ProgressTracker, outputDir: string = GENERATIONS_DIR -): Promise { - const pty = await loadNodePty(); - const answers = buildGeneratorAnswers(options, outputDir); - const npxPath = getNpxPath(); +): Promise { + const flags = buildWidgetFlags(options); + const generatorBin = getGeneratorBinPath(); return new Promise((resolve, reject) => { - const state: GeneratorLocalState = { - output: "", - answerIndex: 0, - promptMatchedIndex: -1, - allPromptsAnswered: false, - lastActivityTime: Date.now() - }; - tracker.start("initializing"); - // Use full path to npx to avoid PATH issues in STDIO mode - const ptyProcess = pty.spawn(npxPath, ["@mendix/generator-widget", options.name], { - name: "xterm-color", - cols: 120, - rows: 30, + let stdout = ""; + let stderr = ""; + let installingNotified = false; + + const child = spawn(generatorBin, [options.name, ...flags], { cwd: outputDir, - env: { ...process.env, FORCE_COLOR: "0" } + env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", DO_NOT_TRACK: "1" }, + stdio: ["ignore", "pipe", "pipe"] }); - const sendNextAnswer = (): void => { - if (state.answerIndex < answers.length) { - const answer = answers[state.answerIndex]; - const displayAnswer = - answer === "" ? "(Enter)" : answer.startsWith("\x1b") ? "(Arrow+Enter)" : `"${answer}"`; - const idx = state.answerIndex + 1; - console.error(`[create-widget] [${idx}/${answers.length}] Sending: ${displayAnswer}`); - state.answerIndex++; - ptyProcess.write(answer + "\r"); - } - }; + child.stdout.on("data", (data: Buffer) => { + const chunk = data.toString(); + stdout += chunk; + console.error(`[create-widget] stdout: ${chunk.trim()}`); - // Stuck detection: if no progress for 30 seconds, try resending current answer - const stuckCheckInterval = setInterval(() => { - const timeSinceActivity = Date.now() - state.lastActivityTime; - if (timeSinceActivity > 30000 && !state.allPromptsAnswered && state.answerIndex > 0) { - console.error( - `[create-widget] No progress for ${Math.round(timeSinceActivity / 1000)}s at step ${state.answerIndex}, retrying...` - ); - // Resend Enter to potentially unstick the process - ptyProcess.write("\r"); - state.lastActivityTime = Date.now(); + if (!installingNotified && stdout.includes("npm install")) { + installingNotified = true; + tracker.updateStep("installing", 2); + tracker.progress(SCAFFOLD_PROGRESS.INSTALLING, "Installing dependencies...").catch(() => undefined); + tracker.info("Installing dependencies...").catch(() => undefined); } - }, 10000); + }); - ptyProcess.onData(data => { - state.output += data; - handleGeneratorOutput(state, tracker, sendNextAnswer, () => { - if (!state.allPromptsAnswered) { - state.allPromptsAnswered = true; - tracker.updateStep("installing", GENERATOR_PROMPTS.length); - tracker.markComplete(); - console.error("[create-widget] Installing dependencies..."); - tracker.progress(SCAFFOLD_PROGRESS.INSTALLING, "Installing dependencies...").catch(() => undefined); - tracker.info("Installing dependencies...").catch(() => undefined); - } - }); + child.stderr.on("data", (data: Buffer) => { + const chunk = data.toString(); + stderr += chunk; + console.error(`[create-widget] stderr: ${chunk.trim()}`); }); - ptyProcess.onExit(({ exitCode }) => { - clearInterval(stuckCheckInterval); + const timeout = setTimeout(() => { + tracker.stop(); + child.kill(); + reject(new Error("Widget scaffold timed out after 5 minutes")); + }, SCAFFOLD_TIMEOUT_MS); + + child.on("close", (exitCode: number | null) => { + clearTimeout(timeout); tracker.stop(); + if (exitCode === 0) { - // Generator creates folder with exact widget name (preserves case) - const widgetFolder = options.name; - console.error(`[create-widget] Widget scaffolded successfully: ${widgetFolder}`); - resolve(widgetFolder); + console.error(`[create-widget] Widget scaffolded successfully`); + resolve(); } else { console.error(`[create-widget] Widget scaffold failed with exit code ${exitCode}`); - const cleanOutput = cleanTerminalOutput(state.output); - tracker - .error(`Scaffold failed with exit code ${exitCode}`, { - lastOutput: cleanOutput.slice(-500) - }) - .catch(() => undefined); - reject(new Error(`Generator exited with code ${exitCode}\nOutput: ${cleanOutput.slice(-2000)}`)); + reject( + new Error( + `Generator exited with code ${exitCode}\nStderr: ${stderr.slice(-2000)}\nStdout: ${stdout.slice(-1000)}` + ) + ); } }); - const timeout = setTimeout(() => { - clearInterval(stuckCheckInterval); + child.on("error", (err: Error) => { + clearTimeout(timeout); tracker.stop(); - console.error("[create-widget] Widget scaffold timed out after 5 minutes"); - tracker - .error("Widget scaffold timed out after 5 minutes", { - step: tracker.state.step, - stepIndex: tracker.state.stepIndex - }) - .catch(() => undefined); - ptyProcess.kill(); - reject(new Error("Widget scaffold timed out after 5 minutes")); - }, SCAFFOLD_TIMEOUT_MS); - - ptyProcess.onExit(() => clearTimeout(timeout)); + reject(new Error(`Failed to spawn generator: ${err.message}`)); + }); }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94a20204c9..37e67ee124 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -212,7 +212,7 @@ importers: version: 11.1.0 node-fetch: specifier: ^2.7.0 - version: 2.7.0 + version: 2.7.0(encoding@0.1.13) ora: specifier: ^5.4.1 version: 5.4.1 @@ -6225,6 +6225,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.3: + resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} + engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.9: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} @@ -6983,6 +6987,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -8736,6 +8743,10 @@ packages: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true + minimatch@10.2.2: + resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} + engines: {node: 18 || 20 || >=22} + minimatch@10.2.6: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} @@ -14218,7 +14229,7 @@ snapshots: '@typescript-eslint/types': 8.66.0 '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3 - minimatch: 10.2.6 + minimatch: 10.2.2 semver: 7.7.3 tinyglobby: 0.2.15 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -14748,6 +14759,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.3: + dependencies: + balanced-match: 4.0.4 + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -15577,6 +15592,11 @@ snapshots: encodeurl@2.0.0: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -16406,14 +16426,14 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 - minimatch: 10.2.6 + minimatch: 10.2.2 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.0 glob@13.0.6: dependencies: - minimatch: 10.2.6 + minimatch: 10.2.2 minipass: 7.1.3 path-scurry: 2.0.2 @@ -17913,6 +17933,10 @@ snapshots: mini-svg-data-uri@1.4.4: {} + minimatch@10.2.2: + dependencies: + brace-expansion: 5.0.3 + minimatch@10.2.6: dependencies: brace-expansion: 5.0.9 @@ -18048,9 +18072,11 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - node-fetch@2.7.0: + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 node-fetch@3.3.2: dependencies: From 0833277dd7677b415640a4ce61963e392b19b9ba Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 27 Feb 2026 11:16:24 +0100 Subject: [PATCH 13/36] feat(pluggable-widgets-mcp): add project tools, session state, and deploy support Adds get-project-info, set-project-directory, and deploy-widget tools. Introduces SessionState for per-session isolation and MENDIX_PROJECT_DIR env var for project configuration. Includes findMpkFile utility and new error codes for project/deploy failures. Co-Authored-By: Claude Opus 4.6 --- packages/pluggable-widgets-mcp/src/config.ts | 83 +++++++- .../src/server/server.ts | 6 +- .../pluggable-widgets-mcp/src/tools/index.ts | 10 +- .../src/tools/project.tools.ts | 180 ++++++++++++++++++ .../src/tools/session-state.ts | 15 ++ .../src/tools/utils/mpk.ts | 30 +++ .../src/tools/utils/response.ts | 5 +- 7 files changed, 321 insertions(+), 8 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/tools/project.tools.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/session-state.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts diff --git a/packages/pluggable-widgets-mcp/src/config.ts b/packages/pluggable-widgets-mcp/src/config.ts index c474863c55..6d5c7433bd 100644 --- a/packages/pluggable-widgets-mcp/src/config.ts +++ b/packages/pluggable-widgets-mcp/src/config.ts @@ -1,5 +1,6 @@ import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; +import { readdir, stat } from "node:fs/promises"; +import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; // Server configuration @@ -13,8 +14,17 @@ export const SERVER_ICON = { mimeType: "image/png" }; export const SERVER_WEBSITE_URL = "https://github.com/mendix/web-widgets"; -export const SERVER_INSTRUCTIONS = - "This is a MCP server for Mendix Pluggable Widgets. It allows you to create and edit widgets."; +export const SERVER_INSTRUCTIONS = `This is a MCP server for Mendix Pluggable Widgets. It allows you to create, build, and deploy widgets to a Mendix project. + +WORKFLOW GUIDE: +1. Call get-project-info first to discover the configured Mendix project directory. +2. If a project is configured, you can scaffold, build, and deploy widgets without asking for filesystem paths. +3. If no project is configured, use set-project-directory to configure one, or proceed without deployment. +4. Use create-widget to scaffold a new widget (output goes to the generations/ directory). +5. Use build-widget to compile the widget and produce an .mpk file. +6. Use deploy-widget to copy the .mpk to the project's widgets/ folder. + +IMPORTANT: Do NOT ask the user for filesystem paths — use get-project-info to discover the project context automatically.`; // Paths - use fileURLToPath for Node.js 18 compatibility (import.meta.dirname requires Node 20.11+) const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)); @@ -29,3 +39,70 @@ export const DOCS_DIR = join(PACKAGE_ROOT, "docs"); // Timeouts export const SCAFFOLD_TIMEOUT_MS = 300000; // 5 minutes + +// Project directory configuration +export const MENDIX_PROJECT_DIR = process.env.MENDIX_PROJECT_DIR ? resolve(process.env.MENDIX_PROJECT_DIR) : undefined; + +export interface ProjectValidation { + valid: boolean; + projectDir: string; + projectName?: string; + widgetsDir: string; + existingWidgets: string[]; + error?: string; +} + +/** + * Validates a Mendix project directory. + * Checks that it exists and contains a .mpr file. + * Returns the project name and list of existing .mpk widgets. + */ +export async function validateProjectDir(dir: string): Promise { + const widgetsDir = join(dir, "widgets"); + + try { + await stat(dir); + } catch { + return { + valid: false, + projectDir: dir, + widgetsDir, + existingWidgets: [], + error: `Directory does not exist: ${dir}` + }; + } + + let projectName: string | undefined; + try { + const entries = await readdir(dir); + const mprFile = entries.find(entry => entry.endsWith(".mpr")); + if (!mprFile) { + return { + valid: false, + projectDir: dir, + widgetsDir, + existingWidgets: [], + error: `No .mpr file found in ${dir}. This does not appear to be a Mendix project directory.` + }; + } + projectName = mprFile.replace(/\.mpr$/, ""); + } catch { + return { + valid: false, + projectDir: dir, + widgetsDir, + existingWidgets: [], + error: `Failed to read directory: ${dir}` + }; + } + + let existingWidgets: string[] = []; + try { + const entries = await readdir(widgetsDir); + existingWidgets = entries.filter(entry => entry.endsWith(".mpk")); + } catch { + // widgets/ dir may not exist yet — that's fine + } + + return { valid: true, projectDir: dir, projectName, widgetsDir, existingWidgets }; +} diff --git a/packages/pluggable-widgets-mcp/src/server/server.ts b/packages/pluggable-widgets-mcp/src/server/server.ts index a0c2630ecf..47d20dd5c3 100644 --- a/packages/pluggable-widgets-mcp/src/server/server.ts +++ b/packages/pluggable-widgets-mcp/src/server/server.ts @@ -1,12 +1,16 @@ import { SERVER_ICON, SERVER_INSTRUCTIONS, SERVER_NAME, SERVER_VERSION, SERVER_WEBSITE_URL } from "@/config"; import { registerResources } from "@/resources"; import { registerAllTools } from "@/tools"; +import { createSessionState } from "@/tools/session-state"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; /** * Creates and configures a new MCP server instance with all registered tools and resources. + * Each instance gets its own session state so concurrent HTTP sessions are isolated. */ export function createMcpServer(): McpServer { + const state = createSessionState(); + const server = new McpServer( { name: SERVER_NAME, @@ -25,7 +29,7 @@ export function createMcpServer(): McpServer { } ); - registerAllTools(server); + registerAllTools(server, state); registerResources(server); return server; diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts index 0941d00c7c..23aba9f4ee 100644 --- a/packages/pluggable-widgets-mcp/src/tools/index.ts +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -2,8 +2,10 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerBuildTools } from "./build.tools"; import { registerCodeGenerationTools } from "./code-generation.tools"; import { registerFileOperationTools } from "./file-operations.tools"; +import { registerProjectTools } from "./project.tools"; import { registerPropertyUpdateTools } from "./property-update.tools"; import { registerScaffoldingTools } from "./scaffolding.tools"; +import type { SessionState } from "./session-state"; /** * Registers all tools with the MCP server. @@ -14,14 +16,16 @@ import { registerScaffoldingTools } from "./scaffolding.tools"; * - Build: Widget building and validation (build-widget) * - Code Generation: Generate widget XML and TSX (generate-widget-code) * - Property Update: Incremental property updates (update-widget-properties) + * - Project: Project directory config and deployment (get-project-info, set-project-directory, deploy-widget) * * Each category registers its tools directly with the server, preserving * full type safety through the SDK's generic inference. */ -export function registerAllTools(server: McpServer): void { - registerScaffoldingTools(server); +export function registerAllTools(server: McpServer, state: SessionState): void { + registerScaffoldingTools(server, state); registerFileOperationTools(server); - registerBuildTools(server); + registerBuildTools(server, state); registerCodeGenerationTools(server); registerPropertyUpdateTools(server); + registerProjectTools(server, state); } diff --git a/packages/pluggable-widgets-mcp/src/tools/project.tools.ts b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts new file mode 100644 index 0000000000..32a15d5c4c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts @@ -0,0 +1,180 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { copyFile, mkdir } from "node:fs/promises"; +import { basename, join, resolve } from "node:path"; +import { z } from "zod"; +import { GENERATIONS_DIR, validateProjectDir } from "@/config"; +import { isPathAllowed } from "./utils/sandbox"; +import type { ToolResponse } from "@/tools/types"; +import { findMpkFile } from "@/tools/utils/mpk"; +import { createStructuredError, createStructuredErrorResponse, createToolResponse } from "@/tools/utils/response"; +import type { SessionState } from "./session-state"; + +function formatProjectInfo(validation: Awaited>): string { + const lines: string[] = [ + `Project Directory: ${validation.projectDir}`, + ...(validation.projectName ? [`Project Name: ${validation.projectName}`] : []), + `Widgets Directory: ${validation.widgetsDir}` + ]; + + if (validation.existingWidgets.length > 0) { + lines.push(`Existing Widgets (${validation.existingWidgets.length}):`); + for (const widget of validation.existingWidgets) { + lines.push(` - ${widget}`); + } + } else { + lines.push(`Existing Widgets: (none)`); + } + + return lines.join("\n"); +} + +export function registerProjectTools(server: McpServer, state: SessionState): void { + server.registerTool( + "get-project-info", + { + title: "Get Project Info", + description: + "Returns information about the configured Mendix project directory. " + + "Call this first to discover the project context before creating or deploying widgets. " + + "Returns the project directory, project name, widgets directory, and existing .mpk files.", + inputSchema: z.object({}) + }, + async (): Promise => { + if (!state.projectDir) { + return createStructuredErrorResponse( + createStructuredError("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { + suggestion: + "Set the MENDIX_PROJECT_DIR environment variable when starting the server, e.g.:\n" + + " MENDIX_PROJECT_DIR=/Users/you/Mendix/MyProject node dist/index.js http\n" + + "Or call set-project-directory to configure it at runtime." + }) + ); + } + + const validation = await validateProjectDir(state.projectDir); + if (!validation.valid) { + return createStructuredErrorResponse( + createStructuredError( + "ERR_PROJECT_NOT_CONFIGURED", + `Configured project directory is invalid: ${validation.error}`, + { suggestion: "Use set-project-directory to set a valid Mendix project directory." } + ) + ); + } + + return createToolResponse(`✅ Project configured\n\n${formatProjectInfo(validation)}`); + } + ); + + server.registerTool( + "set-project-directory", + { + title: "Set Project Directory", + description: + "Configures the Mendix project directory for this session. " + + "The directory must exist and contain a .mpr file. " + + "Once set, deploy-widget can copy built .mpk files to the project's widgets/ folder.", + inputSchema: z.object({ + projectDir: z + .string() + .describe("Absolute path to the Mendix project directory (must contain a .mpr file)") + }) + }, + async (args: { projectDir: string }): Promise => { + const resolvedDir = resolve(args.projectDir); + const validation = await validateProjectDir(resolvedDir); + if (!validation.valid) { + return createStructuredErrorResponse( + createStructuredError( + "ERR_PROJECT_NOT_CONFIGURED", + `Invalid project directory: ${validation.error}`, + { + suggestion: + "Provide the absolute path to a directory that exists and contains a .mpr file, e.g.:\n" + + " /Users/you/Mendix/MyProject" + } + ) + ); + } + + state.projectDir = validation.projectDir; + return createToolResponse(`✅ Project directory configured\n\n${formatProjectInfo(validation)}`); + } + ); + + server.registerTool( + "deploy-widget", + { + title: "Deploy Widget", + description: + "Copies a built widget .mpk file to the configured Mendix project's widgets/ directory. " + + "Requires a project directory to be configured (via MENDIX_PROJECT_DIR env var or set-project-directory). " + + "Looks for the .mpk file in the widget's dist/ directory. " + + "After deploying, synchronize the app directory in Studio Pro to pick up the new widget.", + inputSchema: z.object({ + widgetPath: z + .string() + .describe("Absolute path to the widget directory (the one containing package.json and dist/)") + }) + }, + async (args: { widgetPath: string }): Promise => { + if (!state.projectDir) { + return createStructuredErrorResponse( + createStructuredError("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { + suggestion: + "Call get-project-info to check the current configuration, " + + "or set-project-directory to configure a project directory." + }) + ); + } + + if (!isPathAllowed(args.widgetPath, state, "MCP_ALLOWED_BUILD_PATHS")) { + return createStructuredErrorResponse( + createStructuredError( + "ERR_NOT_FOUND", + `Widget path is not within an allowed directory: ${args.widgetPath}`, + { + suggestion: `Widget must be within ${GENERATIONS_DIR} or an allowed build path.` + } + ) + ); + } + + const mpkPath = findMpkFile(args.widgetPath); + if (!mpkPath) { + return createStructuredErrorResponse( + createStructuredError("ERR_MPK_NOT_FOUND", `No .mpk file found in ${args.widgetPath}/dist/`, { + suggestion: "Run build-widget first to compile the widget and produce the .mpk file." + }) + ); + } + + const widgetsDir = join(state.projectDir, "widgets"); + try { + await mkdir(widgetsDir, { recursive: true }); + const mpkFileName = basename(mpkPath); + const destPath = join(widgetsDir, mpkFileName); + await copyFile(mpkPath, destPath); + + return createToolResponse( + [ + `✅ Widget deployed successfully!`, + ``, + `Source: ${mpkPath}`, + `Destination: ${destPath}`, + ``, + `Synchronize the app directory in Studio Pro to pick up the new widget.` + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return createStructuredErrorResponse( + createStructuredError("ERR_DEPLOY_FAILED", `Failed to deploy widget: ${message}`, { + suggestion: "Check write permissions on the widgets directory.", + rawOutput: message + }) + ); + } + } + ); +} diff --git a/packages/pluggable-widgets-mcp/src/tools/session-state.ts b/packages/pluggable-widgets-mcp/src/tools/session-state.ts new file mode 100644 index 0000000000..b090c7aa79 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/session-state.ts @@ -0,0 +1,15 @@ +import { MENDIX_PROJECT_DIR } from "@/config"; + +export interface SessionState { + projectDir: string | undefined; +} + +/** + * Creates a new session state, initialized from the MENDIX_PROJECT_DIR env var (if set). + * Each MCP server instance gets its own state, so concurrent sessions are isolated. + */ +export function createSessionState(): SessionState { + return { + projectDir: MENDIX_PROJECT_DIR + }; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts b/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts new file mode 100644 index 0000000000..c0b14a6ac2 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts @@ -0,0 +1,30 @@ +import { existsSync, readdirSync } from "node:fs"; +import { join } from "node:path"; + +/** + * Finds the .mpk file in the widget's dist directory. + * Searches recursively (usually in dist/x.x.x/). + */ +export function findMpkFile(widgetPath: string): string | undefined { + const distPath = join(widgetPath, "dist"); + if (!existsSync(distPath)) return undefined; + + try { + const searchDir = (dir: string): string | undefined => { + const entries = readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + const found = searchDir(fullPath); + if (found) return found; + } else if (entry.name.endsWith(".mpk")) { + return fullPath; + } + } + return undefined; + }; + return searchDir(distPath); + } catch { + return undefined; + } +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts index e436113080..5469040388 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts @@ -15,7 +15,10 @@ export type ErrorCode = | "ERR_FILE_WRITE" // File write failure | "ERR_NOT_FOUND" // Resource not found | "ERR_OUTPUT_PATH_REQUIRED" // Output path required (e.g., in Claude Desktop) - | "ERR_OUTPUT_PATH_INVALID"; // Output path is not accessible + | "ERR_OUTPUT_PATH_INVALID" // Output path is not accessible + | "ERR_PROJECT_NOT_CONFIGURED" // Project directory not configured or invalid + | "ERR_MPK_NOT_FOUND" // Built .mpk file not found in dist/ + | "ERR_DEPLOY_FAILED"; // Failed to deploy .mpk to project widgets dir /** * Structured error with code, message, and optional details. From 2711875e1684a048010e97280487d72fca3776d3 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 27 Feb 2026 11:17:41 +0100 Subject: [PATCH 14/36] refactor(pluggable-widgets-mcp): extract shared sandbox utility and add project context to startup Extracts duplicated path-allowlist logic into shared isPathAllowed() in sandbox.ts. Adds project directory logging on HTTP/STDIO startup and exposes projectDir in /health endpoint for observability. Co-Authored-By: Claude Opus 4.6 --- .../pluggable-widgets-mcp/src/server/http.ts | 16 ++++- .../src/server/routes.ts | 7 ++- .../pluggable-widgets-mcp/src/server/stdio.ts | 15 +++++ .../src/tools/build.tools.ts | 58 +++++-------------- .../src/tools/scaffolding.tools.ts | 35 +++++------ .../src/tools/utils/sandbox.ts | 20 +++++++ 6 files changed, 84 insertions(+), 67 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts diff --git a/packages/pluggable-widgets-mcp/src/server/http.ts b/packages/pluggable-widgets-mcp/src/server/http.ts index 3f54654dba..6c867a7543 100644 --- a/packages/pluggable-widgets-mcp/src/server/http.ts +++ b/packages/pluggable-widgets-mcp/src/server/http.ts @@ -1,9 +1,22 @@ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; import cors from "cors"; -import { PORT } from "@/config"; +import { MENDIX_PROJECT_DIR, PORT, validateProjectDir } from "@/config"; import { setupRoutes } from "./routes"; import { sessionManager } from "./session"; +async function logProjectConfig(): Promise { + if (MENDIX_PROJECT_DIR) { + const validation = await validateProjectDir(MENDIX_PROJECT_DIR); + if (validation.valid) { + console.log(`[HTTP] Project: ${validation.projectName} (${MENDIX_PROJECT_DIR})`); + } else { + console.warn(`[HTTP] Warning: MENDIX_PROJECT_DIR is set but invalid: ${validation.error}`); + } + } else { + console.log(`[HTTP] No project configured (set MENDIX_PROJECT_DIR to enable deploy support)`); + } +} + /** * Starts the MCP server with HTTP/Streamable transport. * Supports multiple concurrent sessions via Express. @@ -26,6 +39,7 @@ export function startHttpServer(): void { console.log(`[HTTP] MCP Server started on port ${PORT}`); console.log(`[HTTP] Health check: http://localhost:${PORT}/health`); console.log(`[HTTP] MCP endpoint: http://localhost:${PORT}/mcp`); + logProjectConfig(); }); const shutdown = async (): Promise => { diff --git a/packages/pluggable-widgets-mcp/src/server/routes.ts b/packages/pluggable-widgets-mcp/src/server/routes.ts index 38cdbc1d64..fe32512f1e 100644 --- a/packages/pluggable-widgets-mcp/src/server/routes.ts +++ b/packages/pluggable-widgets-mcp/src/server/routes.ts @@ -1,6 +1,6 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; import type { Express, Request, Response } from "express"; -import { SERVER_NAME, SERVER_VERSION } from "@/config"; +import { MENDIX_PROJECT_DIR, SERVER_NAME, SERVER_VERSION } from "@/config"; import { createMcpServer } from "./server"; import { sessionManager } from "./session"; @@ -17,11 +17,14 @@ export function setupRoutes(app: Express): void { */ function setupHealthRoute(app: Express): void { app.get("/health", (_req: Request, res: Response) => { + const projectDir = MENDIX_PROJECT_DIR ?? null; res.json({ status: "ok", server: SERVER_NAME, version: SERVER_VERSION, - sessions: sessionManager.sessionCount + sessions: sessionManager.sessionCount, + projectDir, + widgetsDir: projectDir ? `${projectDir}/widgets` : null }); }); } diff --git a/packages/pluggable-widgets-mcp/src/server/stdio.ts b/packages/pluggable-widgets-mcp/src/server/stdio.ts index 96039e6cd6..c03696d206 100644 --- a/packages/pluggable-widgets-mcp/src/server/stdio.ts +++ b/packages/pluggable-widgets-mcp/src/server/stdio.ts @@ -1,6 +1,20 @@ +import { MENDIX_PROJECT_DIR, validateProjectDir } from "@/config"; import { createMcpServer } from "./server"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +async function logProjectConfig(): Promise { + if (MENDIX_PROJECT_DIR) { + const validation = await validateProjectDir(MENDIX_PROJECT_DIR); + if (validation.valid) { + console.error(`[STDIO] Project: ${validation.projectName} (${MENDIX_PROJECT_DIR})`); + } else { + console.error(`[STDIO] Warning: MENDIX_PROJECT_DIR is set but invalid: ${validation.error}`); + } + } else { + console.error(`[STDIO] No project configured (set MENDIX_PROJECT_DIR to enable deploy support)`); + } +} + /** * Starts the MCP server with STDIO transport. * Communicates via stdin/stdout for CLI-based MCP clients. @@ -11,6 +25,7 @@ export async function startStdioServer(): Promise { // Log to stderr since stdout is used for MCP communication console.error("[STDIO] Starting MCP server..."); + await logProjectConfig(); await server.connect(transport); diff --git a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts index 2773a52ca6..275f5bfaf6 100644 --- a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts @@ -5,8 +5,8 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { spawn } from "node:child_process"; -import { existsSync, readdirSync } from "node:fs"; -import { join, resolve } from "node:path"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; import { z } from "zod"; import { GENERATIONS_DIR } from "@/config"; import type { ToolContext, ToolResponse } from "./types"; @@ -17,6 +17,9 @@ import { createToolResponse, type StructuredError } from "./utils/response"; +import { findMpkFile } from "./utils/mpk"; +import { isPathAllowed } from "./utils/sandbox"; +import type { SessionState } from "./session-state"; /** * Input schema for build-widget tool. @@ -306,38 +309,14 @@ function toStructuredError(error: ParsedError): StructuredError { }); } -/** - * Finds the most recently created MPK file in the widget's dist directory. - */ -function findMpkFile(widgetPath: string): string | undefined { - const distPath = join(widgetPath, "dist"); - if (!existsSync(distPath)) return undefined; - - try { - // Search for .mpk files recursively (usually in dist/x.x.x/) - const searchDir = (dir: string): string | undefined => { - const entries = readdirSync(dir, { withFileTypes: true }); - for (const entry of entries) { - const fullPath = join(dir, entry.name); - if (entry.isDirectory()) { - const found = searchDir(fullPath); - if (found) return found; - } else if (entry.name.endsWith(".mpk")) { - return fullPath; - } - } - return undefined; - }; - return searchDir(distPath); - } catch { - return undefined; - } -} - /** * Handler for the build-widget tool. */ -async function handleBuildWidget(args: BuildWidgetInput, context: ToolContext): Promise { +async function handleBuildWidget( + args: BuildWidgetInput, + context: ToolContext, + state: SessionState +): Promise { const { widgetPath } = args; // Validate path exists @@ -350,18 +329,7 @@ async function handleBuildWidget(args: BuildWidgetInput, context: ToolContext): } // Validate path is within allowed directories - const resolvedWidgetPath = resolve(widgetPath); - const allowedBuildPaths = [ - resolve(GENERATIONS_DIR), - ...(process.env.MCP_ALLOWED_BUILD_PATHS ?? "") - .split(":") - .filter(Boolean) - .map(p => resolve(p)) - ]; - const isAllowedPath = allowedBuildPaths.some( - allowed => resolvedWidgetPath.startsWith(allowed + "/") || resolvedWidgetPath === allowed - ); - if (!isAllowedPath) { + if (!isPathAllowed(widgetPath, state, "MCP_ALLOWED_BUILD_PATHS")) { return createStructuredErrorResponse( createStructuredError("ERR_NOT_FOUND", `Widget path is not within an allowed directory: ${widgetPath}`, { suggestion: `Widget must be within ${GENERATIONS_DIR} or set MCP_ALLOWED_BUILD_PATHS env var (colon-separated paths).` @@ -447,7 +415,7 @@ async function handleBuildWidget(args: BuildWidgetInput, context: ToolContext): /** * Registers the build tools with the MCP server. */ -export function registerBuildTools(server: McpServer): void { +export function registerBuildTools(server: McpServer, state: SessionState): void { server.registerTool( "build-widget", { @@ -458,6 +426,6 @@ export function registerBuildTools(server: McpServer): void { "Returns build errors if any, which can be used to fix issues.", inputSchema: buildWidgetSchema }, - handleBuildWidget + (args, context) => handleBuildWidget(args, context, state) ); } diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 6c216eb0f2..57101de6d1 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -10,8 +10,10 @@ import { type ErrorCode } from "@/tools/utils/response"; import { access, mkdir } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; +import { dirname } from "node:path"; import { z } from "zod"; +import { isPathAllowed } from "./utils/sandbox"; +import type { SessionState } from "./session-state"; /** * Schema for create-widget tool input. @@ -22,7 +24,7 @@ const createWidgetSchema = widgetOptionsSchema.extend({ .string() .optional() .describe( - "[OPTIONAL] Directory where widget will be created. Defaults to ./generations/ in the current working directory. For desktop clients without a clear working directory, ask the user for their preferred location." + "[OPTIONAL] Directory where widget will be created. Defaults to ./generations/ in the current working directory. Leave unset in most cases — the server manages the output location." ) }); @@ -45,9 +47,11 @@ OPTIONAL (with defaults): • programmingLanguage: "typescript" or "javascript" (default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}") • unitTests: Include Jest test setup (default: ${DEFAULT_WIDGET_OPTIONS.unitTests}) • e2eTests: Include Playwright E2E tests (default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}) - • outputPath: Directory where widget will be created (default: ./generations/) + • outputPath: Directory where widget will be created (default: ./generations/). Leave unset in most cases. -Ask the user if they want to customize any options before proceeding.`; +Ask the user if they want to customize any options before proceeding. + +After scaffolding, use build-widget to compile, then deploy-widget to copy the .mpk to the Mendix project.`; /** * Registers scaffolding-related tools for widget creation and management. @@ -60,7 +64,7 @@ Ask the user if they want to customize any options before proceeding.`; * * @see AGENTS.md Roadmap Context section for planned additions */ -export function registerScaffoldingTools(server: McpServer): void { +export function registerScaffoldingTools(server: McpServer, state: SessionState): void { server.registerTool( "create-widget", { @@ -68,28 +72,21 @@ export function registerScaffoldingTools(server: McpServer): void { description: CREATE_WIDGET_DESCRIPTION, inputSchema: createWidgetSchema }, - handleCreateWidget + (args, context) => handleCreateWidget(args, context, state) ); } -async function handleCreateWidget(args: CreateWidgetInput, context: ToolContext): Promise { +async function handleCreateWidget( + args: CreateWidgetInput, + context: ToolContext, + state: SessionState +): Promise { const options = buildWidgetOptions(args); const outputDir = args.outputPath ?? GENERATIONS_DIR; // Validate user-provided outputPath is within allowed directories if (args.outputPath) { - const resolvedOutputPath = resolve(args.outputPath); - const allowedOutputPaths = [ - resolve(GENERATIONS_DIR), - ...(process.env.MCP_ALLOWED_OUTPUT_PATHS ?? "") - .split(":") - .filter(Boolean) - .map(p => resolve(p)) - ]; - const isAllowedPath = allowedOutputPaths.some( - allowed => resolvedOutputPath.startsWith(allowed + "/") || resolvedOutputPath === allowed - ); - if (!isAllowedPath) { + if (!isPathAllowed(args.outputPath, state, "MCP_ALLOWED_OUTPUT_PATHS")) { return createStructuredErrorResponse( createStructuredError( "ERR_OUTPUT_PATH_INVALID", diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts b/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts new file mode 100644 index 0000000000..2546890c7e --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts @@ -0,0 +1,20 @@ +import { resolve } from "node:path"; +import { GENERATIONS_DIR } from "@/config"; +import type { SessionState } from "@/tools/session-state"; + +/** + * Checks whether a resolved path is within the allowed directories. + * Allowed dirs: GENERATIONS_DIR, env-var paths (colon-separated), state.projectDir. + */ +export function isPathAllowed(targetPath: string, state: SessionState, envVar?: string): boolean { + const resolved = resolve(targetPath); + const allowed = [ + resolve(GENERATIONS_DIR), + ...((envVar ? process.env[envVar] : undefined) ?? "") + .split(":") + .filter(Boolean) + .map(p => resolve(p)), + ...(state.projectDir ? [resolve(state.projectDir)] : []) + ]; + return allowed.some(a => resolved.startsWith(a + "/") || resolved === a); +} From 5a2db95706d88e84a0859fb79becf4f12d0bc6a5 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 27 Feb 2026 11:18:06 +0100 Subject: [PATCH 15/36] test(pluggable-widgets-mcp): add vitest infrastructure and unit tests Adds vitest config, MCP test harness (in-memory transport), temp directory helpers, and 55 unit tests covering config validation, security guardrails, project tools, scaffolding/build sandbox, session state, MPK finder, and response utilities. Co-Authored-By: Claude Opus 4.6 --- .../src/__test-utils__/mcp-test-harness.ts | 67 ++++++ .../src/__test-utils__/temp-dir.ts | 76 ++++++ .../src/__tests__/config.test.ts | 70 ++++++ .../src/security/__tests__/guardrails.test.ts | 53 +++++ .../src/tools/__tests__/build.tools.test.ts | 59 +++++ .../src/tools/__tests__/project.tools.test.ts | 220 ++++++++++++++++++ .../tools/__tests__/scaffolding.tools.test.ts | 77 ++++++ .../src/tools/__tests__/session-state.test.ts | 36 +++ .../src/tools/utils/__tests__/mpk.test.ts | 48 ++++ .../tools/utils/__tests__/response.test.ts | 84 +++++++ .../pluggable-widgets-mcp/vitest.config.ts | 12 + 11 files changed, 802 insertions(+) create mode 100644 packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts create mode 100644 packages/pluggable-widgets-mcp/src/__test-utils__/temp-dir.ts create mode 100644 packages/pluggable-widgets-mcp/src/__tests__/config.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts create mode 100644 packages/pluggable-widgets-mcp/vitest.config.ts diff --git a/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts b/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts new file mode 100644 index 0000000000..e88ce8576c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts @@ -0,0 +1,67 @@ +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { SessionState } from "@/tools/session-state"; + +type ToolRegistrationFn = (server: McpServer, state: SessionState) => void; + +interface McpTestContext { + server: McpServer; + client: Client; + state: SessionState; + cleanup: () => Promise; +} + +/** + * Creates an MCP server + client pair connected via InMemoryTransport. + * By default registers `registerProjectTools`; callers can pass custom registration functions. + */ +export function getResultText(result: Awaited>): string { + if ("content" in result && Array.isArray(result.content)) { + return result.content + .filter((c): c is { type: "text"; text: string } => c.type === "text") + .map(c => c.text) + .join("\n"); + } + return ""; +} + +export function isError(result: Awaited>): boolean { + return "isError" in result && result.isError === true; +} + +export async function createMcpTestContext(...registerFns: ToolRegistrationFn[]): Promise { + const state: SessionState = { projectDir: undefined }; + + const server = new McpServer( + { name: "test-server", version: "0.0.0" }, + { capabilities: { tools: {}, logging: {} } } + ); + + // Register tools — default to project tools if none provided + if (registerFns.length === 0) { + const { registerProjectTools } = await import("@/tools/project.tools"); + registerProjectTools(server, state); + } else { + for (const fn of registerFns) { + fn(server, state); + } + } + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + const client = new Client({ name: "test-client", version: "0.0.0" }); + + await server.connect(serverTransport); + await client.connect(clientTransport); + + return { + server, + client, + state, + cleanup: async () => { + await client.close(); + await server.close(); + } + }; +} diff --git a/packages/pluggable-widgets-mcp/src/__test-utils__/temp-dir.ts b/packages/pluggable-widgets-mcp/src/__test-utils__/temp-dir.ts new file mode 100644 index 0000000000..47d6b9cdee --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__test-utils__/temp-dir.ts @@ -0,0 +1,76 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; + +interface TempMendixProjectOptions { + projectName?: string; + widgets?: string[]; + skipMpr?: boolean; + skipWidgetsDir?: boolean; +} + +interface TempWidgetOptions { + mpkName?: string; + versionDir?: string; + noMpk?: boolean; + noDist?: boolean; +} + +interface TempDirResult { + dir: string; + cleanup: () => void; +} + +/** + * Creates a temp directory that looks like a Mendix project. + * Includes a .mpr file and optionally a widgets/ dir with .mpk files. + */ +export function createTempMendixProject(opts: TempMendixProjectOptions = {}): TempDirResult { + const dir = mkdtempSync(join(tmpdir(), "mcp-test-project-")); + const projectName = opts.projectName ?? "TestProject"; + + if (!opts.skipMpr) { + writeFileSync(join(dir, `${projectName}.mpr`), ""); + } + + if (!opts.skipWidgetsDir) { + const widgetsDir = join(dir, "widgets"); + mkdirSync(widgetsDir, { recursive: true }); + + if (opts.widgets) { + for (const w of opts.widgets) { + writeFileSync(join(widgetsDir, w), ""); + } + } + } + + return { + dir, + cleanup: () => rmSync(dir, { recursive: true, force: true }) + }; +} + +/** + * Creates a temp directory that looks like a built widget. + * Includes a dist/ dir with an optional .mpk file (possibly nested in a version dir). + */ +export function createTempWidgetWithMpk(opts: TempWidgetOptions = {}): TempDirResult { + const dir = mkdtempSync(join(tmpdir(), "mcp-test-widget-")); + const mpkName = opts.mpkName ?? "TestWidget.mpk"; + + if (!opts.noDist) { + const distDir = join(dir, "dist"); + mkdirSync(distDir, { recursive: true }); + + if (!opts.noMpk) { + const mpkDir = opts.versionDir ? join(distDir, opts.versionDir) : distDir; + mkdirSync(mpkDir, { recursive: true }); + writeFileSync(join(mpkDir, mpkName), "fake-mpk-content"); + } + } + + return { + dir, + cleanup: () => rmSync(dir, { recursive: true, force: true }) + }; +} diff --git a/packages/pluggable-widgets-mcp/src/__tests__/config.test.ts b/packages/pluggable-widgets-mcp/src/__tests__/config.test.ts new file mode 100644 index 0000000000..d9dd60d71e --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__tests__/config.test.ts @@ -0,0 +1,70 @@ +import { join } from "node:path"; +import { writeFileSync } from "node:fs"; +import { afterEach, describe, expect, it } from "vitest"; +import { validateProjectDir } from "@/config"; +import { createTempMendixProject } from "@/__test-utils__/temp-dir"; + +describe("validateProjectDir", () => { + const cleanups: Array<() => void> = []; + + afterEach(() => { + for (const cleanup of cleanups) cleanup(); + cleanups.length = 0; + }); + + it("returns invalid for a non-existent directory", async () => { + const result = await validateProjectDir("/nonexistent/path/to/project"); + expect(result.valid).toBe(false); + expect(result.error).toContain("does not exist"); + }); + + it("returns invalid when directory has no .mpr file", async () => { + const { dir, cleanup } = createTempMendixProject({ skipMpr: true }); + cleanups.push(cleanup); + const result = await validateProjectDir(dir); + expect(result.valid).toBe(false); + expect(result.error).toContain("No .mpr file"); + }); + + it("returns valid with correct projectName for a proper Mendix project", async () => { + const { dir, cleanup } = createTempMendixProject({ projectName: "MyApp" }); + cleanups.push(cleanup); + const result = await validateProjectDir(dir); + expect(result.valid).toBe(true); + expect(result.projectName).toBe("MyApp"); + }); + + it("sets widgetsDir to /widgets", async () => { + const { dir, cleanup } = createTempMendixProject(); + cleanups.push(cleanup); + const result = await validateProjectDir(dir); + expect(result.widgetsDir).toBe(join(dir, "widgets")); + }); + + it("lists .mpk files from widgets/ directory", async () => { + const { dir, cleanup } = createTempMendixProject({ + widgets: ["Foo.mpk", "Bar.mpk"] + }); + cleanups.push(cleanup); + const result = await validateProjectDir(dir); + expect(result.existingWidgets).toContain("Foo.mpk"); + expect(result.existingWidgets).toContain("Bar.mpk"); + }); + + it("returns empty existingWidgets when widgets/ dir does not exist", async () => { + const { dir, cleanup } = createTempMendixProject({ skipWidgetsDir: true }); + cleanups.push(cleanup); + const result = await validateProjectDir(dir); + expect(result.valid).toBe(true); + expect(result.existingWidgets).toEqual([]); + }); + + it("filters out non-.mpk files from widgets/", async () => { + const { dir, cleanup } = createTempMendixProject({ widgets: ["Widget.mpk"] }); + cleanups.push(cleanup); + // Add a non-mpk file + writeFileSync(join(dir, "widgets", "readme.txt"), "not a widget"); + const result = await validateProjectDir(dir); + expect(result.existingWidgets).toEqual(["Widget.mpk"]); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts new file mode 100644 index 0000000000..b249fdca9c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from "vitest"; +import { isPathWithinDirectory, isExtensionAllowed, validateFilePath } from "@/security/guardrails"; + +describe("isPathWithinDirectory", () => { + it("returns true for a path within the base directory", () => { + expect(isPathWithinDirectory("/widgets/foo", "src/Bar.tsx")).toBe(true); + }); + + it("returns true for a nested path within the base directory", () => { + expect(isPathWithinDirectory("/widgets/foo", "src/components/deep/Bar.tsx")).toBe(true); + }); + + it("returns false for .. traversal escaping the base", () => { + expect(isPathWithinDirectory("/widgets/foo", "../../../etc/passwd")).toBe(false); + }); + + it("returns false for a sibling directory (foobar vs foo)", () => { + // /widgets/foobar is NOT within /widgets/foo (prefix attack) + expect(isPathWithinDirectory("/widgets/foo", "../foobar/secret.txt")).toBe(false); + }); +}); + +describe("isExtensionAllowed", () => { + it.each([".tsx", ".ts", ".xml", ".scss", ".json"])("allows %s extension", ext => { + expect(isExtensionAllowed(`Component${ext}`)).toBe(true); + }); + + it.each([".exe", ".sh"])("rejects %s extension", ext => { + expect(isExtensionAllowed(`script${ext}`)).toBe(false); + }); + + it("allows .gitignore (dot-file in allowlist)", () => { + expect(isExtensionAllowed(".gitignore")).toBe(true); + }); + + it("allows extensionless config files like tsconfig", () => { + expect(isExtensionAllowed("tsconfig")).toBe(true); + }); +}); + +describe("validateFilePath", () => { + it("does not throw for a valid path without extension check", () => { + expect(() => validateFilePath("/widgets/foo", "src/Bar.tsx")).not.toThrow(); + }); + + it("throws for .. in the path", () => { + expect(() => validateFilePath("/widgets/foo", "../secret.txt")).toThrow("Path traversal"); + }); + + it("throws for disallowed extension when checkExtension is true", () => { + expect(() => validateFilePath("/widgets/foo", "src/script.exe", true)).toThrow("extension not allowed"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts new file mode 100644 index 0000000000..5a5214d76e --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts @@ -0,0 +1,59 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; +import { createTempMendixProject } from "@/__test-utils__/temp-dir"; +import { registerBuildTools } from "@/tools/build.tools"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { SessionState } from "@/tools/session-state"; + +describe("build-widget sandbox expansion", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext(registerBuildTools)); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("rejects widget path outside allowed directories", async () => { + // Create a real temp dir (must exist to pass the existsSync check) + const rogueDir = mkdtempSync(join(tmpdir(), "mcp-test-rogue-")); + tempCleanups.push(() => rmSync(rogueDir, { recursive: true, force: true })); + state.projectDir = undefined; + const result = await client.callTool({ + name: "build-widget", + arguments: { widgetPath: rogueDir } + }); + const text = getResultText(result); + expect(isError(result)).toBe(true); + expect(text).toContain("not within an allowed directory"); + }); + + it("allows widget path within state.projectDir", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + + // Create a fake widget dir inside the project with a package.json + const widgetDir = join(dir, "my-widget"); + mkdirSync(widgetDir, { recursive: true }); + writeFileSync(join(widgetDir, "package.json"), '{"name":"my-widget"}'); + + const result = await client.callTool({ + name: "build-widget", + arguments: { widgetPath: widgetDir } + }); + const text = getResultText(result); + // Path check passed — build itself will fail (no real widget), but NOT with sandbox error + expect(text).not.toContain("not within an allowed directory"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts new file mode 100644 index 0000000000..f4fdecd7b3 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts @@ -0,0 +1,220 @@ +import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; +import { createTempMendixProject } from "@/__test-utils__/temp-dir"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { SessionState } from "@/tools/session-state"; + +describe("get-project-info", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext()); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("returns error when no project is configured", async () => { + state.projectDir = undefined; + const result = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_PROJECT_NOT_CONFIGURED"); + }); + + it("returns error when project dir is invalid", async () => { + state.projectDir = "/nonexistent/project"; + const result = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(result)).toBe(true); + const text = getResultText(result); + expect(text).toContain("ERR_PROJECT_NOT_CONFIGURED"); + expect(text).toContain("invalid"); + }); + + it("returns project info for a valid project", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject({ + projectName: "DemoApp", + widgets: ["SomeWidget.mpk"] + }); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + + const result = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(result)).toBe(false); + const text = getResultText(result); + expect(text).toContain("DemoApp"); + expect(text).toContain("SomeWidget.mpk"); + }); +}); + +describe("set-project-directory", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext()); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("sets project dir for a valid Mendix project", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + + const result = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: dir } + }); + expect(isError(result)).toBe(false); + expect(state.projectDir).toBe(dir); + }); + + it("returns error for non-existent directory", async () => { + const originalDir = state.projectDir; + const result = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: "/nope/not/real" } + }); + expect(isError(result)).toBe(true); + expect(state.projectDir).toBe(originalDir); + }); + + it("returns error for directory without .mpr file", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject({ skipMpr: true }); + tempCleanups.push(tempCleanup); + + const result = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: dir } + }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("No .mpr"); + }); +}); + +describe("deploy-widget", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext()); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("returns error when no project is configured", async () => { + state.projectDir = undefined; + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: "/some/widget" } + }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_PROJECT_NOT_CONFIGURED"); + }); + + it("returns error when widget has no .mpk in dist/", async () => { + const { dir: projectDir, cleanup: pc } = createTempMendixProject(); + tempCleanups.push(pc); + state.projectDir = projectDir; + // Create widget dir inside projectDir (passes sandbox) with empty dist/ + const widgetDir = join(projectDir, "my-widget"); + mkdirSync(join(widgetDir, "dist"), { recursive: true }); + + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: widgetDir } + }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_MPK_NOT_FOUND"); + }); + + it("returns error when widget has no dist/ at all", async () => { + const { dir: projectDir, cleanup: pc } = createTempMendixProject(); + tempCleanups.push(pc); + state.projectDir = projectDir; + // Create widget dir inside projectDir (passes sandbox) with no dist/ + const widgetDir = join(projectDir, "my-widget"); + mkdirSync(widgetDir, { recursive: true }); + + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: widgetDir } + }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_MPK_NOT_FOUND"); + }); + + it("deploys .mpk to project widgets/ directory", async () => { + const { dir: projectDir, cleanup: pc } = createTempMendixProject(); + tempCleanups.push(pc); + state.projectDir = projectDir; + // Create widget dir inside projectDir with a .mpk + const widgetDir = join(projectDir, "my-widget"); + mkdirSync(join(widgetDir, "dist"), { recursive: true }); + writeFileSync(join(widgetDir, "dist", "Cool.mpk"), "fake-mpk-content"); + + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: widgetDir } + }); + expect(isError(result)).toBe(false); + expect(getResultText(result)).toContain("deployed"); + expect(existsSync(join(projectDir, "widgets", "Cool.mpk"))).toBe(true); + }); + + it("creates widgets/ directory if it does not exist", async () => { + const { dir: projectDir, cleanup: pc } = createTempMendixProject({ + skipWidgetsDir: true + }); + tempCleanups.push(pc); + state.projectDir = projectDir; + // Create widget dir inside projectDir with a .mpk + const widgetDir = join(projectDir, "my-widget"); + mkdirSync(join(widgetDir, "dist"), { recursive: true }); + writeFileSync(join(widgetDir, "dist", "New.mpk"), "fake-mpk-content"); + + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: widgetDir } + }); + expect(isError(result)).toBe(false); + expect(existsSync(join(projectDir, "widgets", "New.mpk"))).toBe(true); + }); + + it("rejects widgetPath outside allowed directories", async () => { + const { dir: projectDir, cleanup: pc } = createTempMendixProject(); + // Create a rogue dir outside projectDir and GENERATIONS_DIR + const rogueDir = mkdtempSync(join(tmpdir(), "mcp-test-rogue-")); + mkdirSync(join(rogueDir, "dist"), { recursive: true }); + writeFileSync(join(rogueDir, "dist", "Evil.mpk"), "fake-mpk-content"); + tempCleanups.push(pc, () => rmSync(rogueDir, { recursive: true, force: true })); + state.projectDir = projectDir; + + const result = await client.callTool({ + name: "deploy-widget", + arguments: { widgetPath: rogueDir } + }); + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("not within an allowed directory"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts new file mode 100644 index 0000000000..e28f837d3e --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts @@ -0,0 +1,77 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { createMcpTestContext, getResultText } from "@/__test-utils__/mcp-test-harness"; +import { createTempMendixProject } from "@/__test-utils__/temp-dir"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { SessionState } from "@/tools/session-state"; + +// Mock the generator so the tool returns immediately after path validation +vi.mock("@/tools/utils/generator", () => ({ + buildWidgetOptions: (args: Record) => ({ + name: args.name ?? "TestWidget", + description: args.description ?? "test", + version: "1.0.0", + author: "Mendix", + license: "Apache-2.0", + organization: "Mendix", + template: "empty", + programmingLanguage: "typescript", + unitTests: true, + e2eTests: false + }), + runWidgetGenerator: vi.fn().mockResolvedValue(undefined), + SCAFFOLD_PROGRESS: { + START: { progress: 0, message: "Starting..." }, + COMPLETE: { progress: 100, message: "Done!" } + } +})); + +describe("create-widget sandbox expansion", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + const { registerScaffoldingTools } = await import("@/tools/scaffolding.tools"); + ({ client, state, cleanup } = await createMcpTestContext(registerScaffoldingTools)); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("rejects outputPath outside all allowed directories", async () => { + state.projectDir = undefined; + const result = await client.callTool({ + name: "create-widget", + arguments: { + name: "TestWidget", + description: "test", + outputPath: "/tmp/evil-path" + } + }); + const text = getResultText(result); + expect(text).toContain("ERR_OUTPUT_PATH_INVALID"); + }); + + it("allows outputPath within state.projectDir", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + + const result = await client.callTool({ + name: "create-widget", + arguments: { + name: "TestWidget", + description: "test", + outputPath: dir + "/sub" + } + }); + const text = getResultText(result); + // Path check passed — the mocked generator runs instantly. + expect(text).not.toContain("ERR_OUTPUT_PATH_INVALID"); + expect(text).toContain("created successfully"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts new file mode 100644 index 0000000000..90694fcc3c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts @@ -0,0 +1,36 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +describe("createSessionState", () => { + afterEach(() => { + // resetModules required because vi.doMock re-registers per test + vi.resetModules(); + }); + + it("returns undefined projectDir when MENDIX_PROJECT_DIR is not set", async () => { + vi.doMock("@/config", () => ({ + MENDIX_PROJECT_DIR: undefined + })); + const { createSessionState } = await import("@/tools/session-state"); + const state = createSessionState(); + expect(state.projectDir).toBeUndefined(); + }); + + it("returns resolved projectDir when MENDIX_PROJECT_DIR is set", async () => { + vi.doMock("@/config", () => ({ + MENDIX_PROJECT_DIR: "/resolved/path" + })); + const { createSessionState } = await import("@/tools/session-state"); + const state = createSessionState(); + expect(state.projectDir).toBe("/resolved/path"); + }); + + it("returns mutable state", async () => { + vi.doMock("@/config", () => ({ + MENDIX_PROJECT_DIR: undefined + })); + const { createSessionState } = await import("@/tools/session-state"); + const state = createSessionState(); + state.projectDir = "/new/path"; + expect(state.projectDir).toBe("/new/path"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk.test.ts new file mode 100644 index 0000000000..a31ca9350f --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk.test.ts @@ -0,0 +1,48 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { findMpkFile } from "@/tools/utils/mpk"; +import { createTempWidgetWithMpk } from "@/__test-utils__/temp-dir"; + +describe("findMpkFile", () => { + const cleanups: Array<() => void> = []; + + afterEach(() => { + for (const cleanup of cleanups) cleanup(); + cleanups.length = 0; + }); + + it("returns undefined when dist/ does not exist", () => { + const { dir, cleanup } = createTempWidgetWithMpk({ noDist: true }); + cleanups.push(cleanup); + expect(findMpkFile(dir)).toBeUndefined(); + }); + + it("returns undefined when dist/ exists but has no .mpk", () => { + const { dir, cleanup } = createTempWidgetWithMpk({ noMpk: true }); + cleanups.push(cleanup); + expect(findMpkFile(dir)).toBeUndefined(); + }); + + it("finds .mpk directly in dist/", () => { + const { dir, cleanup } = createTempWidgetWithMpk({ mpkName: "MyWidget.mpk" }); + cleanups.push(cleanup); + const result = findMpkFile(dir); + expect(result).toBeDefined(); + expect(result).toContain("MyWidget.mpk"); + }); + + it("finds .mpk in a nested version directory (dist/1.0.0/)", () => { + const { dir, cleanup } = createTempWidgetWithMpk({ + mpkName: "MyWidget.mpk", + versionDir: "1.0.0" + }); + cleanups.push(cleanup); + const result = findMpkFile(dir); + expect(result).toBeDefined(); + expect(result).toContain("1.0.0"); + expect(result).toContain("MyWidget.mpk"); + }); + + it("returns undefined when widget path does not exist", () => { + expect(findMpkFile("/nonexistent/widget/path")).toBeUndefined(); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts new file mode 100644 index 0000000000..709a4cb149 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from "vitest"; +import { + createToolResponse, + createErrorResponse, + createStructuredError, + createStructuredErrorResponse +} from "@/tools/utils/response"; + +describe("createToolResponse", () => { + it("returns content with text and no isError", () => { + const result = createToolResponse("hello"); + expect(result).toEqual({ + content: [{ type: "text", text: "hello" }] + }); + expect(result).not.toHaveProperty("isError"); + }); +}); + +describe("createErrorResponse", () => { + it("sets isError to true", () => { + const result = createErrorResponse("something broke"); + expect(result.isError).toBe(true); + expect(result.content[0].text).toBe("something broke"); + }); +}); + +describe("createStructuredError", () => { + it("creates an error with code and message", () => { + const err = createStructuredError("ERR_NOT_FOUND", "Widget not found"); + expect(err.code).toBe("ERR_NOT_FOUND"); + expect(err.message).toBe("Widget not found"); + expect(err.suggestion).toBeUndefined(); + expect(err.details).toBeUndefined(); + }); + + it("includes optional suggestion and details", () => { + const err = createStructuredError("ERR_BUILD_TS", "Type error", { + suggestion: "Check types", + file: "src/Foo.tsx", + line: 42, + rawOutput: "raw stuff" + }); + expect(err.suggestion).toBe("Check types"); + expect(err.details?.file).toBe("src/Foo.tsx"); + expect(err.details?.line).toBe(42); + expect(err.details?.rawOutput).toBe("raw stuff"); + }); +}); + +describe("createStructuredErrorResponse", () => { + it("formats header with [CODE] message", () => { + const resp = createStructuredErrorResponse(createStructuredError("ERR_NOT_FOUND", "Widget missing")); + const text = resp.content[0].text; + expect(text).toContain("[ERR_NOT_FOUND]"); + expect(text).toContain("Widget missing"); + }); + + it("includes file location line when details.file is set", () => { + const resp = createStructuredErrorResponse( + createStructuredError("ERR_BUILD_TS", "Type error", { + file: "src/Foo.tsx", + line: 10, + column: 5 + }) + ); + const text = resp.content[0].text; + expect(text).toContain("src/Foo.tsx:10:5"); + }); + + it("truncates rawOutput longer than 500 chars", () => { + const longOutput = "x".repeat(600); + const resp = createStructuredErrorResponse( + createStructuredError("ERR_BUILD_UNKNOWN", "fail", { rawOutput: longOutput }) + ); + const text = resp.content[0].text; + expect(text).toContain("...(truncated)"); + expect(text).not.toContain("x".repeat(600)); + }); + + it("sets isError to true", () => { + const resp = createStructuredErrorResponse(createStructuredError("ERR_NOT_FOUND", "gone")); + expect(resp.isError).toBe(true); + }); +}); diff --git a/packages/pluggable-widgets-mcp/vitest.config.ts b/packages/pluggable-widgets-mcp/vitest.config.ts new file mode 100644 index 0000000000..873146deac --- /dev/null +++ b/packages/pluggable-widgets-mcp/vitest.config.ts @@ -0,0 +1,12 @@ +import tsconfigPaths from "vite-tsconfig-paths"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + globals: false, + include: ["src/**/__tests__/*.test.ts"], + testTimeout: 10_000, + restoreMocks: true + } +}); From ac782162a6fb93d04478040c515bc013d331da08 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 27 Feb 2026 11:18:29 +0100 Subject: [PATCH 16/36] =?UTF-8?q?fix(pluggable-widgets-mcp):=20fix=20E2E?= =?UTF-8?q?=20pipeline=20=E2=80=94=20name=20passing,=20scaffold=20cleanup,?= =?UTF-8?q?=20build=20parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes 6 issues from E2E testing to make the create → generate → build pipeline work end-to-end: - Pass widget name via --name flag instead of positional arg (defense-in-depth) - Read widgetName from package.json instead of deriving from directory basename - Clean up stale scaffold files and regenerate package.xml before code generation - Only import executeAction for TSX patterns that actually use it (fixes TS6133) - Parse Rollup TypeScript error format with file location correlation - Switch generator-widget to local file: reference for development Co-Authored-By: Claude Opus 4.6 --- packages/pluggable-widgets-mcp/package.json | 11 +- .../src/generators/tsx-generator.ts | 16 +- .../src/tools/build.tools.ts | 42 ++- .../src/tools/code-generation.tools.ts | 121 ++++++- .../src/tools/utils/generator.ts | 4 +- pnpm-lock.yaml | 301 ++++++++++++++++-- 6 files changed, 452 insertions(+), 43 deletions(-) diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index 28260f69db..d21d2cb1ec 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -20,10 +20,12 @@ "lint": "eslint src/ package.json", "start": "pnpm run build && node dist/index.js", "start:http": "pnpm run build && node dist/index.js http", - "start:stdio": "pnpm run build && node dist/index.js stdio" + "start:stdio": "pnpm run build && node dist/index.js stdio", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { - "@mendix/generator-widget": "github:rahmanunver/widgets-tools#generator-widget-noninteractive-defaults&path:packages/generator-widget", + "@mendix/generator-widget": "file:../../../widgets-tools/packages/generator-widget", "@modelcontextprotocol/sdk": "^1.24.2", "cors": "^2.8.5", "express": "^5.1.0", @@ -35,7 +37,10 @@ "@types/node": "^22.0.0", "tsc-alias": "^1.8.16", "tsx": "^4.21.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vite": "^4.5.14", + "vite-tsconfig-paths": "^4.3.2", + "vitest": "^0.34.6" }, "keywords": [], "packageManager": "pnpm@10.17.0", diff --git a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts index 8778a1855e..ae8f0ccab4 100644 --- a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts @@ -105,8 +105,22 @@ function generateImports(widgetName: string, properties: PropertyDefinition[], p imports.push(`import { ${Array.from(reactImports).sort().join(", ")} } from "react";`); - // Mendix imports + // Mendix imports — only import executeAction when the pattern actually uses it + let needsExecuteAction = false; if (hasAction) { + if (pattern === "display" || pattern === "button") { + // These patterns use generateActionHandler for all action props + needsExecuteAction = true; + } else if (pattern === "input") { + // Input pattern only uses executeAction if there's a "change" action + needsExecuteAction = properties.some(p => p.type === "action" && p.key.toLowerCase().includes("change")); + } else if (pattern === "dataList") { + // DataList pattern uses executeAction for item click actions + needsExecuteAction = properties.some(p => p.type === "action" && p.key.toLowerCase().includes("item")); + } + // container pattern doesn't use executeAction (uses useState for toggle) + } + if (needsExecuteAction) { imports.push('import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";'); } if (hasDatasource) { diff --git a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts index 275f5bfaf6..eb96eda6c5 100644 --- a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts @@ -64,6 +64,18 @@ const TS_ERROR_PATTERN = /^(.+?)[:(](\d+)[,:](\d+)[):]?\s*[-:]?\s*error\s+(TS\d+ */ const TS_ERROR_SIMPLE_PATTERN = /^error\s+(TS\d+):\s*(.+)$/; +/** + * Rollup TS error pattern from pluggable-widgets-tools build: + * (plugin typescript) RollupError: @rollup/plugin-typescript TS6133: 'executeAction' is declared but its value is never read. + */ +const ROLLUP_TS_ERROR_PATTERN = /RollupError:.*?(TS\d+):\s*(.+)/; + +/** + * File location pattern that follows Rollup errors on the next line: + * src/CounterTwo.tsx (2:1) + */ +const ROLLUP_FILE_LOCATION_PATTERN = /^(.+?\.\w+)\s+\((\d+):(\d+)\)$/; + /** * XML error patterns */ @@ -101,6 +113,16 @@ function parseTypeScriptError(line: string): ParsedError | null { }; } + // Try Rollup TS error pattern + const rollupMatch = line.match(ROLLUP_TS_ERROR_PATTERN); + if (rollupMatch) { + return { + tsCode: rollupMatch[1], + message: rollupMatch[2], + category: "typescript" + }; + } + return null; } @@ -115,12 +137,13 @@ function parseBuildOutput(stdout: string, stderr: string): BuildResult { const lines = output.split("\n"); - for (const line of lines) { - const trimmed = line.trim(); + for (let i = 0; i < lines.length; i++) { + const trimmed = lines[i].trim(); if (!trimmed) continue; // TypeScript errors (try to parse with location) - if (trimmed.includes("error TS") || trimmed.match(/:\s*error\s+TS/)) { + // Matches standard TS errors, simple TS errors, and Rollup TS errors + if (trimmed.includes("error TS") || trimmed.match(/:\s*error\s+TS/) || trimmed.includes("RollupError:")) { const parsed = parseTypeScriptError(trimmed); if (parsed) { errors.push(parsed); @@ -128,6 +151,19 @@ function parseBuildOutput(stdout: string, stderr: string): BuildResult { } } + // Check for Rollup file location pattern on a line following a Rollup error + // Format: "src/Widget.tsx (2:1)" + const fileLocMatch = trimmed.match(ROLLUP_FILE_LOCATION_PATTERN); + if (fileLocMatch && errors.length > 0) { + const lastError = errors[errors.length - 1]; + if (!lastError.file) { + lastError.file = fileLocMatch[1]; + lastError.line = parseInt(fileLocMatch[2], 10); + lastError.column = parseInt(fileLocMatch[3], 10); + } + continue; + } + // XML validation errors if (XML_ERROR_PATTERNS.some(pattern => pattern.test(trimmed))) { errors.push({ diff --git a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts index 7d39394228..71ec6545d9 100644 --- a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts @@ -6,7 +6,7 @@ */ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { mkdir, stat, writeFile } from "node:fs/promises"; +import { mkdir, readdir, readFile, stat, unlink, writeFile } from "node:fs/promises"; import { basename, dirname, join } from "node:path"; import { z } from "zod"; import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; @@ -132,14 +132,122 @@ type GenerateWidgetCodeInput = z.infer; // ============================================================================= /** - * Extracts widget name from path (e.g., /path/to/MyWidget -> MyWidget) + * Extracts widget name from the widget directory. + * Reads from package.json widgetName (authoritative source), falls back to basename. */ -function extractWidgetName(widgetPath: string): string { +async function extractWidgetName(widgetPath: string): Promise { + // Try reading from package.json (authoritative source) + try { + const pkgPath = join(widgetPath, "package.json"); + const pkgJson = JSON.parse(await readFile(pkgPath, "utf-8")); + if (pkgJson.widgetName && /^[A-Z][a-zA-Z0-9]*$/.test(pkgJson.widgetName)) { + return pkgJson.widgetName; + } + } catch { + // Fall through to basename approach + } + // Fallback: derive from directory name const base = basename(widgetPath); - // Convert to PascalCase if needed return base.charAt(0).toUpperCase() + base.slice(1); } +/** + * Cleans up stale scaffold files that don't match the current widget name. + * The generator creates files named after the widget (e.g., CounterTwo.xml, CounterTwo.tsx). + * When generate-widget-code writes new files, old scaffold artifacts must be removed + * to prevent build conflicts (wrong typings, duplicate XML definitions). + */ +async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Promise { + const srcDir = join(widgetPath, "src"); + const typingsDir = join(widgetPath, "typings"); + + let srcFiles: string[]; + try { + srcFiles = await readdir(srcDir); + } catch { + return; // src dir doesn't exist yet, nothing to clean + } + + // 1. Remove old .xml files in src/ (except package.xml and the one we're about to write) + for (const file of srcFiles) { + if (file === "package.xml" || file === `${widgetName}.xml`) continue; + if (file.endsWith(".xml")) { + await unlink(join(srcDir, file)); + console.error(`[code-generation] Cleaned up stale file: src/${file}`); + } + } + + // 2. Remove old .tsx, .editorConfig.ts, .editorPreview.tsx that don't match our widget name + for (const file of srcFiles) { + // Only clean top-level src/ files, not files in subdirectories + const isOldTsx = + file.endsWith(".tsx") && file !== `${widgetName}.tsx` && file !== `${widgetName}.editorPreview.tsx`; + const isOldEditorConfig = file.endsWith(".editorConfig.ts") && file !== `${widgetName}.editorConfig.ts`; + const isOldEditorPreview = file.endsWith(".editorPreview.tsx") && file !== `${widgetName}.editorPreview.tsx`; + if (isOldTsx || isOldEditorConfig || isOldEditorPreview) { + await unlink(join(srcDir, file)); + console.error(`[code-generation] Cleaned up stale file: src/${file}`); + } + } + + // 3. Remove old .css/.scss files in src/ui/ that don't match + const uiDir = join(srcDir, "ui"); + try { + const uiFiles = await readdir(uiDir); + for (const file of uiFiles) { + if ( + (file.endsWith(".css") || file.endsWith(".scss")) && + file !== `${widgetName}.css` && + file !== `${widgetName}.scss` + ) { + await unlink(join(uiDir, file)); + console.error(`[code-generation] Cleaned up stale file: src/ui/${file}`); + } + } + } catch { + /* ui dir might not exist yet */ + } + + // 4. Clear old typings that don't match + try { + const typingsFiles = await readdir(typingsDir); + for (const file of typingsFiles) { + if (file.endsWith(".d.ts") && file !== `${widgetName}Props.d.ts`) { + await unlink(join(typingsDir, file)); + console.error(`[code-generation] Cleaned up stale file: typings/${file}`); + } + } + } catch { + /* typings dir might not exist yet */ + } + + // 5. Regenerate package.xml with correct widget name + version + const packageXmlPath = join(srcDir, "package.xml"); + const widgetNameLower = widgetName.toLowerCase(); + let version = "1.0.0"; + try { + const pkgJson = JSON.parse(await readFile(join(widgetPath, "package.json"), "utf-8")); + if (pkgJson.version) version = pkgJson.version; + } catch { + /* use default */ + } + const packageXml = [ + '', + '', + ` `, + " ", + ` `, + " ", + " ", + ` `, + " ", + " ", + "" + ].join("\n"); + await writeFile(packageXmlPath, packageXml, "utf-8"); + console.error(`[code-generation] Regenerated package.xml for ${widgetName}`); +} + /** * Generates property suggestions based on widget description. */ @@ -331,7 +439,7 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< } // Extract widget name from path - const widgetName = extractWidgetName(widgetPath); + const widgetName = await extractWidgetName(widgetPath); console.error(`[code-generation] Generating code for ${widgetName} with ${properties.length} properties`); @@ -358,6 +466,9 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< ); } + // Clean up stale scaffold files before writing new ones + await cleanupScaffoldFiles(widgetPath, widgetName); + // Generate XML console.error(`[code-generation] Generating XML...`); const xmlResult = generateWidgetXml(widgetDefinition); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts index be7b338ffa..0589c887e5 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -52,6 +52,8 @@ function getGeneratorBinPath(): string { function buildWidgetFlags(options: WidgetOptions): string[] { return [ "--default", + "--name", + options.name, "--description", options.description, "--organization", @@ -102,7 +104,7 @@ export async function runWidgetGenerator( let stderr = ""; let installingNotified = false; - const child = spawn(generatorBin, [options.name, ...flags], { + const child = spawn(generatorBin, flags, { cwd: outputDir, env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", DO_NOT_TRACK: "1" }, stdio: ["ignore", "pipe", "pipe"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37e67ee124..80022c2d74 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -286,10 +286,10 @@ importers: version: link:../../shared/prettier-config-web-widgets '@rollup/plugin-node-resolve': specifier: ^15.3.1 - version: 15.3.1(rollup@3.29.5) + version: 15.3.1(rollup@4.59.0) '@rollup/plugin-terser': specifier: ^1.0.0 - version: 1.0.0(rollup@3.29.5) + version: 1.0.0(rollup@4.59.0) concurrently: specifier: ^6.5.1 version: 6.5.1 @@ -298,7 +298,7 @@ importers: version: 0.1.8 rollup: specifier: '*' - version: 3.29.5 + version: 4.59.0 xlsx: specifier: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz version: https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz @@ -2686,16 +2686,16 @@ importers: version: link:../widget-plugin-test-utils '@rollup/plugin-commonjs': specifier: ^29.0.3 - version: 29.0.3(rollup@3.29.5) + version: 29.0.3(rollup@4.59.0) '@rollup/plugin-node-resolve': specifier: ^15.3.1 - version: 15.3.1(rollup@3.29.5) + version: 15.3.1(rollup@4.59.0) '@rollup/plugin-replace': specifier: ^6.0.2 - version: 6.0.2(rollup@3.29.5) + version: 6.0.2(rollup@4.59.0) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@3.29.5) + version: 0.4.4(rollup@4.59.0) '@types/jest': specifier: ^30.0.0 version: 30.0.0 @@ -2713,13 +2713,13 @@ importers: version: 4.4.1 rollup: specifier: '*' - version: 3.29.5 + version: 4.59.0 rollup-plugin-copy: specifier: ^3.5.0 version: 3.5.0 rollup-plugin-license: specifier: ^3.6.0 - version: 3.6.0(picomatch@4.0.3)(rollup@3.29.5) + version: 3.6.0(picomatch@4.0.3)(rollup@4.59.0) packages/shared/eslint-config-web-widgets: dependencies: @@ -5150,139 +5150,277 @@ packages: rollup: optional: true + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm-eabi@4.62.4': resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==} cpu: [arm] os: [android] + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + cpu: [arm64] + os: [android] + '@rollup/rollup-android-arm64@4.62.4': resolution: {integrity: sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==} cpu: [arm64] os: [android] + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-arm64@4.62.4': resolution: {integrity: sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.62.4': resolution: {integrity: sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==} cpu: [x64] os: [darwin] + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.62.4': resolution: {integrity: sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.62.4': resolution: {integrity: sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==} cpu: [x64] os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': resolution: {integrity: sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==} cpu: [arm] os: [linux] libc: [glibc] + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + cpu: [arm] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-arm-musleabihf@4.62.4': resolution: {integrity: sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==} cpu: [arm] os: [linux] libc: [musl] + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-arm64-gnu@4.62.4': resolution: {integrity: sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==} cpu: [arm64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-arm64-musl@4.62.4': resolution: {integrity: sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==} cpu: [arm64] os: [linux] libc: [musl] + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-loong64-gnu@4.62.4': resolution: {integrity: sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==} cpu: [loong64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-loong64-musl@4.62.4': resolution: {integrity: sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==} cpu: [loong64] os: [linux] libc: [musl] + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-ppc64-gnu@4.62.4': resolution: {integrity: sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==} cpu: [ppc64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + cpu: [ppc64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-ppc64-musl@4.62.4': resolution: {integrity: sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==} cpu: [ppc64] os: [linux] libc: [musl] + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-riscv64-gnu@4.62.4': resolution: {integrity: sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==} cpu: [riscv64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-riscv64-musl@4.62.4': resolution: {integrity: sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==} cpu: [riscv64] os: [linux] libc: [musl] + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-s390x-gnu@4.62.4': resolution: {integrity: sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==} cpu: [s390x] os: [linux] libc: [glibc] + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-x64-gnu@4.62.4': resolution: {integrity: sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==} cpu: [x64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + cpu: [x64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-x64-musl@4.62.4': resolution: {integrity: sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==} cpu: [x64] os: [linux] libc: [musl] + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openbsd-x64@4.62.4': resolution: {integrity: sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==} cpu: [x64] os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.62.4': resolution: {integrity: sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.62.4': resolution: {integrity: sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.62.4': resolution: {integrity: sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.62.4': resolution: {integrity: sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.62.4': resolution: {integrity: sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==} cpu: [x64] @@ -9505,6 +9643,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -9945,9 +10084,9 @@ packages: peerDependencies: rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 - rollup@3.29.5: - resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true rollup@4.62.4: @@ -13556,9 +13695,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@rollup/plugin-commonjs@29.0.3(rollup@3.29.5)': + '@rollup/plugin-commonjs@29.0.3(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -13566,7 +13705,7 @@ snapshots: magic-string: 0.30.19 picomatch: 4.0.3 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 '@rollup/plugin-commonjs@29.0.3(rollup@4.62.4)': dependencies: @@ -13593,15 +13732,15 @@ snapshots: optionalDependencies: rollup: 4.62.4 - '@rollup/plugin-node-resolve@15.3.1(rollup@3.29.5)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 '@rollup/plugin-node-resolve@15.3.1(rollup@4.62.4)': dependencies: @@ -13613,12 +13752,12 @@ snapshots: optionalDependencies: rollup: 4.62.4 - '@rollup/plugin-replace@6.0.2(rollup@3.29.5)': + '@rollup/plugin-replace@6.0.2(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@3.29.5) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) magic-string: 0.30.19 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 '@rollup/plugin-replace@6.0.2(rollup@4.62.4)': dependencies: @@ -13627,21 +13766,21 @@ snapshots: optionalDependencies: rollup: 4.62.4 - '@rollup/plugin-terser@0.4.4(rollup@3.29.5)': + '@rollup/plugin-terser@0.4.4(rollup@4.59.0)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 terser: 5.44.0 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 - '@rollup/plugin-terser@1.0.0(rollup@3.29.5)': + '@rollup/plugin-terser@1.0.0(rollup@4.59.0)': dependencies: serialize-javascript: 7.1.0 smob: 1.5.0 terser: 5.44.0 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 '@rollup/plugin-terser@1.0.0(rollup@4.62.4)': dependencies: @@ -13668,13 +13807,13 @@ snapshots: optionalDependencies: rollup: 4.62.4 - '@rollup/pluginutils@5.3.0(rollup@3.29.5)': + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 3.29.5 + rollup: 4.59.0 '@rollup/pluginutils@5.3.0(rollup@4.62.4)': dependencies: @@ -13684,78 +13823,153 @@ snapshots: optionalDependencies: rollup: 4.62.4 + '@rollup/rollup-android-arm-eabi@4.59.0': + optional: true + '@rollup/rollup-android-arm-eabi@4.62.4': optional: true + '@rollup/rollup-android-arm64@4.59.0': + optional: true + '@rollup/rollup-android-arm64@4.62.4': optional: true + '@rollup/rollup-darwin-arm64@4.59.0': + optional: true + '@rollup/rollup-darwin-arm64@4.62.4': optional: true + '@rollup/rollup-darwin-x64@4.59.0': + optional: true + '@rollup/rollup-darwin-x64@4.62.4': optional: true + '@rollup/rollup-freebsd-arm64@4.59.0': + optional: true + '@rollup/rollup-freebsd-arm64@4.62.4': optional: true + '@rollup/rollup-freebsd-x64@4.59.0': + optional: true + '@rollup/rollup-freebsd-x64@4.62.4': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.62.4': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.62.4': optional: true + '@rollup/rollup-linux-arm64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.62.4': optional: true + '@rollup/rollup-linux-arm64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.62.4': optional: true + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.62.4': optional: true + '@rollup/rollup-linux-loong64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-loong64-musl@4.62.4': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.62.4': optional: true + '@rollup/rollup-linux-ppc64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-ppc64-musl@4.62.4': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.62.4': optional: true + '@rollup/rollup-linux-riscv64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.62.4': optional: true + '@rollup/rollup-linux-s390x-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.62.4': optional: true + '@rollup/rollup-linux-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.62.4': optional: true + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.62.4': optional: true + '@rollup/rollup-openbsd-x64@4.59.0': + optional: true + '@rollup/rollup-openbsd-x64@4.62.4': optional: true + '@rollup/rollup-openharmony-arm64@4.59.0': + optional: true + '@rollup/rollup-openharmony-arm64@4.62.4': optional: true + '@rollup/rollup-win32-arm64-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.62.4': optional: true + '@rollup/rollup-win32-ia32-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.62.4': optional: true + '@rollup/rollup-win32-x64-gnu@4.59.0': + optional: true + '@rollup/rollup-win32-x64-gnu@4.62.4': optional: true + '@rollup/rollup-win32-x64-msvc@4.59.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.62.4': optional: true @@ -19428,7 +19642,7 @@ snapshots: globby: 10.0.1 is-plain-object: 3.0.1 - rollup-plugin-license@3.6.0(picomatch@4.0.3)(rollup@3.29.5): + rollup-plugin-license@3.6.0(picomatch@4.0.3)(rollup@4.59.0): dependencies: commenting: 1.1.0 fdir: 6.5.0(picomatch@4.0.3) @@ -19436,7 +19650,7 @@ snapshots: magic-string: 0.30.19 moment: 2.30.1 package-name-regex: 2.0.6 - rollup: 3.29.5 + rollup: 4.59.0 spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 transitivePeerDependencies: @@ -19515,8 +19729,35 @@ snapshots: magic-string: 0.30.19 rollup: 4.62.4 - rollup@3.29.5: + rollup@4.59.0: + dependencies: + '@types/estree': 1.0.8 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 rollup@4.62.4: From a6cb1c092361fc3cdaf2cf70d93c4370a6c995cc Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 01:59:22 +0100 Subject: [PATCH 17/36] fix(xml-generator): force required=true for primitive property types --- .../src/generators/xml-generator.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts index 9b299a9d25..4cc002c31f 100644 --- a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts @@ -44,8 +44,12 @@ function generateProperty(prop: PropertyDefinition, indent: string): string { const attrs: string[] = [`key="${escapeXml(prop.key)}"`, `type="${prop.type}"`]; // Add optional attributes - if (prop.required !== undefined) { - attrs.push(`required="${prop.required}"`); + // Primitive types (integer, boolean, decimal) have no null representation in Mendix — + // Studio Pro rejects required="false" on them. Force required="true" for these types. + const PRIMITIVE_TYPES = ["integer", "boolean", "decimal"]; + const effectiveRequired = PRIMITIVE_TYPES.includes(prop.type) ? true : prop.required; + if (effectiveRequired !== undefined) { + attrs.push(`required="${effectiveRequired}"`); } if (prop.defaultValue !== undefined) { attrs.push(`defaultValue="${escapeXml(String(prop.defaultValue))}"`); @@ -113,7 +117,7 @@ export function generateWidgetXml(widget: WidgetDefinition): GeneratorResult { // Derive defaults const organization = widget.organization ?? "mendix"; const widgetNameLower = widget.name.toLowerCase(); - const widgetId = widget.id ?? `com.${organization}.widget.custom.${widgetNameLower}.${widget.name}`; + const widgetId = widget.id ?? `${organization}.${widgetNameLower}.${widget.name}`; const studioCategory = widget.studioCategory ?? "Display"; const needsEntityContext = widget.needsEntityContext ?? false; const offlineCapable = widget.offlineCapable ?? true; From 2c1c144d10ebed790dec256bb8c5076949b0a4ad Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 01:59:45 +0100 Subject: [PATCH 18/36] fix(tsx-generator): use _props param in editorPreview when no label prop --- .../src/generators/tsx-generator.ts | 74 +++++++++++++++++-- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts index ae8f0ccab4..0230605cb5 100644 --- a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts @@ -22,6 +22,9 @@ export interface TsxGeneratorResult { /** Generated main component content (src/[Widget].tsx) */ mainComponent?: string; + /** Generated editor preview content (src/[Widget].editorPreview.tsx) */ + editorPreview?: string; + /** Detected or specified widget pattern */ pattern?: WidgetPattern; @@ -96,7 +99,9 @@ function generateImports(widgetName: string, properties: PropertyDefinition[], p p => p.type === "attribute" && p.attributeTypes?.some(t => ["Integer", "Long", "Decimal"].includes(t)) ); - if (hasAction || hasAttribute) { + // useCallback is needed for action handlers (all patterns) and attribute setValue + // callbacks (only input/dataList patterns — button/display/container don't call setValue) + if (hasAction || (hasAttribute && (pattern === "input" || pattern === "dataList"))) { reactImports.add("useCallback"); } if (pattern === "container") { @@ -127,8 +132,9 @@ function generateImports(widgetName: string, properties: PropertyDefinition[], p imports.push('import { ValueStatus } from "mendix";'); } - // Big.js for numeric attributes (Integer, Long, Decimal use Big internally) - if (hasIntegerAttribute) { + // Big.js is only needed by the input pattern — it's the only pattern that calls + // attribute.setValue() with a Big value. Other patterns don't write back to attributes. + if (hasIntegerAttribute && pattern === "input") { imports.push('import Big from "big.js";'); } @@ -306,10 +312,10 @@ function generateInputPattern(widgetName: string, properties: PropertyDefinition const attributeProps = properties.filter(p => p.type === "attribute"); const mainAttribute = attributeProps[0]; const actionProps = properties.filter(p => p.type === "action"); - const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); - // Generate destructuring - const allProps = [...attributeProps, ...actionProps, ...textProps]; + // Generate destructuring — only include props that are actually used in the rendered input. + // Text/string props are not rendered by the input element, so omit them to avoid unused-var errors. + const allProps = [...attributeProps, ...actionProps]; const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; // Determine input type based on attribute type @@ -325,7 +331,7 @@ function generateInputPattern(widgetName: string, properties: PropertyDefinition // Find change action const changeAction = actionProps.find(p => p.key.toLowerCase().includes("change")); - const changeHandler = changeAction ? true : false; + const changeHandler = !!changeAction; // Determine if we need Big conversion for numeric attributes const usesBig = inputType === "number"; @@ -495,6 +501,59 @@ export default function ${widgetName}(props: ${widgetName}ContainerProps): React `; } +/** + * Generates a Studio Pro design-mode preview component (src/[Widget].editorPreview.tsx). + * + * In preview mode Mendix simplifies all property types to primitives. The generated + * stub picks the first "displayable" property and renders its value, so `props` is + * always read and TS6133 never fires. Displayable types: + * string / textTemplate / attribute → rendered as-is (falsy-safe with ||) + * integer / decimal / boolean → rendered via String() with explicit null check + * + * Non-displayable types (action, enumeration, datasource, …) are skipped. If no + * displayable property exists, _props is used as the TypeScript convention for an + * intentionally unused parameter. + */ +export function generateEditorPreview(widgetName: string, properties: PropertyDefinition[]): string { + const widgetClass = `widget-${widgetName.toLowerCase()}`; + + const STRING_TYPES = new Set(["string", "textTemplate", "attribute"]); + const NUMERIC_BOOL_TYPES = new Set(["integer", "decimal", "boolean"]); + + // Find the first property that can produce a meaningful display value in PreviewProps + const displayProp = properties.find(p => STRING_TYPES.has(p.type) || NUMERIC_BOOL_TYPES.has(p.type)); + + // Generate a type-appropriate expression so props is always read when displayProp exists + let previewContent: string; + if (!displayProp) { + previewContent = `"[${widgetName}]"`; + } else if (STRING_TYPES.has(displayProp.type)) { + previewContent = `props.${displayProp.key} || "[${widgetName}]"`; + } else { + // integer, decimal, boolean: explicit null check because 0 and false are falsy + previewContent = `props.${displayProp.key} != null ? String(props.${displayProp.key}) : "[${widgetName}]"`; + } + + // _props only when no property is displayed (TS6133: declared but never read) + const previewParam = displayProp ? "props" : "_props"; + + return `import { ReactElement, createElement } from "react"; +import { ${widgetName}PreviewProps } from "../typings/${widgetName}Props"; + +export function preview(${previewParam}: ${widgetName}PreviewProps): ReactElement { + return ( +
+ {${previewContent}} +
+ ); +} + +export function getPreviewCss(): string { + return ""; +} +`; +} + /** * Generates the complete widget TSX from a widget definition. */ @@ -532,6 +591,7 @@ export function generateWidgetTsx( return { success: true, mainComponent, + editorPreview: generateEditorPreview(widgetName, properties), pattern: detectedPattern }; } catch (error) { From ed70e620ada6b091a2235aaa66139145b166b0cf Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:00:44 +0100 Subject: [PATCH 19/36] fix(scaffolding): skip scaffold when widget directory already exists --- .../src/tools/scaffolding.tools.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 57101de6d1..81e651f858 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -9,7 +9,7 @@ import { createToolResponse, type ErrorCode } from "@/tools/utils/response"; -import { access, mkdir } from "node:fs/promises"; +import { access, mkdir, stat } from "node:fs/promises"; import { dirname } from "node:path"; import { z } from "zod"; import { isPathAllowed } from "./utils/sandbox"; @@ -139,8 +139,24 @@ async function handleCreateWidget( const widgetFolder = options.name.charAt(0).toLowerCase() + options.name.slice(1); const widgetPath = `${outputDir}/${widgetFolder}`; - // Run generator inside outputDir — it creates the widget subfolder - await runWidgetGenerator(options, tracker, outputDir); + // If the widget directory already exists, skip the Yeoman scaffold — the generator + // refuses to run in non-empty directories. The existing scaffold is still valid; + // generate-widget-code will overwrite the source files anyway. + let alreadyExists = false; + try { + await stat(widgetPath); + alreadyExists = true; + } catch { + /* directory doesn't exist yet — proceed with scaffold */ + } + + if (alreadyExists) { + console.error(`[create-widget] Widget directory already exists at ${widgetPath} — skipping scaffold`); + await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget directory already exists — skipping scaffold."); + } else { + // Run generator inside outputDir — it creates the widget subfolder + await runWidgetGenerator(options, tracker, outputDir); + } console.error(`[create-widget] Widget created successfully at ${widgetPath}`); await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget created successfully!"); From d9746cc08065b2a27716da8995b36b0dbde52eaf Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:01:01 +0100 Subject: [PATCH 20/36] feat(build): extract formatBuildSuccessResponse, chain build to deploy in tool description --- .../src/tools/build.tools.ts | 164 ++++++++++-------- .../src/tools/project.tools.ts | 1 + 2 files changed, 94 insertions(+), 71 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts index eb96eda6c5..4f8d2643c9 100644 --- a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts @@ -6,17 +6,13 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; -import { join } from "node:path"; +import { readFile } from "node:fs/promises"; +import { join, normalize, sep } from "node:path"; import { z } from "zod"; import { GENERATIONS_DIR } from "@/config"; import type { ToolContext, ToolResponse } from "./types"; import { ProgressTracker } from "./utils/progress-tracker"; -import { - createStructuredError, - createStructuredErrorResponse, - createToolResponse, - type StructuredError -} from "./utils/response"; +import { createStructuredError, createStructuredErrorResponse, createToolResponse } from "./utils/response"; import { findMpkFile } from "./utils/mpk"; import { isPathAllowed } from "./utils/sandbox"; import type { SessionState } from "./session-state"; @@ -33,7 +29,7 @@ type BuildWidgetInput = z.infer; /** * Parsed error with location information. */ -interface ParsedError { +export interface ParsedError { message: string; file?: string; line?: number; @@ -137,8 +133,8 @@ function parseBuildOutput(stdout: string, stderr: string): BuildResult { const lines = output.split("\n"); - for (let i = 0; i < lines.length; i++) { - const trimmed = lines[i].trim(); + for (const line of lines) { + const trimmed = line.trim(); if (!trimmed) continue; // TypeScript errors (try to parse with location) @@ -221,6 +217,80 @@ function parseBuildOutput(stdout: string, stderr: string): BuildResult { }; } +/** + * Formats a build failure response for Maia, including: + * - All errors with file/line/column/code + * - Content of every source file that appears in the error list + * + * Embedding file content lets Maia fix errors without an extra read-widget-file + * round-trip. Output format is designed to be read by an AI agent. + */ +export async function formatBuildFailureResponse(errors: ParsedError[], widgetPath: string): Promise { + // Format error list — each error gets code, location (file line N col N), and message + const errorLines = errors.map(e => { + const loc = e.file + ? `${e.file}${e.line != null ? ` line ${e.line}` : ""}${e.column != null ? ` col ${e.column}` : ""}` + : null; + const code = e.tsCode ? `[${e.tsCode}]` : `[${e.category}]`; + const locStr = loc ? ` ${loc} —` : ""; + return ` ${code}${locStr} ${e.message}`; + }); + + // Collect unique source files that appear in errors + const uniqueFiles = [...new Set(errors.map(e => e.file).filter((f): f is string => !!f))]; + + // Read each failing file (skip if not found — don't throw) + const fileSections: string[] = []; + for (const relPath of uniqueFiles) { + // Block path traversal: only allow files under widgetPath + const normalizedBase = normalize(widgetPath); + const fullPath = normalize(join(widgetPath, relPath)); + if (!fullPath.startsWith(normalizedBase + sep) && fullPath !== normalizedBase) continue; + if (!existsSync(fullPath)) continue; + + try { + const content = await readFile(fullPath, "utf-8"); + fileSections.push(`--- ${relPath} ---\n${content}`); + } catch { + // Skip unreadable files silently + } + } + + const lines = [ + `❌ Build failed — ${errors.length} error(s). Fix the errors below, write with write-widget-file, then retry build-widget (max 3 attempts total).`, + "", + "Errors:", + ...errorLines + ]; + + if (fileSections.length > 0) { + lines.push("", "Failing file contents:", ""); + lines.push(...fileSections); + } + + return lines.join("\n"); +} + +/** + * Formats a successful build response, including MPK path, warnings, and a + * chaining instruction to call deploy-widget next. + */ +export function formatBuildSuccessResponse( + mpkPath: string | undefined, + widgetPath: string, + warnings: string[] +): string { + let message = "✅ Build successful!"; + if (mpkPath) { + message += `\n\n📦 MPK output: ${mpkPath}`; + } + if (warnings.length > 0) { + message += `\n\n⚠️ Warnings:\n${warnings.map(w => ` - ${w}`).join("\n")}`; + } + message += `\n\n🚀 Next step: Call deploy-widget with widgetPath: "${widgetPath}" to copy the .mpk to your Mendix project's widgets/ directory.`; + return message; +} + /** * Build progress phases for user-friendly messages. */ @@ -317,34 +387,6 @@ async function runBuild(widgetPath: string, tracker?: ProgressTracker): Promise< }); } -/** - * Converts a parsed error to a structured error with suggestions. - */ -function toStructuredError(error: ParsedError): StructuredError { - const suggestions: Record = { - typescript: - "Check the TypeScript code at the specified location. Ensure props match the generated types from widget XML.", - xml: "Verify your widget.xml follows the Mendix schema. Check property types and required attributes.", - dependency: - "Run 'npm install' in the widget directory. If the issue persists, check that all dependencies are listed in package.json.", - unknown: "Review the build output for more details. Try running 'npx pluggable-widget-tools build' manually." - }; - - const codeMap: Record = { - typescript: "ERR_BUILD_TS", - xml: "ERR_BUILD_XML", - dependency: "ERR_BUILD_MISSING_DEP", - unknown: "ERR_BUILD_UNKNOWN" - }; - - return createStructuredError(codeMap[error.category], error.message, { - suggestion: suggestions[error.category], - file: error.file, - line: error.line, - column: error.column - }); -} - /** * Handler for the build-widget tool. */ @@ -400,42 +442,14 @@ async function handleBuildWidget( const mpkPath = result.mpkPath || findMpkFile(widgetPath); if (result.success) { - let message = `✅ Build successful!`; - - if (mpkPath) { - message += `\n\n📦 MPK output: ${mpkPath}`; - } - - if (result.warnings.length > 0) { - message += `\n\n⚠️ Warnings:\n${result.warnings.map(w => ` - ${w}`).join("\n")}`; - } - - return createToolResponse(message); + return createToolResponse(formatBuildSuccessResponse(mpkPath, widgetPath, result.warnings)); } else { - // Return first error as structured error (most relevant) if (result.errors.length > 0) { - const primaryError = toStructuredError(result.errors[0]); - - // Add additional errors to raw output if multiple - if (result.errors.length > 1) { - const additionalErrors = result.errors - .slice(1) - .map(e => { - const loc = e.file ? `${e.file}${e.line ? `:${e.line}` : ""}` : ""; - return loc ? `[${loc}] ${e.message}` : e.message; - }) - .join("\n"); - - primaryError.details = { - ...primaryError.details, - rawOutput: `Additional errors (${result.errors.length - 1}):\n${additionalErrors}` - }; - } - - return createStructuredErrorResponse(primaryError); + const message = await formatBuildFailureResponse(result.errors, widgetPath); + return { content: [{ type: "text", text: message }], isError: true }; } - // Fallback for unknown failures + // Fallback for unknown failures (no structured errors detected) return createStructuredErrorResponse( createStructuredError("ERR_BUILD_UNKNOWN", "Build failed with unknown error", { suggestion: "Check the raw build output for details.", @@ -459,7 +473,15 @@ export function registerBuildTools(server: McpServer, state: SessionState): void description: "Builds a Mendix pluggable widget using pluggable-widget-tools. " + "Validates XML, compiles TypeScript, generates types, and produces an .mpk file. " + - "Returns build errors if any, which can be used to fix issues.", + "If the build fails with TypeScript errors, the response includes ALL errors with " + + "file locations AND the content of every failing source file. " + + "RETRY LOOP: On failure, (1) read the errors and embedded file content, " + + "(2) fix the TypeScript errors, (3) write the fixed files using write-widget-file, " + + "(4) call build-widget again. Repeat until the build passes. " + + "Maximum 3 total attempts — if still failing after 3 attempts, " + + "report the errors and file contents to the user. " + + "SUCCESS: When the build succeeds, you MUST call deploy-widget next with the same widgetPath " + + "to copy the .mpk to the Mendix project. Do not stop after a successful build.", inputSchema: buildWidgetSchema }, (args, context) => handleBuildWidget(args, context, state) diff --git a/packages/pluggable-widgets-mcp/src/tools/project.tools.ts b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts index 32a15d5c4c..e1684cc40b 100644 --- a/packages/pluggable-widgets-mcp/src/tools/project.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts @@ -108,6 +108,7 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo title: "Deploy Widget", description: "Copies a built widget .mpk file to the configured Mendix project's widgets/ directory. " + + "Call this after build-widget succeeds. " + "Requires a project directory to be configured (via MENDIX_PROJECT_DIR env var or set-project-directory). " + "Looks for the .mpk file in the widget's dist/ directory. " + "After deploying, synchronize the app directory in Studio Pro to pick up the new widget.", From 941c16d8f34c34b57a2287db0674f0544ba59246 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:01:19 +0100 Subject: [PATCH 21/36] feat(code-generation): add detectTemplateMismatch, reorder next steps on pattern mismatch --- .../src/tools/code-generation.tools.ts | 105 +++++++++++++++--- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts index 71ec6545d9..47a4757615 100644 --- a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts @@ -104,8 +104,18 @@ const generateWidgetCodeSchema = z.object({ widgetPath: z.string().min(1).describe("Absolute path to the scaffolded widget directory"), description: z.string().min(1).describe("Description of what the widget should do"), properties: z - .array(propertyDefinitionSchema) - .optional() + .preprocess(v => { + // MCP clients (e.g. Maia) sometimes send JSON arrays as a stringified string. + // Parse it transparently so validation still runs on the actual array contents. + if (typeof v === "string") { + try { + return JSON.parse(v); + } catch { + return v; // let Zod report the type error + } + } + return v; + }, z.array(propertyDefinitionSchema).optional()) .describe("Array of property definitions. If not provided, returns suggestions."), widgetPattern: z .enum(["display", "button", "input", "container", "dataList"]) @@ -225,12 +235,15 @@ async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Pro const packageXmlPath = join(srcDir, "package.xml"); const widgetNameLower = widgetName.toLowerCase(); let version = "1.0.0"; + let packagePath = "mendix"; try { const pkgJson = JSON.parse(await readFile(join(widgetPath, "package.json"), "utf-8")); if (pkgJson.version) version = pkgJson.version; + if (pkgJson.packagePath) packagePath = pkgJson.packagePath; } catch { /* use default */ } + const filePath = `${packagePath.replace(/\./g, "/")}/${widgetNameLower}`; const packageXml = [ '', '', @@ -239,7 +252,7 @@ async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Pro ` `, " ", " ", - ` `, + ` `, " ", " ", "" @@ -418,6 +431,55 @@ function getPatternDescription(pattern: WidgetPattern): string { } } +/** + * Detects a mismatch between the selected widget pattern and the provided properties. + * + * Returns a warning string when the pattern's key property types are absent, + * or null when the properties satisfy the pattern's requirements. + */ +export function detectTemplateMismatch(pattern: WidgetPattern, properties: PropertyDefinition[]): string | null { + const types = properties.map(p => p.type); + + switch (pattern) { + case "button": { + const warnings: string[] = []; + if (!types.includes("action")) { + warnings.push("button will be permanently disabled (no action property)"); + } + if (!types.includes("textTemplate") && !types.includes("string")) { + warnings.push("button will render empty text (no textTemplate or string property for caption)"); + } + return warnings.length > 0 ? warnings.join("; ") : null; + } + case "input": + if (!types.includes("attribute")) { + return "no attribute property for data binding — input pattern needs an attribute to read/write values"; + } + return null; + case "display": + if (!types.includes("textTemplate") && !types.includes("expression") && !types.includes("string")) { + return "read-only display component with no dynamic text source — only primitive types found; customize the generated code or add a textTemplate/expression property"; + } + return null; + case "container": + if (!types.includes("widgets")) { + return "no child content slot — container pattern needs a widgets property for nested widget content"; + } + return null; + case "dataList": { + const missing: string[] = []; + if (!types.includes("datasource")) missing.push("datasource"); + if (!types.includes("widgets")) missing.push("widgets"); + if (missing.length > 0) { + return `dataList pattern is missing required properties: ${missing.join(", ")}`; + } + return null; + } + default: + return null; + } +} + // ============================================================================= // Tool Handler // ============================================================================= @@ -491,6 +553,7 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< const filesToWrite = [ { path: `src/${widgetName}.xml`, content: xmlResult.xml }, { path: `src/${widgetName}.tsx`, content: tsxResult.mainComponent }, + { path: `src/${widgetName}.editorPreview.tsx`, content: tsxResult.editorPreview! }, { path: `src/ui/${widgetName}.scss`, content: `.widget-${widgetName.toLowerCase()} {\n}\n` }, { path: `src/.widget-definition.json`, content: JSON.stringify(widgetDefinition, null, 2) } ]; @@ -518,24 +581,40 @@ async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise< // Build success response const propSummary = properties.map(p => p.key).join(", "); + const mismatch = detectTemplateMismatch(pattern, properties as PropertyDefinition[]); + + const lines = [ + `✅ Widget code generated successfully!`, + "", + `📁 Files written:`, + ` • src/${widgetName}.xml - Widget definition with ${properties.length} properties (${propSummary})`, + ` • src/${widgetName}.tsx - Component using ${pattern} pattern`, + ` • src/ui/${widgetName}.scss - Empty SCSS placeholder`, + ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)` + ]; - return createToolResponse( - [ - `✅ Widget code generated successfully!`, + if (mismatch) { + lines.push("", `⚠️ Template notice: ${mismatch}`); + lines.push( "", - `📁 Files written:`, - ` • src/${widgetName}.xml - Widget definition with ${properties.length} properties (${propSummary})`, - ` • src/${widgetName}.tsx - Component using ${pattern} pattern`, - ` • src/ui/${widgetName}.scss - Empty SCSS placeholder`, - ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)`, + `🔨 Next steps:`, + ` 1. Review and customize the generated code (use write-widget-file to update src/${widgetName}.tsx)`, + ` 2. Run build-widget to compile and validate`, + ` 3. Update src/${widgetName}.editorPreview.tsx for Studio Pro design mode preview`, + ` 4. Test in Mendix Studio Pro` + ); + } else { + lines.push( "", `🔨 Next steps:`, ` 1. Run build-widget to compile and validate`, ` 2. Review and customize generated code`, ` 3. Update src/${widgetName}.editorPreview.tsx for Studio Pro design mode preview`, ` 4. Test in Mendix Studio Pro` - ].join("\n") - ); + ); + } + + return createToolResponse(lines.join("\n")); } catch (error) { const message = error instanceof Error ? error.message : String(error); console.error(`[code-generation] Error: ${message}`); From 6cda1b190f0c8482db94ebe1299d0c3edc4919e8 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:01:44 +0100 Subject: [PATCH 22/36] feat(server): add protocol logger and session lifecycle instrumentation --- .../src/server/protocol-logger.ts | 98 +++++++++++++++++++ .../src/server/routes.ts | 19 +++- .../src/server/session.ts | 15 ++- 3 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/server/protocol-logger.ts diff --git a/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts b/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts new file mode 100644 index 0000000000..1ea3550440 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts @@ -0,0 +1,98 @@ +import { appendFileSync, mkdirSync } from "node:fs"; +import { join } from "node:path"; + +export interface ProtocolLogEntry { + timestamp: string; + sessionId: string; + direction: "incoming" | "outgoing"; + method?: string; + id?: string | number | null; + params?: unknown; + result?: unknown; + error?: unknown; + duration?: number; + clientCapabilities?: unknown; + clientInfo?: unknown; + protocolVersion?: string; +} + +const LOG_DIR = join(process.cwd(), "mcp-session-logs"); +let logDirCreated = false; + +function ensureLogDir(): void { + if (!logDirCreated) { + mkdirSync(LOG_DIR, { recursive: true }); + logDirCreated = true; + } +} + +/** + * Appends a JSON-lines log entry for the given session. + * Writes to mcp-session-logs/.jsonl. + * Uses synchronous I/O to keep the MCP stdio channel clean. + */ +export function logProtocolMessage(sessionId: string, entry: ProtocolLogEntry): void { + try { + ensureLogDir(); + const line = JSON.stringify(entry) + "\n"; + appendFileSync(join(LOG_DIR, `${sessionId}.jsonl`), line, "utf-8"); + } catch { + // Never crash the server over a logging failure + } +} + +/** + * Extracts a structured log entry from an incoming JSON-RPC request body. + * Special-cases initialize requests to surface ClientCapabilities at the top level. + */ +export function buildIncomingLogEntry(sessionId: string, body: Record): ProtocolLogEntry { + const entry: ProtocolLogEntry = { + timestamp: new Date().toISOString(), + sessionId, + direction: "incoming", + method: typeof body.method === "string" ? body.method : undefined, + id: (body.id as string | number | null | undefined) ?? undefined + }; + + const method = entry.method; + + if (method === "initialize") { + const params = body.params as Record | undefined; + if (params) { + entry.protocolVersion = params.protocolVersion as string | undefined; + entry.clientInfo = params.clientInfo; + entry.clientCapabilities = params.capabilities; + } + } else if (method === "tools/call") { + const params = body.params as Record | undefined; + entry.params = params ? { name: params.name, arguments: params.arguments } : undefined; + } else { + // For other methods log params as-is (but omit large payloads) + entry.params = body.params; + } + + return entry; +} + +/** + * Builds a log entry for an outgoing response. + */ +export function buildOutgoingLogEntry( + sessionId: string, + method: string | undefined, + id: string | number | null | undefined, + result: unknown, + error: unknown, + duration: number +): ProtocolLogEntry { + return { + timestamp: new Date().toISOString(), + sessionId, + direction: "outgoing", + method, + id, + result: error ? undefined : result, + error, + duration + }; +} diff --git a/packages/pluggable-widgets-mcp/src/server/routes.ts b/packages/pluggable-widgets-mcp/src/server/routes.ts index fe32512f1e..ff11a81a2f 100644 --- a/packages/pluggable-widgets-mcp/src/server/routes.ts +++ b/packages/pluggable-widgets-mcp/src/server/routes.ts @@ -1,6 +1,7 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; import type { Express, Request, Response } from "express"; import { MENDIX_PROJECT_DIR, SERVER_NAME, SERVER_VERSION } from "@/config"; +import { buildIncomingLogEntry, logProtocolMessage } from "./protocol-logger"; import { createMcpServer } from "./server"; import { sessionManager } from "./session"; @@ -40,27 +41,41 @@ function setupMcpRoute(app: Express): void { app.all("/mcp", async (req: Request, res: Response) => { const sessionId = req.headers["mcp-session-id"] as string | undefined; + const requestStart = Date.now(); try { // Case 1: Existing session - reuse transport if (sessionId && sessionManager.hasSession(sessionId)) { + const body = req.body as Record; + logProtocolMessage(sessionId, buildIncomingLogEntry(sessionId, body)); + console.error( + `[MCP] ${body.method ?? "request"} session=${sessionId} elapsed=${Date.now() - requestStart}ms` + ); const transport = sessionManager.getTransport(sessionId)!; - await transport.handleRequest(req, res, req.body); + await transport.handleRequest(req, res, body); return; } // Case 2: New session via POST with initialize request if (req.method === "POST" && !sessionId && isInitializeRequest(req.body)) { + const body = req.body as Record; + const pendingSessionId = "pending-" + Date.now(); + const logEntry = buildIncomingLogEntry(pendingSessionId, body); + console.error( + `[MCP] initialize (new session) protocolVersion=${logEntry.protocolVersion} clientInfo=${JSON.stringify(logEntry.clientInfo)}` + ); + logProtocolMessage(pendingSessionId, logEntry); const transport = sessionManager.createTransport(); const server = createMcpServer(); await server.connect(transport); - await transport.handleRequest(req, res, req.body); + await transport.handleRequest(req, res, body); return; } // Case 3: GET request for SSE - create new session // StreamableHTTP uses GET for server-to-client event streams if (req.method === "GET") { + console.error(`[MCP] SSE GET — creating new session`); const transport = sessionManager.createTransport(); const server = createMcpServer(); await server.connect(transport); diff --git a/packages/pluggable-widgets-mcp/src/server/session.ts b/packages/pluggable-widgets-mcp/src/server/session.ts index 4015739e11..b382f045b8 100644 --- a/packages/pluggable-widgets-mcp/src/server/session.ts +++ b/packages/pluggable-widgets-mcp/src/server/session.ts @@ -4,6 +4,7 @@ import { randomUUID } from "node:crypto"; export interface Session { transport: StreamableHTTPServerTransport; createdAt: Date; + toolCallCount: number; } /** @@ -20,15 +21,23 @@ export class SessionManager { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: sessionId => { + const createdAt = new Date(); this.sessions.set(sessionId, { transport, - createdAt: new Date() + createdAt, + toolCallCount: 0 }); - console.log(`[MCP] Session initialized: ${sessionId}`); + console.error(`[MCP] Session initialized: ${sessionId} at=${createdAt.toISOString()}`); }, onsessionclosed: sessionId => { + const session = this.sessions.get(sessionId); + if (session) { + const durationMs = Date.now() - session.createdAt.getTime(); + console.error( + `[MCP] Session closed: ${sessionId} duration=${durationMs}ms toolCalls=${session.toolCallCount}` + ); + } this.sessions.delete(sessionId); - console.log(`[MCP] Session closed: ${sessionId}`); } }); From abbc54834d793bed092a1327f4a6f1726e940c21 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:02:12 +0100 Subject: [PATCH 23/36] test: add formatBuildSuccessResponse, detectTemplateMismatch, scenario and generator tests --- .../src/__test-utils__/mcp-test-harness.ts | 119 +++++++++ .../scenarios/widget-lifecycle.test.ts | 250 ++++++++++++++++++ .../__tests__/tsx-generator.test.ts | 136 ++++++++++ .../src/security/__tests__/guardrails.test.ts | 2 +- .../src/tools/__tests__/build.tools.test.ts | 124 ++++++++- .../__tests__/code-generation.tools.test.ts | 93 +++++++ .../utils/__tests__/mpk-analyzer.test.ts | 206 +++++++++++++++ .../tools/utils/__tests__/response.test.ts | 4 +- .../pluggable-widgets-mcp/vitest.config.ts | 2 +- 9 files changed, 931 insertions(+), 5 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts diff --git a/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts b/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts index e88ce8576c..75a1010828 100644 --- a/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts +++ b/packages/pluggable-widgets-mcp/src/__test-utils__/mcp-test-harness.ts @@ -3,6 +3,27 @@ import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { SessionState } from "@/tools/session-state"; +// ─── Protocol Recording Types ──────────────────────────────────────────────── + +export interface ProtocolRecord { + timestamp: number; + direction: "client-to-server" | "server-to-client"; + message: unknown; +} + +export interface ToolCallRecord { + name: string; + arguments: unknown; + timestamp: number; +} + +export interface RecordingMcpTestContext extends McpTestContext { + records: ProtocolRecord[]; + getToolCalls(): ToolCallRecord[]; + getToolCallSequence(): string[]; + assertToolOrder(expectedNames: string[]): void; +} + type ToolRegistrationFn = (server: McpServer, state: SessionState) => void; interface McpTestContext { @@ -30,6 +51,8 @@ export function isError(result: Awaited>): boolea return "isError" in result && result.isError === true; } +// ─── Core Test Context ──────────────────────────────────────────────────────── + export async function createMcpTestContext(...registerFns: ToolRegistrationFn[]): Promise { const state: SessionState = { projectDir: undefined }; @@ -65,3 +88,99 @@ export async function createMcpTestContext(...registerFns: ToolRegistrationFn[]) } }; } + +// ─── Recording Test Context ─────────────────────────────────────────────────── + +/** + * Creates an MCP test context that records all JSON-RPC messages in both directions. + * Wraps InMemoryTransport.send and onmessage post-connection to intercept traffic. + */ +export async function createRecordingMcpTestContext( + ...registerFns: ToolRegistrationFn[] +): Promise { + const records: ProtocolRecord[] = []; + const state: SessionState = { projectDir: undefined }; + + const server = new McpServer( + { name: "test-server", version: "0.0.0" }, + { capabilities: { tools: {}, logging: {} } } + ); + + if (registerFns.length === 0) { + const { registerProjectTools } = await import("@/tools/project.tools"); + registerProjectTools(server, state); + } else { + for (const fn of registerFns) { + fn(server, state); + } + } + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + const client = new Client({ name: "test-client", version: "0.0.0" }); + + await server.connect(serverTransport); + await client.connect(clientTransport); + + // Intercept outgoing client messages (client → server) + const originalClientSend = clientTransport.send.bind(clientTransport); + clientTransport.send = async (message: unknown, options?: unknown) => { + records.push({ timestamp: Date.now(), direction: "client-to-server", message }); + return (originalClientSend as (m: unknown, o?: unknown) => Promise)(message, options); + }; + + // Intercept incoming client messages (server → client) + const originalOnMessage = clientTransport.onmessage; + clientTransport.onmessage = (message: unknown, extra?: unknown) => { + records.push({ timestamp: Date.now(), direction: "server-to-client", message }); + (originalOnMessage as ((m: unknown, e?: unknown) => void) | undefined)?.(message, extra); + }; + + const getToolCalls = (): ToolCallRecord[] => + records + .filter(r => { + const msg = r.message as Record; + return r.direction === "client-to-server" && msg.method === "tools/call"; + }) + .map(r => { + const msg = r.message as Record; + const params = msg.params as Record; + return { + name: params.name as string, + arguments: params.arguments, + timestamp: r.timestamp + }; + }); + + const getToolCallSequence = (): string[] => getToolCalls().map(c => c.name); + + const assertToolOrder = (expectedNames: string[]): void => { + const actual = getToolCallSequence(); + if (actual.length < expectedNames.length) { + throw new Error( + `Expected tool call sequence ${JSON.stringify(expectedNames)} but only got ${JSON.stringify(actual)}` + ); + } + for (let i = 0; i < expectedNames.length; i++) { + if (actual[i] !== expectedNames[i]) { + throw new Error( + `Tool call at index ${i}: expected "${expectedNames[i]}" but got "${actual[i]}"\n` + + `Full sequence: ${JSON.stringify(actual)}` + ); + } + } + }; + + return { + server, + client, + state, + records, + getToolCalls, + getToolCallSequence, + assertToolOrder, + cleanup: async () => { + await client.close(); + await server.close(); + } + }; +} diff --git a/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts b/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts new file mode 100644 index 0000000000..353b9a894a --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts @@ -0,0 +1,250 @@ +/** + * Integration scenario tests that simulate complete Maia workflows. + * Uses the recording harness to verify tool ordering and state transitions. + * The generator is mocked so tests run fast without Yeoman scaffolding. + */ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + createMcpTestContext, + createRecordingMcpTestContext, + getResultText, + isError +} from "@/__test-utils__/mcp-test-harness"; +import { createTempMendixProject } from "@/__test-utils__/temp-dir"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import type { SessionState } from "@/tools/session-state"; + +vi.mock("@/tools/utils/generator", () => ({ + buildWidgetOptions: (args: Record) => ({ + name: args.name ?? "ScenarioWidget", + description: args.description ?? "scenario test", + version: "1.0.0", + author: "Mendix", + license: "Apache-2.0", + organization: "Mendix", + template: "empty", + programmingLanguage: "typescript", + unitTests: false, + e2eTests: false + }), + runWidgetGenerator: vi.fn().mockResolvedValue(undefined), + SCAFFOLD_PROGRESS: { + START: { progress: 0, message: "Starting..." }, + COMPLETE: { progress: 100, message: "Done!" } + } +})); + +describe("discovery workflow", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext()); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("get-project-info (no project) → set-project-directory → get-project-info (success)", async () => { + // Step 1: get-project-info with no project configured + const noProjectResult = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(noProjectResult)).toBe(true); + expect(getResultText(noProjectResult)).toContain("ERR_PROJECT_NOT_CONFIGURED"); + + // Step 2: set-project-directory with a valid project + const { dir, cleanup: tempCleanup } = createTempMendixProject({ projectName: "DiscoveryApp" }); + tempCleanups.push(tempCleanup); + + const setResult = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: dir } + }); + expect(isError(setResult)).toBe(false); + expect(state.projectDir).toBe(dir); + + // Step 3: get-project-info now succeeds with project name + const infoResult = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(infoResult)).toBe(false); + expect(getResultText(infoResult)).toContain("DiscoveryApp"); + }); +}); + +describe("scaffold workflow", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + const { registerScaffoldingTools } = await import("@/tools/scaffolding.tools"); + const { registerFileOperationTools } = await import("@/tools/file-operations.tools"); + ({ client, state, cleanup } = await createMcpTestContext(registerScaffoldingTools, registerFileOperationTools)); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("set-project-directory → create-widget returns valid path", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + + // create-widget with outputPath inside projectDir (passes sandbox check) + const outputPath = dir + "/widgets-out"; + const createResult = await client.callTool({ + name: "create-widget", + arguments: { + name: "ScenarioWidget", + description: "scenario test", + outputPath + } + }); + expect(isError(createResult)).toBe(false); + const text = getResultText(createResult); + expect(text).toContain("ScenarioWidget"); + expect(text).toContain("created successfully"); + }); +}); + +describe("error recovery", () => { + let client: Client; + let state: SessionState; + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + beforeEach(async () => { + ({ client, state, cleanup } = await createMcpTestContext()); + }); + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("get-project-info on invalid path → returns structured error with suggestion", async () => { + state.projectDir = "/nonexistent/path/to/project"; + + const result = await client.callTool({ name: "get-project-info", arguments: {} }); + expect(isError(result)).toBe(true); + const text = getResultText(result); + + // Error is structured and actionable + expect(text).toContain("ERR_PROJECT_NOT_CONFIGURED"); + expect(text).toContain("Suggestion"); + }); + + it("set-project-directory with invalid path → state unchanged, actionable error returned", async () => { + state.projectDir = undefined; + + const result = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: "/completely/nonexistent" } + }); + expect(isError(result)).toBe(true); + expect(state.projectDir).toBeUndefined(); + const text = getResultText(result); + expect(text).toContain("ERR_PROJECT_NOT_CONFIGURED"); + + // Retry with valid path + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + + const retryResult = await client.callTool({ + name: "set-project-directory", + arguments: { projectDir: dir } + }); + expect(isError(retryResult)).toBe(false); + expect(state.projectDir).toBe(dir); + }); +}); + +describe("protocol recording captures tool sequence", () => { + let cleanup: () => Promise; + const tempCleanups: Array<() => void> = []; + + afterEach(async () => { + await cleanup(); + for (const c of tempCleanups) c(); + tempCleanups.length = 0; + }); + + it("records tool calls in order and assertToolOrder passes", async () => { + const ctx = await createRecordingMcpTestContext(); + cleanup = ctx.cleanup; + + const { dir, cleanup: tempCleanup } = createTempMendixProject({ projectName: "RecordingApp" }); + tempCleanups.push(tempCleanup); + + // Call two tools in sequence + await ctx.client.callTool({ name: "get-project-info", arguments: {} }); + ctx.state.projectDir = dir; + await ctx.client.callTool({ name: "get-project-info", arguments: {} }); + + // Verify recording captured both calls + const sequence = ctx.getToolCallSequence(); + expect(sequence.length).toBeGreaterThanOrEqual(2); + expect(sequence[0]).toBe("get-project-info"); + expect(sequence[1]).toBe("get-project-info"); + + // assertToolOrder should not throw + expect(() => ctx.assertToolOrder(["get-project-info", "get-project-info"])).not.toThrow(); + }); + + it("records messages in both directions", async () => { + const ctx = await createRecordingMcpTestContext(); + cleanup = ctx.cleanup; + + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + ctx.state.projectDir = dir; + + await ctx.client.callTool({ name: "get-project-info", arguments: {} }); + + const clientToServer = ctx.records.filter(r => r.direction === "client-to-server"); + const serverToClient = ctx.records.filter(r => r.direction === "server-to-client"); + + expect(clientToServer.length).toBeGreaterThan(0); + expect(serverToClient.length).toBeGreaterThan(0); + }); + + it("getToolCalls returns name and arguments", async () => { + const ctx = await createRecordingMcpTestContext(); + cleanup = ctx.cleanup; + + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + + await ctx.client.callTool({ + name: "set-project-directory", + arguments: { projectDir: dir } + }); + + const toolCalls = ctx.getToolCalls(); + expect(toolCalls).toHaveLength(1); + expect(toolCalls[0].name).toBe("set-project-directory"); + expect((toolCalls[0].arguments as Record).projectDir).toBe(dir); + expect(toolCalls[0].timestamp).toBeGreaterThan(0); + }); + + it("assertToolOrder throws when sequence is wrong", async () => { + const ctx = await createRecordingMcpTestContext(); + cleanup = ctx.cleanup; + + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + ctx.state.projectDir = dir; + + await ctx.client.callTool({ name: "get-project-info", arguments: {} }); + + expect(() => ctx.assertToolOrder(["set-project-directory"])).toThrow(); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts b/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts new file mode 100644 index 0000000000..bfe9b1244d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts @@ -0,0 +1,136 @@ +import { describe, expect, it } from "vitest"; +import { generateEditorPreview } from "@/generators/tsx-generator"; +import type { PropertyDefinition } from "@/generators/types"; + +/** + * generateEditorPreview produces a Studio Pro design-mode preview component. + * + * Design rule: if any property can produce a meaningful value in PreviewProps, + * use it — so `props` is always read and TS6133 never fires. + * + * Displayable types (produce readable primitives in PreviewProps): + * string, textTemplate, attribute → string → props.key || "[WidgetName]" + * integer, decimal → number → props.key != null ? String(props.key) : "[WidgetName]" + * boolean → boolean → props.key != null ? String(props.key) : "[WidgetName]" + * + * Non-displayable types (action, enumeration, datasource, …) do not produce + * a value worth showing in the preview, so they are skipped. If ALL properties + * are non-displayable (or there are no properties), _props is used as the + * conventional TypeScript signal for an intentionally unused parameter. + */ +describe("generateEditorPreview", () => { + // ── string-like props (existing behaviour, must not regress) ──────────── + + it("uses props and shows string value when a string property is present", () => { + const properties: PropertyDefinition[] = [{ key: "title", type: "string", caption: "Title", required: false }]; + const output = generateEditorPreview("Card", properties); + + expect(output).toContain("preview(props: CardPreviewProps)"); + expect(output).toContain('props.title || "[Card]"'); + expect(output).not.toContain("_props"); + }); + + it("uses props and shows textTemplate value when a textTemplate property is present", () => { + const properties: PropertyDefinition[] = [ + { key: "text", type: "textTemplate", caption: "Text", required: true } + ]; + const output = generateEditorPreview("Label", properties); + + expect(output).toContain("preview(props: LabelPreviewProps)"); + expect(output).toContain('props.text || "[Label]"'); + expect(output).not.toContain("_props"); + }); + + it("uses props and shows attribute value when an attribute property is present", () => { + const properties: PropertyDefinition[] = [ + { key: "value", type: "attribute", caption: "Value", required: true, attributeTypes: ["String"] } + ]; + const output = generateEditorPreview("Input", properties); + + expect(output).toContain("preview(props: InputPreviewProps)"); + expect(output).toContain('props.value || "[Input]"'); + expect(output).not.toContain("_props"); + }); + + // ── numeric props ──────────────────────────────────────────────────────── + + it("uses props and shows integer value when an integer property is present", () => { + const properties: PropertyDefinition[] = [ + { key: "initialValue", type: "integer", caption: "Initial value", required: false } + ]; + const output = generateEditorPreview("Counter", properties); + + expect(output).toContain("preview(props: CounterPreviewProps)"); + expect(output).toContain('props.initialValue != null ? String(props.initialValue) : "[Counter]"'); + expect(output).not.toContain("_props"); + }); + + it("uses props and shows decimal value when a decimal property is present", () => { + const properties: PropertyDefinition[] = [ + { key: "amount", type: "decimal", caption: "Amount", required: false } + ]; + const output = generateEditorPreview("Price", properties); + + expect(output).toContain("preview(props: PricePreviewProps)"); + expect(output).toContain('props.amount != null ? String(props.amount) : "[Price]"'); + expect(output).not.toContain("_props"); + }); + + // ── boolean props ──────────────────────────────────────────────────────── + + it("uses props and shows boolean value when a boolean property is present", () => { + const properties: PropertyDefinition[] = [ + { key: "enabled", type: "boolean", caption: "Enabled", required: false } + ]; + const output = generateEditorPreview("Toggle", properties); + + expect(output).toContain("preview(props: TogglePreviewProps)"); + expect(output).toContain('props.enabled != null ? String(props.enabled) : "[Toggle]"'); + expect(output).not.toContain("_props"); + }); + + // ── first displayable prop wins ────────────────────────────────────────── + + it("uses the first displayable prop when multiple types are mixed", () => { + const properties: PropertyDefinition[] = [ + { key: "onClick", type: "action", caption: "On click", required: false }, + { key: "count", type: "integer", caption: "Count", required: false }, + { key: "label", type: "string", caption: "Label", required: false } + ]; + const output = generateEditorPreview("Widget", properties); + + // action is skipped, integer is first displayable + expect(output).toContain('props.count != null ? String(props.count) : "[Widget]"'); + expect(output).not.toContain("_props"); + }); + + // ── non-displayable only → _props ──────────────────────────────────────── + + it("uses _props when all properties are non-displayable types (action, enumeration)", () => { + const properties: PropertyDefinition[] = [ + { key: "onClick", type: "action", caption: "On click", required: false }, + { + key: "size", + type: "enumeration", + caption: "Size", + required: false, + enumValues: [ + { key: "small", caption: "Small" }, + { key: "large", caption: "Large" } + ] + } + ]; + const output = generateEditorPreview("Button", properties); + + expect(output).toContain("preview(_props: ButtonPreviewProps)"); + expect(output).toContain('"[Button]"'); + expect(output).not.toContain("preview(props: ButtonPreviewProps)"); + }); + + it("uses _props when there are no properties at all", () => { + const output = generateEditorPreview("Empty", []); + + expect(output).toContain("preview(_props: EmptyPreviewProps)"); + expect(output).toContain('"[Empty]"'); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts index b249fdca9c..19c7ec7ee8 100644 --- a/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts +++ b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { isPathWithinDirectory, isExtensionAllowed, validateFilePath } from "@/security/guardrails"; +import { isExtensionAllowed, isPathWithinDirectory, validateFilePath } from "@/security/guardrails"; describe("isPathWithinDirectory", () => { it("returns true for a path within the base directory", () => { diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts index 5a5214d76e..2619da62e6 100644 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; import { createTempMendixProject } from "@/__test-utils__/temp-dir"; -import { registerBuildTools } from "@/tools/build.tools"; +import { formatBuildFailureResponse, formatBuildSuccessResponse, registerBuildTools } from "@/tools/build.tools"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; @@ -57,3 +57,125 @@ describe("build-widget sandbox expansion", () => { expect(text).not.toContain("not within an allowed directory"); }); }); + +describe("formatBuildFailureResponse", () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = mkdtempSync(join(tmpdir(), "mcp-build-test-")); + mkdirSync(join(tmpDir, "src"), { recursive: true }); + }); + + afterEach(() => { + rmSync(tmpDir, { recursive: true, force: true }); + }); + + it("includes all error details in the response", async () => { + const errors = [ + { + category: "typescript" as const, + tsCode: "TS6133", + message: "'props' is declared but its value is never read.", + file: "src/Counter.editorPreview.tsx", + line: 4, + column: 25 + } + ]; + writeFileSync( + join(tmpDir, "src/Counter.editorPreview.tsx"), + `export function preview(props: CounterPreviewProps) {\n return
[Counter]
;\n}\n` + ); + + const response = await formatBuildFailureResponse(errors, tmpDir); + + expect(response).toContain("TS6133"); + expect(response).toContain("'props' is declared but its value is never read."); + expect(response).toContain("src/Counter.editorPreview.tsx"); + expect(response).toContain("line 4"); + expect(response).toContain("col 25"); + }); + + it("embeds content of failing source files in the response", async () => { + const fileContent = `export function preview(props: CounterPreviewProps) {\n return
[Counter]
;\n}\n`; + writeFileSync(join(tmpDir, "src/Counter.editorPreview.tsx"), fileContent); + + const errors = [ + { + category: "typescript" as const, + tsCode: "TS6133", + message: "'props' is declared but its value is never read.", + file: "src/Counter.editorPreview.tsx", + line: 4, + column: 25 + } + ]; + + const response = await formatBuildFailureResponse(errors, tmpDir); + + expect(response).toContain("export function preview"); + // The separator format "--- ---" is the required output format for this function + expect(response).toContain("--- src/Counter.editorPreview.tsx ---"); + }); + + it("skips file embed when file does not exist on disk", async () => { + const errors = [ + { + category: "typescript" as const, + tsCode: "TS2339", + message: "Property 'x' does not exist on type 'Y'.", + file: "src/Nonexistent.tsx", + line: 10, + column: 5 + } + ]; + + const response = await formatBuildFailureResponse(errors, tmpDir); + + expect(response).toContain("TS2339"); + expect(response).not.toContain("--- src/Nonexistent.tsx ---"); + }); + + it("handles errors with no file location gracefully", async () => { + const errors = [ + { + category: "unknown" as const, + message: "Build failed with exit code 1" + } + ]; + + const response = await formatBuildFailureResponse(errors, tmpDir); + + expect(response).toContain("Build failed with exit code 1"); + expect(response).not.toContain("--- "); + }); +}); + +describe("formatBuildSuccessResponse", () => { + const widgetPath = "/tmp/my-widget"; + + it("includes deploy-widget as next step with widgetPath", () => { + const result = formatBuildSuccessResponse(undefined, widgetPath, []); + expect(result).toContain("deploy-widget"); + expect(result).toContain(widgetPath); + }); + + it("includes MPK path when available", () => { + const mpkPath = "/tmp/my-widget/dist/MyWidget.mpk"; + const result = formatBuildSuccessResponse(mpkPath, widgetPath, []); + expect(result).toContain(mpkPath); + expect(result).toContain("MPK output"); + }); + + it("includes next step even without MPK path", () => { + const result = formatBuildSuccessResponse(undefined, widgetPath, []); + expect(result).toContain("Next step"); + expect(result).not.toContain("MPK output"); + }); + + it("includes warnings when present", () => { + const result = formatBuildSuccessResponse(undefined, widgetPath, ["unused variable", "deprecated API"]); + expect(result).toContain("Warnings"); + expect(result).toContain("unused variable"); + expect(result).toContain("deprecated API"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts new file mode 100644 index 0000000000..13d1de3a54 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts @@ -0,0 +1,93 @@ +import { describe, expect, it } from "vitest"; +import { detectTemplateMismatch } from "@/tools/code-generation.tools"; +import type { PropertyDefinition } from "@/generators/types"; + +function prop(key: string, type: PropertyDefinition["type"]): PropertyDefinition { + return { key, type, caption: key }; +} + +describe("detectTemplateMismatch", () => { + it("warns: button pattern + no action → disabled", () => { + const result = detectTemplateMismatch("button", [prop("caption", "textTemplate")]); + expect(result).not.toBeNull(); + expect(result).toContain("permanently disabled"); + }); + + it("warns: button pattern + no caption → empty text", () => { + const result = detectTemplateMismatch("button", [prop("onClick", "action")]); + expect(result).not.toBeNull(); + expect(result).toContain("empty text"); + }); + + it("null: button pattern + action + textTemplate → OK", () => { + const result = detectTemplateMismatch("button", [prop("onClick", "action"), prop("caption", "textTemplate")]); + expect(result).toBeNull(); + }); + + it("null: button pattern + action + string → OK", () => { + const result = detectTemplateMismatch("button", [prop("onClick", "action"), prop("label", "string")]); + expect(result).toBeNull(); + }); + + it("warns: display pattern + only integer → read-only", () => { + const result = detectTemplateMismatch("display", [prop("count", "integer")]); + expect(result).not.toBeNull(); + expect(result).toContain("read-only"); + }); + + it("null: display pattern + textTemplate → OK", () => { + const result = detectTemplateMismatch("display", [prop("value", "textTemplate")]); + expect(result).toBeNull(); + }); + + it("null: display pattern + expression → OK", () => { + const result = detectTemplateMismatch("display", [prop("computed", "expression")]); + expect(result).toBeNull(); + }); + + it("warns: input pattern + no attribute", () => { + const result = detectTemplateMismatch("input", [prop("onChange", "action")]); + expect(result).not.toBeNull(); + expect(result).toContain("attribute"); + }); + + it("null: input pattern + attribute → OK", () => { + const result = detectTemplateMismatch("input", [prop("value", "attribute")]); + expect(result).toBeNull(); + }); + + it("warns: container pattern + no widgets", () => { + const result = detectTemplateMismatch("container", [prop("title", "textTemplate")]); + expect(result).not.toBeNull(); + expect(result).toContain("widgets"); + }); + + it("null: container pattern + widgets → OK", () => { + const result = detectTemplateMismatch("container", [prop("content", "widgets")]); + expect(result).toBeNull(); + }); + + it("warns: dataList pattern + missing datasource", () => { + const result = detectTemplateMismatch("dataList", [prop("content", "widgets")]); + expect(result).not.toBeNull(); + expect(result).toContain("datasource"); + }); + + it("warns: dataList pattern + missing widgets", () => { + const result = detectTemplateMismatch("dataList", [prop("items", "datasource")]); + expect(result).not.toBeNull(); + expect(result).toContain("widgets"); + }); + + it("warns: dataList pattern + both missing", () => { + const result = detectTemplateMismatch("dataList", [prop("title", "string")]); + expect(result).not.toBeNull(); + expect(result).toContain("datasource"); + expect(result).toContain("widgets"); + }); + + it("null: dataList pattern + datasource + widgets → OK", () => { + const result = detectTemplateMismatch("dataList", [prop("items", "datasource"), prop("content", "widgets")]); + expect(result).toBeNull(); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts new file mode 100644 index 0000000000..95fbfa2576 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts @@ -0,0 +1,206 @@ +import { existsSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; +import { analyzeMpk } from "@/tools/utils/mpk-analyzer"; + +const CURRENT_DIR = dirname(fileURLToPath(import.meta.url)); +const REPO_ROOT = resolve(CURRENT_DIR, "../../../../../.."); + +// Repository-relative paths to fixture .mpk files. +// Tests skip gracefully when files are not present on the current machine. +const KNOWN_GOOD_MPK = `${REPO_ROOT}/packages/pluggableWidgets/badge-web/dist/3.2.3/Badge.mpk`; + +const MCP_GENERATED_MPK = `${REPO_ROOT}/packages/pluggable-widgets-mcp/generations/asciiArtWidget/dist/1.0.0/mendix.AsciiArtWidget.mpk`; + +describe("mpk-analyzer (diagnostic)", () => { + it("analyzes a known-good .mpk from pluggableWidgets", () => { + if (!existsSync(KNOWN_GOOD_MPK)) { + console.log(`[SKIP] fixture not found: ${KNOWN_GOOD_MPK}`); + expect(true).toBe(true); + return; + } + + const analysis = analyzeMpk(KNOWN_GOOD_MPK); + + console.log("\n=== Known-Good MPK Analysis ==="); + console.log(`Path: ${analysis.mpkPath}`); + console.log(`Size: ${analysis.mpkSizeBytes} bytes`); + console.log(`Files (${analysis.files.length}):`); + for (const f of analysis.files) { + console.log(` ${f.path} (${f.sizeBytes} bytes)`); + } + if (analysis.packageXml) { + console.log(`\npackage.xml:`); + console.log(` clientModuleName: ${analysis.packageXml.clientModuleName}`); + console.log(` version: ${analysis.packageXml.version}`); + console.log(` widgetFilePath: ${analysis.packageXml.widgetFilePath}`); + console.log(` filesPath: ${analysis.packageXml.filesPath}`); + } + if (analysis.widgetXml) { + console.log(`\nWidget XML:`); + console.log(` id: ${analysis.widgetXml.id}`); + console.log(` pluginWidget: ${analysis.widgetXml.pluginWidget}`); + console.log(` needsEntityContext:${analysis.widgetXml.needsEntityContext}`); + console.log(` propertyCount: ${analysis.widgetXml.propertyCount}`); + } + if (analysis.bundle) { + console.log(`\nJS Bundle:`); + console.log(` fileName: ${analysis.bundle.fileName}`); + console.log(` sizeBytes: ${analysis.bundle.sizeBytes}`); + console.log(` format: ${analysis.bundle.format}`); + console.log(` containsDefine: ${analysis.bundle.containsDefine}`); + console.log(` exportDefault: ${analysis.bundle.containsExportDefault}`); + console.log(` exportNamed: ${analysis.bundle.containsExportNamed}`); + console.log(` hasUseStrict: ${analysis.bundle.hasUseStrict}`); + console.log(` exportPattern: ${JSON.stringify(analysis.bundle.exportPattern)}`); + } + if (analysis.errors.length > 0) { + console.log(`\nErrors: ${JSON.stringify(analysis.errors)}`); + } + + // Structural assertions — known-good widget must parse cleanly + expect(analysis.errors).toHaveLength(0); + expect(analysis.packageXml).toBeDefined(); + expect(analysis.bundle).toBeDefined(); + // format is diagnostic output, not asserted — printed above for comparison + }); + + it("analyzes an MCP-generated .mpk", () => { + if (!existsSync(MCP_GENERATED_MPK)) { + console.log(`[SKIP] fixture not found: ${MCP_GENERATED_MPK}`); + expect(true).toBe(true); + return; + } + + const analysis = analyzeMpk(MCP_GENERATED_MPK); + + console.log("\n=== MCP-Generated MPK Analysis ==="); + console.log(`Path: ${analysis.mpkPath}`); + console.log(`Size: ${analysis.mpkSizeBytes} bytes`); + console.log(`Files (${analysis.files.length}):`); + for (const f of analysis.files) { + console.log(` ${f.path} (${f.sizeBytes} bytes)`); + } + if (analysis.packageXml) { + console.log(`\npackage.xml:`); + console.log(` clientModuleName: ${analysis.packageXml.clientModuleName}`); + console.log(` version: ${analysis.packageXml.version}`); + console.log(` widgetFilePath: ${analysis.packageXml.widgetFilePath}`); + console.log(` filesPath: ${analysis.packageXml.filesPath}`); + } + if (analysis.widgetXml) { + console.log(`\nWidget XML:`); + console.log(` id: ${analysis.widgetXml.id}`); + console.log(` pluginWidget: ${analysis.widgetXml.pluginWidget}`); + console.log(` needsEntityContext:${analysis.widgetXml.needsEntityContext}`); + console.log(` propertyCount: ${analysis.widgetXml.propertyCount}`); + } + if (analysis.bundle) { + console.log(`\nJS Bundle:`); + console.log(` fileName: ${analysis.bundle.fileName}`); + console.log(` sizeBytes: ${analysis.bundle.sizeBytes}`); + console.log(` format: ${analysis.bundle.format}`); + console.log(` containsDefine: ${analysis.bundle.containsDefine}`); + console.log(` exportDefault: ${analysis.bundle.containsExportDefault}`); + console.log(` exportNamed: ${analysis.bundle.containsExportNamed}`); + console.log(` hasUseStrict: ${analysis.bundle.hasUseStrict}`); + console.log(` exportPattern: ${JSON.stringify(analysis.bundle.exportPattern)}`); + } + if (analysis.errors.length > 0) { + console.log(`\nErrors: ${JSON.stringify(analysis.errors)}`); + } + + // Diagnostic only — we don't assert expected format because we're discovering it + expect(analysis.mpkSizeBytes).toBeGreaterThan(0); + }); + + it("compares known-good vs MCP-generated side by side", () => { + const goodExists = existsSync(KNOWN_GOOD_MPK); + const mcpExists = existsSync(MCP_GENERATED_MPK); + + if (!goodExists || !mcpExists) { + console.log(`[SKIP] both fixtures required for comparison`); + console.log(` known-good: ${goodExists ? "found" : "MISSING"}`); + console.log(` mcp-generated: ${mcpExists ? "found" : "MISSING"}`); + expect(true).toBe(true); + return; + } + + const good = analyzeMpk(KNOWN_GOOD_MPK); + const mcp = analyzeMpk(MCP_GENERATED_MPK); + + console.log("\n=== Side-by-Side Comparison ==="); + console.log(`${"FIELD".padEnd(30)} ${"KNOWN-GOOD".padEnd(40)} MCP-GENERATED`); + console.log("-".repeat(100)); + + const row = (label: string, a: unknown, b: unknown): void => { + const aStr = String(a ?? "(none)"); + const bStr = String(b ?? "(none)"); + const flag = aStr !== bStr ? " <<<" : ""; + console.log(`${label.padEnd(30)} ${aStr.padEnd(40)} ${bStr}${flag}`); + }; + + row("mpkSizeBytes", good.mpkSizeBytes, mcp.mpkSizeBytes); + row("fileCount", good.files.length, mcp.files.length); + row("packageXml.clientModuleName", good.packageXml?.clientModuleName, mcp.packageXml?.clientModuleName); + row("packageXml.version", good.packageXml?.version, mcp.packageXml?.version); + row("packageXml.widgetFilePath", good.packageXml?.widgetFilePath, mcp.packageXml?.widgetFilePath); + row("packageXml.filesPath", good.packageXml?.filesPath, mcp.packageXml?.filesPath); + row("widgetXml.id", good.widgetXml?.id, mcp.widgetXml?.id); + row("widgetXml.pluginWidget", good.widgetXml?.pluginWidget, mcp.widgetXml?.pluginWidget); + row("widgetXml.needsEntityContext", good.widgetXml?.needsEntityContext, mcp.widgetXml?.needsEntityContext); + row("widgetXml.propertyCount", good.widgetXml?.propertyCount, mcp.widgetXml?.propertyCount); + row("bundle.format", good.bundle?.format, mcp.bundle?.format); + row("bundle.containsDefine", good.bundle?.containsDefine, mcp.bundle?.containsDefine); + row("bundle.exportDefault", good.bundle?.containsExportDefault, mcp.bundle?.containsExportDefault); + row("bundle.exportNamed", good.bundle?.containsExportNamed, mcp.bundle?.containsExportNamed); + row("bundle.hasUseStrict", good.bundle?.hasUseStrict, mcp.bundle?.hasUseStrict); + row("bundle.sizeBytes", good.bundle?.sizeBytes, mcp.bundle?.sizeBytes); + row("errors", good.errors.join("|"), mcp.errors.join("|")); + + console.log("\nKnown-good file list:"); + for (const f of good.files) console.log(` ${f.path}`); + console.log("\nMCP-generated file list:"); + for (const f of mcp.files) console.log(` ${f.path}`); + + // The comparison runs — the console output is the diagnostic result. + // We assert only that both analyses completed without fatal errors. + expect(good.mpkSizeBytes).toBeGreaterThan(0); + expect(mcp.mpkSizeBytes).toBeGreaterThan(0); + }); + + it("widget XML id matches file path convention", () => { + if (!existsSync(MCP_GENERATED_MPK)) { + console.log(`[SKIP] fixture not found: ${MCP_GENERATED_MPK}`); + expect(true).toBe(true); + return; + } + + const analysis = analyzeMpk(MCP_GENERATED_MPK); + + if (!analysis.widgetXml?.id) { + console.log("[SKIP] no widget XML id found in MPK"); + expect(true).toBe(true); + return; + } + + // Convert dotted id to slash-based path: "mendix.asciiartwidget.AsciiArtWidget" + // → "mendix/asciiartwidget/AsciiArtWidget" + const idAsPath = analysis.widgetXml.id.replace(/\./g, "/"); + const jsPath = `${idAsPath}.js`; + + console.log(`\n=== Widget Id / File Path Check ===`); + console.log(` widgetXml.id: ${analysis.widgetXml.id}`); + console.log(` expected js path: ${jsPath}`); + console.log(` files in MPK:`); + for (const f of analysis.files) { + console.log(` ${f.path}`); + } + + const match = analysis.files.some( + f => f.path === jsPath || f.path.endsWith(`/${idAsPath.split("/").slice(-2).join("/")}.js`) + ); + expect(match).toBe(true); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts index 709a4cb149..31ea3efd0a 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from "vitest"; import { - createToolResponse, createErrorResponse, createStructuredError, - createStructuredErrorResponse + createStructuredErrorResponse, + createToolResponse } from "@/tools/utils/response"; describe("createToolResponse", () => { diff --git a/packages/pluggable-widgets-mcp/vitest.config.ts b/packages/pluggable-widgets-mcp/vitest.config.ts index 873146deac..d4944f8bca 100644 --- a/packages/pluggable-widgets-mcp/vitest.config.ts +++ b/packages/pluggable-widgets-mcp/vitest.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ plugins: [tsconfigPaths()], test: { globals: false, - include: ["src/**/__tests__/*.test.ts"], + include: ["src/**/__tests__/**/*.test.ts"], testTimeout: 10_000, restoreMocks: true } From 3fbd7b3d936f6783ada3143280cea6649b741313 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 4 Mar 2026 02:02:38 +0100 Subject: [PATCH 24/36] chore: add mpk-analyzer utility, widget-patterns doc, update .gitignore --- packages/pluggable-widgets-mcp/.gitignore | 3 +- .../docs/widget-patterns.md | 72 ++++++ .../src/tools/utils/mpk-analyzer.ts | 219 ++++++++++++++++++ 3 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts diff --git a/packages/pluggable-widgets-mcp/.gitignore b/packages/pluggable-widgets-mcp/.gitignore index e6cd8c7e6d..7c204398e4 100644 --- a/packages/pluggable-widgets-mcp/.gitignore +++ b/packages/pluggable-widgets-mcp/.gitignore @@ -1,3 +1,4 @@ dist/ generations/ -node_modules/ \ No newline at end of file +node_modules/ +mcp-session-logs/ \ No newline at end of file diff --git a/packages/pluggable-widgets-mcp/docs/widget-patterns.md b/packages/pluggable-widgets-mcp/docs/widget-patterns.md index 6cf76aa6c4..782e01b488 100644 --- a/packages/pluggable-widgets-mcp/docs/widget-patterns.md +++ b/packages/pluggable-widgets-mcp/docs/widget-patterns.md @@ -515,6 +515,78 @@ if (props.value?.status === "available" && !props.value.readOnly) { } ``` +### Numeric Attribute Types (Integer, Long, Decimal) — CRITICAL + +**Integer, Long, and Decimal attributes use `Big` from `big.js`, NOT JavaScript's native `number` or `BigInt`.** + +```tsx +import Big from "big.js"; // REQUIRED for numeric attributes + +// Reading — .value is a Big object, call .toNumber() to get a JS number +const count = props.counterValue?.value?.toNumber() ?? 0; + +// Writing — pass new Big(value), NEVER BigInt(value) or a plain number +if (props.counterValue?.status === "available" && !props.counterValue.readOnly) { + props.counterValue.setValue(new Big(newCount)); +} +``` + +**Common mistakes:** + +| Wrong | Correct | +| -------------------------------------------- | ---------------------------------- | +| `BigInt(value)` | `new Big(value)` | +| `Number(props.attr.value)` | `props.attr.value.toNumber()` | +| `props.attr.setValue(42)` | `props.attr.setValue(new Big(42))` | +| `props.attr.value` (used as number directly) | `props.attr.value.toNumber()` | + +**Counter widget pattern (Integer/Long attribute):** + +```tsx +import { ReactElement, createElement, useState, useEffect, useCallback } from "react"; +import Big from "big.js"; +import { CounterContainerProps } from "../typings/CounterProps"; +import "./ui/Counter.scss"; + +export default function Counter(props: CounterContainerProps): ReactElement { + const { counterValue, class: className, style, tabIndex } = props; + const [count, setCount] = useState(counterValue?.value?.toNumber() ?? 0); + + useEffect(() => { + if (counterValue?.status === "available" && counterValue.value !== undefined) { + setCount(counterValue.value.toNumber()); + } + }, [counterValue]); + + const handleChange = useCallback( + (delta: number) => { + const newCount = count + delta; + setCount(newCount); + if (counterValue?.status === "available" && !counterValue.readOnly) { + counterValue.setValue(new Big(newCount)); + } + }, + [count, counterValue] + ); + + const isReadOnly = counterValue?.readOnly ?? false; + + return ( +
+ + {count} + +
+ ); +} +``` + +**String attributes** use plain strings — `setValue("text")` is correct for those. Only Integer, Long, and Decimal require `Big`. + ### Loading States Handle datasource loading: diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts b/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts new file mode 100644 index 0000000000..14f6cbfea7 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts @@ -0,0 +1,219 @@ +import { execSync } from "node:child_process"; +import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs"; +import { basename, join } from "node:path"; +import { tmpdir } from "node:os"; + +export interface MpkFileEntry { + path: string; + sizeBytes: number; +} + +export interface PackageXmlInfo { + clientModuleName?: string; + version?: string; + widgetFilePath?: string; + filesPath?: string; + raw: string; +} + +export interface WidgetXmlInfo { + id?: string; + pluginWidget?: boolean; + needsEntityContext?: boolean; + propertyCount: number; + raw: string; +} + +export interface BundleInfo { + fileName: string; + sizeBytes: number; + format: "amd" | "esm" | "unknown"; + hasUseStrict: boolean; + exportPattern: string[]; + containsDefine: boolean; + containsExportDefault: boolean; + containsExportNamed: boolean; +} + +export interface MpkAnalysis { + mpkPath: string; + mpkSizeBytes: number; + files: MpkFileEntry[]; + packageXml?: PackageXmlInfo; + widgetXml?: WidgetXmlInfo; + bundle?: BundleInfo; + errors: string[]; +} + +/** + * Analyzes an .mpk file (ZIP archive) and returns structural findings. + * Unzips to a temp directory, reads package.xml, widget XML, and the JS bundle. + * No new dependencies — uses the macOS/Linux `unzip` command. + */ +export function analyzeMpk(mpkPath: string): MpkAnalysis { + const errors: string[] = []; + + if (!existsSync(mpkPath)) { + return { + mpkPath, + mpkSizeBytes: 0, + files: [], + errors: [`File not found: ${mpkPath}`] + }; + } + + const mpkSizeBytes = statSync(mpkPath).size; + const tempDir = mkdtempSync(join(tmpdir(), "mpk-analyze-")); + + try { + // Unzip to temp directory + try { + execSync(`unzip -q "${mpkPath}" -d "${tempDir}"`, { timeout: 30000 }); + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + errors.push(`unzip failed: ${msg}`); + return { mpkPath, mpkSizeBytes, files: [], errors }; + } + + // Catalog all files + const files = catalogFiles(tempDir, tempDir); + + // Parse package.xml + const packageXml = parsePackageXml(tempDir, errors); + + // Find and parse widget XML + const widgetXml = parseWidgetXml(tempDir, files, errors); + + // Find and analyze JS bundle + const bundle = analyzeBundle(tempDir, files, errors); + + return { mpkPath, mpkSizeBytes, files, packageXml, widgetXml, bundle, errors }; + } finally { + try { + rmSync(tempDir, { recursive: true, force: true }); + } catch { + // ignore cleanup errors + } + } +} + +function catalogFiles(dir: string, rootDir: string): MpkFileEntry[] { + const entries: MpkFileEntry[] = []; + try { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + entries.push(...catalogFiles(fullPath, rootDir)); + } else { + const relativePath = fullPath.slice(rootDir.length + 1); + entries.push({ path: relativePath, sizeBytes: statSync(fullPath).size }); + } + } + } catch { + // ignore + } + return entries; +} + +function parsePackageXml(tempDir: string, errors: string[]): PackageXmlInfo | undefined { + const xmlPath = join(tempDir, "package.xml"); + if (!existsSync(xmlPath)) { + errors.push("package.xml not found"); + return undefined; + } + + const raw = readFileSync(xmlPath, "utf-8"); + + const clientModuleName = extractXmlAttr(raw, "clientModule", "name"); + const version = extractXmlAttr(raw, "clientModule", "version"); + + // + const widgetFileMatch = raw.match(/ + const filesMatch = raw.match(/ f.path.endsWith(".xml") && !f.path.includes("package.xml")); + if (!xmlFile) { + errors.push("No widget .xml found"); + return undefined; + } + + const raw = readFileSync(join(tempDir, xmlFile.path), "utf-8"); + + const id = extractXmlAttr(raw, "widget", "id"); + const pluginWidgetStr = extractXmlAttr(raw, "widget", "pluginWidget"); + const needsEntityContextStr = extractXmlAttr(raw, "widget", "needsEntityContext"); + + const propertyCount = (raw.match(/ + f.path.endsWith(".js") && + f.path.includes("/") && + !f.path.includes("editorPreview") && + !f.path.includes("editorConfig") + ) ?? + files.find( + f => f.path.endsWith(".js") && !f.path.includes("editorPreview") && !f.path.includes("editorConfig") + ); + + if (!jsFile) { + errors.push("No JS bundle found"); + return undefined; + } + + const fullPath = join(tempDir, jsFile.path); + const content = readFileSync(fullPath, "utf-8"); + + const containsDefine = content.includes("define("); + const containsExportDefault = /export\s+default\s/.test(content); + const containsExportNamed = /export\s+\{/.test(content) || /export\s+function\s/.test(content); + const hasUseStrict = content.includes('"use strict"') || content.includes("'use strict'"); + + let format: "amd" | "esm" | "unknown" = "unknown"; + if (containsDefine) format = "amd"; + else if (containsExportDefault || containsExportNamed) format = "esm"; + + // Collect first few export/define patterns for comparison + const exportPattern: string[] = []; + const patterns = content.matchAll(/(export\s+(?:default\s+)?(?:function|class|const|let|var)\s+\w+|define\s*\()/g); + for (const match of patterns) { + if (exportPattern.length < 5) exportPattern.push(match[0]); + } + + return { + fileName: basename(jsFile.path), + sizeBytes: jsFile.sizeBytes, + format, + hasUseStrict, + exportPattern, + containsDefine, + containsExportDefault, + containsExportNamed + }; +} + +function extractXmlAttr(xml: string, tagName: string, attrName: string): string | undefined { + const pattern = new RegExp(`<${tagName}[^>]+${attrName}="([^"]+)"`, "i"); + return xml.match(pattern)?.[1]; +} From d060acafda0f5087e417d6060958d8a435143d60 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:34:04 +0200 Subject: [PATCH 25/36] build(mcp): drive the widget generator through a Yeoman adapter The package depended on a `file:` path outside the repo that resolved to a local 10.24.0 fork of @mendix/generator-widget, making it uninstallable for anyone else. That fork was needed because create-widget drove the generator by simulating its interactive CLI: `--default --name X ...` flags that exist only in the fork, plus scraping stdout to infer progress. Replace the simulation with yeoman-environment's own extension point. The new AnswerAdapter supplies answers as data: a supplied answer wins, the prompt's own default fills a gap, and a prompt that is neither answered nor defaulted throws MissingAnswerError, so an upstream rename fails loudly instead of silently defaulting. It implements the full QueuedAdapter shape because environment-base assigns the adapter directly rather than wrapping it. Nothing in the adapter writes to stdout. Under the stdio transport that channel carries JSON-RPC, and the generator's banner would corrupt it. Scaffolding and dependency installation are now separate steps with separate outcomes, so a registry stall still leaves a usable scaffold. The 14 prompt names are pinned in a test. They are the generator's contract, and wrong names fall through to defaults without any error. Also switches the package off the browser/React eslint config it was borrowing and onto a Node/TS one, bumps engines to >=22, and ships docs/ so the MCP resources resolve in an installed copy. --- package.json | 2 +- .../pluggable-widgets-mcp/eslint.config.mjs | 30 ++- packages/pluggable-widgets-mcp/package.json | 11 +- .../utils/__tests__/answer-adapter.test.ts | 83 +++++++ .../tools/utils/__tests__/generator.test.ts | 112 +++++++++ .../src/tools/utils/answer-adapter.ts | 200 ++++++++++++++++ .../src/tools/utils/generator.ts | 224 ++++++++++-------- pnpm-lock.yaml | 105 +++----- 8 files changed, 598 insertions(+), 169 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/answer-adapter.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/generator.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/answer-adapter.ts diff --git a/package.json b/package.json index 741f82e8c2..eb4ca933ad 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "create-gh-release": "turbo run create-gh-release --concurrency 1", "create-translation": "turbo run create-translation", "include-oss-in-artifact": "pnpm --filter @mendix/automation-utils run include-oss-in-artifact", - "start:mcp": "pnpm --filter pluggable-widgets-mcp run start", "lint": "turbo run lint --continue --concurrency 1", "merge-changelogs-pr": "pnpm --filter @mendix/automation-utils run merge-changelogs-pr", "oss-clearance": "pnpm --filter @mendix/automation-utils run oss-clearance", @@ -21,6 +20,7 @@ "prepare-release": "pnpm --filter @mendix/automation-utils run prepare-release", "publish-marketplace": "turbo run publish-marketplace", "release": "turbo run release", + "start:mcp": "pnpm --filter @mendix/pluggable-widgets-mcp run start", "test": "turbo run test --continue --concurrency 1", "update-screenshots": "node automation/scripts/update-screenshots.mjs", "update-screenshots-local": "node automation/scripts/update-screenshots-local.mjs", diff --git a/packages/pluggable-widgets-mcp/eslint.config.mjs b/packages/pluggable-widgets-mcp/eslint.config.mjs index ed68ae9e78..466c5f6440 100644 --- a/packages/pluggable-widgets-mcp/eslint.config.mjs +++ b/packages/pluggable-widgets-mcp/eslint.config.mjs @@ -1,3 +1,31 @@ import config from "@mendix/eslint-config-web-widgets/widget-ts.mjs"; -export default config; +/** + * This package is a Node server, not a browser widget, so the shared widget config needs two + * corrections: + * + * 1. It sets `tsconfigRootDir` to its own directory, which makes the TypeScript project service + * resolve our files against the wrong tsconfig — build output under `dist/` then fails with + * "not found by the project service". + * 2. Build output and scaffolded widgets should not be linted at all. + * + * The React/JSX and Jest blocks in the shared config are scoped to `*.tsx` and `*.spec.ts`, neither + * of which exists here (tests are `*.test.ts` under vitest), so they are inert and there is no + * reason to fork the rule set. + */ +export default [ + { + ignores: ["dist/**", "generations/**"] + }, + ...config, + { + name: "pluggable-widgets-mcp: type-aware linting rooted in this package", + files: ["**/*.ts"], + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname + } + } + } +]; diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index d21d2cb1ec..dff627d466 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -11,7 +11,8 @@ "type": "module", "main": "dist/index.js", "files": [ - "dist" + "dist", + "docs" ], "scripts": { "build": "tsc && tsc-alias -p tsconfig.json --resolve-full-paths && chmod +x dist/index.js", @@ -25,14 +26,13 @@ "test:watch": "vitest" }, "dependencies": { - "@mendix/generator-widget": "file:../../../widgets-tools/packages/generator-widget", + "@mendix/generator-widget": "^11.11.0", "@modelcontextprotocol/sdk": "^1.24.2", - "cors": "^2.8.5", "express": "^5.1.0", + "yeoman-environment": "^6.1.0", "zod": "^4.1.13" }, "devDependencies": { - "@types/cors": "^2.8.19", "@types/express": "^5.0.6", "@types/node": "^22.0.0", "tsc-alias": "^1.8.16", @@ -43,8 +43,7 @@ "vitest": "^0.34.6" }, "keywords": [], - "packageManager": "pnpm@10.17.0", "engines": { - "node": ">=20" + "node": ">=22" } } diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/answer-adapter.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/answer-adapter.test.ts new file mode 100644 index 0000000000..6bc1f3c3af --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/answer-adapter.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it, vi } from "vitest"; +import { AnswerAdapter, InvalidAnswerError, MissingAnswerError } from "@/tools/utils/answer-adapter"; + +describe("AnswerAdapter.prompt", () => { + it("uses the supplied answer over the prompt default", async () => { + const adapter = new AnswerAdapter({ name: "MyWidget" }); + const answers = await adapter.prompt([{ name: "name", default: "DefaultWidget" }]); + expect(answers.name).toBe("MyWidget"); + }); + + it("falls back to the prompt default when no answer is supplied", async () => { + const adapter = new AnswerAdapter({}); + const answers = await adapter.prompt([{ name: "license", default: "Apache-2.0" }]); + expect(answers.license).toBe("Apache-2.0"); + }); + + it("resolves function-valued defaults against the answers so far", async () => { + const adapter = new AnswerAdapter({ boilerplate: "full" }); + const answers = await adapter.prompt([ + { name: "boilerplate" }, + { name: "hasUnitTests", default: (a: Record) => a.boilerplate === "full" } + ]); + expect(answers.hasUnitTests).toBe(true); + }); + + it("throws when a prompt has neither an answer nor a default", async () => { + const adapter = new AnswerAdapter({}); + await expect(adapter.prompt([{ name: "somethingNew", message: "What now?" }])).rejects.toBeInstanceOf( + MissingAnswerError + ); + }); + + it("throws when the generator's own validate rejects the value", async () => { + const adapter = new AnswerAdapter({ name: "not valid!" }); + await expect( + adapter.prompt([{ name: "name", validate: () => "may only contain [a-zA-Z]" }]) + ).rejects.toBeInstanceOf(InvalidAnswerError); + }); + + it("honours a `when` guard so conditional prompts are not demanded", async () => { + const adapter = new AnswerAdapter({ platform: "native" }); + const answers = await adapter.prompt([ + { name: "platform" }, + { name: "hasE2eTests", when: (a: Record) => a.platform === "web" } + ]); + expect(answers).not.toHaveProperty("hasE2eTests"); + expect(adapter.askedFor).toEqual(["platform"]); + }); + + it("records every prompt it was asked, in order", async () => { + const adapter = new AnswerAdapter({ a: 1, b: 2 }); + await adapter.prompt([{ name: "a" }, { name: "b" }]); + expect(adapter.askedFor).toEqual(["a", "b"]); + }); + + it("never writes to stdout — that channel carries MCP JSON-RPC", () => { + const stdout = vi.spyOn(process.stdout, "write").mockReturnValue(true); + vi.spyOn(console, "error").mockImplementation(() => undefined); + + const adapter = new AnswerAdapter({}); + adapter.log("a banner"); + adapter.log.create("src/Widget.tsx"); + adapter.log.error("something broke"); + adapter.log.colored([{ message: "coloured" }]); + + expect(stdout).not.toHaveBeenCalled(); + }); + + it("routes generator output to stderr, with routine detail at debug level", () => { + vi.spyOn(process.stdout, "write").mockReturnValue(true); + const stderr = vi.spyOn(console, "error").mockImplementation(() => undefined); + + const adapter = new AnswerAdapter({}); + + // Per-file chatter is debug, so it is filtered at the default level... + adapter.log.create("src/Widget.tsx"); + expect(stderr).not.toHaveBeenCalled(); + + // ...but the generator's own errors always surface. + adapter.log.error("template render failed"); + expect(stderr).toHaveBeenCalledWith(expect.stringContaining("template render failed")); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/generator.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/generator.test.ts new file mode 100644 index 0000000000..1cec7cfd80 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/generator.test.ts @@ -0,0 +1,112 @@ +import { existsSync, mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { ToolContext } from "@/tools/types"; +import { buildGeneratorAnswers, runWidgetGenerator } from "@/tools/utils/generator"; +import { ProgressTracker } from "@/tools/utils/progress-tracker"; + +/** + * The generator's prompt names are an external contract we depend on. If an upstream release + * renames one, our answer silently stops applying and the prompt's default takes over — a change + * that produces a working-but-wrong widget rather than an error. Pinning the set turns that into a + * test failure. + */ +const EXPECTED_PROMPTS = [ + "name", + "description", + "organization", + "copyright", + "license", + "version", + "author", + "projectPath", + "programmingLanguage", + "programmingStyle", + "platform", + "boilerplate", + "hasUnitTests", + "hasE2eTests" +]; + +const OPTIONS = { + name: "ProbeWidget", + description: "a probe widget", + version: "1.0.0", + author: "Mendix", + license: "Apache-2.0", + organization: "mendix", + template: "empty" as const, + programmingLanguage: "typescript" as const, + unitTests: false, + e2eTests: false +}; + +function stubTracker(): ProgressTracker { + const context = { sendNotification: async () => undefined } as unknown as ToolContext; + return new ProgressTracker({ context, logger: "test", totalSteps: 3 }); +} + +describe("buildGeneratorAnswers", () => { + it("maps our option names onto the generator's prompt names", () => { + const answers = buildGeneratorAnswers(OPTIONS, "../"); + expect(answers).toMatchObject({ + name: "ProbeWidget", + boilerplate: "empty", + hasUnitTests: false, + hasE2eTests: false, + platform: "web", + programmingStyle: "function" + }); + }); + + it("omits copyright so the generator's current-year default applies", () => { + expect(buildGeneratorAnswers(OPTIONS, "../")).not.toHaveProperty("copyright"); + }); + + it("answers every prompt the generator asks except copyright", () => { + const supplied = Object.keys(buildGeneratorAnswers(OPTIONS, "../")); + expect(supplied.sort()).toEqual(EXPECTED_PROMPTS.filter(p => p !== "copyright").sort()); + }); +}); + +describe("runWidgetGenerator", () => { + let dir: string; + let stdout: { mock: { calls: unknown[][] } }; + + beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), "gen-test-")); + // stderr is noisy (the generator logs a banner); silence it but keep stdout observable. + vi.spyOn(console, "error").mockImplementation(() => undefined); + stdout = vi.spyOn(process.stdout, "write").mockReturnValue(true); + }); + + afterEach(() => { + vi.restoreAllMocks(); + rmSync(dir, { recursive: true, force: true }); + }); + + it("scaffolds a widget without prompting, and is asked exactly the prompts we pin", async () => { + const { askedFor } = await runWidgetGenerator(OPTIONS, stubTracker(), dir); + + expect(askedFor).toEqual(EXPECTED_PROMPTS); + expect(existsSync(join(dir, "src", "ProbeWidget.tsx"))).toBe(true); + expect(existsSync(join(dir, "src", "ProbeWidget.xml"))).toBe(true); + expect(existsSync(join(dir, "package.json"))).toBe(true); + }, 60000); + + it("never writes to stdout — that channel belongs to MCP JSON-RPC", async () => { + await runWidgetGenerator(OPTIONS, stubTracker(), dir); + expect(stdout).not.toHaveBeenCalled(); + }, 60000); + + it("does not install dependencies", async () => { + await runWidgetGenerator(OPTIONS, stubTracker(), dir); + expect(existsSync(join(dir, "node_modules"))).toBe(false); + }, 60000); + + it("refuses to scaffold into a non-empty directory", async () => { + await runWidgetGenerator(OPTIONS, stubTracker(), dir); + await expect(runWidgetGenerator(OPTIONS, stubTracker(), dir)).rejects.toThrow(); + }, 60000); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/answer-adapter.ts b/packages/pluggable-widgets-mcp/src/tools/utils/answer-adapter.ts new file mode 100644 index 0000000000..7f2f759fd3 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/answer-adapter.ts @@ -0,0 +1,200 @@ +/** + * A Yeoman adapter that answers prompts from a supplied map instead of a TTY. + * + * Yeoman generators are interactive by design. Rather than simulating keystrokes or passing + * generator-specific CLI flags, we replace the environment's I/O layer — the supported extension + * point — so prompts are resolved from data. + * + * Two properties matter here: + * + * 1. Nothing is written to stdout. Under the STDIO transport stdout carries the MCP JSON-RPC + * stream, and the Mendix generator logs a large ASCII banner. All output goes to stderr. + * 2. An unanswered prompt with no default throws. A generator that adds a prompt we don't know + * about must fail loudly rather than hang forever waiting on a TTY that isn't attached. + */ + +import { createLogger } from "./logger"; + +/** The subset of an inquirer question the generator actually uses. */ +interface PromptQuestion { + name: string; + message?: string; + default?: unknown | ((answers: Answers) => unknown | Promise); + when?: boolean | ((answers: Answers) => boolean | Promise); + validate?: (input: unknown, answers: Answers) => boolean | string | Promise; +} + +export type Answers = Record; + +/** Categories `yeoman-environment` calls on `adapter.log`. */ +const LOG_CATEGORIES = [ + "skip", + "force", + "create", + "invoke", + "conflict", + "identical", + "info", + "added", + "removed", + "write", + "writeln", + "ok", + "error" +] as const; + +type LogCategory = (typeof LOG_CATEGORIES)[number]; + +type LogFn = ((...args: unknown[]) => LogFn) & + Record LogFn> & { + colored: (parts: Array<{ message: string }>) => LogFn; + }; + +/** + * Builds the chainable, category-bearing logger object Yeoman expects, backed by our own logger. + * + * The generator's file-by-file output is routine detail, so it logs at debug; only its `error` + * category is surfaced by default. + */ +function createGeneratorLog(tag: string): LogFn { + const logger = createLogger(tag); + + const write = (category: LogCategory | undefined, args: unknown[]): LogFn => { + const text = args + .map(argument => (typeof argument === "string" ? argument : JSON.stringify(argument))) + .join(" ") + .trimEnd(); + + if (text) { + const message = category ? `${category}: ${text}` : text; + if (category === "error") { + logger.error(message); + } else { + logger.debug(message); + } + } + return generatorLog; + }; + + const generatorLog = ((...args: unknown[]) => write(undefined, args)) as LogFn; + for (const category of LOG_CATEGORIES) { + generatorLog[category] = (...args: unknown[]) => write(category, args); + } + generatorLog.colored = parts => write(undefined, [parts.map(part => part.message).join("")]); + return generatorLog; +} + +export class MissingAnswerError extends Error { + constructor( + public readonly promptName: string, + message?: string + ) { + super( + `The widget generator asked for "${promptName}" but no answer was supplied and it has no default.` + + (message ? ` Prompt was: ${message}` : "") + ); + this.name = "MissingAnswerError"; + } +} + +export class InvalidAnswerError extends Error { + constructor( + public readonly promptName: string, + public readonly reason: string + ) { + super(`The widget generator rejected the value for "${promptName}": ${reason}`); + this.name = "InvalidAnswerError"; + } +} + +/** + * Implements the `QueuedAdapter` shape that `yeoman-environment` assigns straight onto + * `env.adapter` — it does not wrap a plain adapter, so `queue` and `progress` must exist. + */ +export class AnswerAdapter { + readonly log: LogFn; + readonly signal: AbortSignal; + + /** Prompt names the generator asked for, in order. Used to pin the contract in tests. */ + readonly askedFor: string[] = []; + + private readonly abortController = new AbortController(); + + constructor( + private readonly answers: Answers, + private readonly onStep?: (message: string) => void, + tag = "generator" + ) { + this.log = createGeneratorLog(tag); + this.signal = this.abortController.signal; + } + + async prompt(questions: PromptQuestion | PromptQuestion[], initialAnswers: Answers = {}): Promise { + const list = Array.isArray(questions) ? questions : [questions]; + const resolved: Answers = { ...initialAnswers }; + + for (const question of list) { + if (!(await this.shouldAsk(question, resolved))) { + continue; + } + + this.askedFor.push(question.name); + + const answer = await this.resolveAnswer(question, resolved); + if (answer === undefined) { + throw new MissingAnswerError(question.name, question.message); + } + + if (typeof question.validate === "function") { + const verdict = await question.validate(answer, resolved); + if (verdict !== true) { + throw new InvalidAnswerError( + question.name, + typeof verdict === "string" ? verdict : "invalid value" + ); + } + } + + resolved[question.name] = answer; + } + + return resolved; + } + + /** Honours inquirer's `when` guard so conditional prompts aren't demanded. */ + private async shouldAsk(question: PromptQuestion, answers: Answers): Promise { + if (typeof question.when === "function") { + return question.when(answers); + } + return question.when !== false; + } + + /** Supplied answer wins; otherwise fall back to the prompt's own default. */ + private async resolveAnswer(question: PromptQuestion, answers: Answers): Promise { + if (this.answers[question.name] !== undefined) { + return this.answers[question.name]; + } + if (answers[question.name] !== undefined) { + return answers[question.name]; + } + return typeof question.default === "function" ? question.default(answers) : question.default; + } + + async queue(task: (adapter: AnswerAdapter) => T | PromiseLike): Promise { + return task(this); + } + + async progress(task: (progress: { step: (prefix: string, message: string) => void }) => T): Promise { + return task({ + step: (prefix, message) => this.onStep?.(`${prefix} ${message}`.trim()) + }); + } + + close(): void { + // No resources to release; the environment calls this on completion. + } + + abort(reason?: unknown): void { + this.abortController.abort(reason); + } +} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts index 0589c887e5..24dbc82412 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/generator.ts @@ -1,11 +1,12 @@ import { spawn } from "node:child_process"; -import { resolve } from "node:path"; -import { GENERATIONS_DIR, PACKAGE_ROOT, SCAFFOLD_TIMEOUT_MS } from "@/config"; +import { createRequire } from "node:module"; +import { INSTALL_TIMEOUT_MS, SCAFFOLD_TIMEOUT_MS } from "@/config"; import { DEFAULT_WIDGET_OPTIONS, type WidgetOptions, type WidgetOptionsInput } from "@/tools/types"; +import { AnswerAdapter, type Answers } from "./answer-adapter"; +import { createLogger } from "./logger"; import { ProgressTracker } from "./progress-tracker"; -// Re-export for backward compatibility with existing imports -export { DEFAULT_WIDGET_OPTIONS }; +const installLog = createLogger("npm install"); /** * Progress milestones for widget scaffolding. @@ -16,6 +17,9 @@ export const SCAFFOLD_PROGRESS = { COMPLETE: 100 } as const; +/** Namespace the Mendix generator is registered under inside our Yeoman environment. */ +const GENERATOR_NAMESPACE = "@mendix/widget"; + /** * Builds widget options from input arguments with defaults applied. * Takes the schema-validated input (with optional fields) and returns @@ -37,125 +41,159 @@ export function buildWidgetOptions(args: WidgetOptionsInput): WidgetOptions { } /** - * Returns the path to the generator-widget binary installed in this package's node_modules. - * Using a direct path (rather than npx) ensures we always use the correct version - * regardless of the spawn cwd (which is set to outputDir for widget placement). + * Maps our options onto the generator's prompt names. + * + * These keys are the generator's contract, not ours — they come from + * `@mendix/generator-widget/generators/app/lib/prompttexts.js`. `generator.test.ts` pins the full + * set so an upstream rename fails loudly instead of silently falling back to a default. + * + * `copyright` is deliberately omitted: the generator's own default computes the current year, which + * is more correct than anything we can hardcode. */ -function getGeneratorBinPath(): string { - return resolve(PACKAGE_ROOT, "node_modules/.bin/generator-widget"); +export function buildGeneratorAnswers(options: WidgetOptions, projectPath: string): Answers { + return { + name: options.name, + description: options.description, + organization: options.organization, + license: options.license, + version: options.version, + author: options.author, + projectPath, + programmingLanguage: options.programmingLanguage, + programmingStyle: "function", + platform: "web", + boilerplate: options.template, + hasUnitTests: options.unitTests, + hasE2eTests: options.e2eTests + }; +} + +export interface ScaffoldResult { + /** Prompt names the generator asked for, in order. */ + askedFor: string[]; } /** - * Maps WidgetOptions to CLI flags for the non-interactive generator. - * Requires @mendix/generator-widget with --default flag support (commit 16cf75e). + * Thrown when the generator exceeds its time budget. + * + * A distinct type rather than a message the caller has to recognise by substring — categorising an + * error by `message.includes(...)` is the same guesswork this module exists to remove. */ -function buildWidgetFlags(options: WidgetOptions): string[] { - return [ - "--default", - "--name", - options.name, - "--description", - options.description, - "--organization", - options.organization, - "--copyright", - "© Mendix Technology BV 2026", - "--license", - options.license, - "--version", - options.version, - "--author", - options.author, - "--projectPath", - "../", - "--programmingLanguage", - options.programmingLanguage, - "--programmingStyle", - "function", - "--platform", - "web", - "--boilerplate", - options.template, - ...(options.unitTests ? ["--hasUnitTests"] : []), - ...(options.e2eTests ? ["--hasE2eTests"] : []) - ]; +export class ScaffoldTimeoutError extends Error { + constructor(public readonly timeoutMs: number) { + super(`Widget scaffold timed out after ${timeoutMs / 1000}s`); + this.name = "ScaffoldTimeoutError"; + } } /** - * Runs the Mendix widget generator using non-interactive CLI flags. - * Replaces the previous node-pty / interactive-prompt approach. + * Scaffolds a widget by running the Mendix generator in-process through a Yeoman environment + * whose I/O adapter answers prompts from data. + * + * `widgetDir` must be a fresh, empty directory. That is not just tidiness: the generator's `end()` + * hook spawns `pluggable-widgets-tools audit:fix`, `npm run lint:fix` and `npm run build` with + * `stdio: "inherit"` when it finds a populated `node_modules`, which would write directly into the + * MCP stdio channel. Scaffolding into an empty directory makes that branch unreachable — the + * generator's own `initializing()` refuses a non-empty target first. * - * @param options - Widget configuration options - * @param tracker - Progress tracker for notifications - * @param outputDir - Directory where the widget folder will be created + * Dependencies are NOT installed here; call `runNpmInstall` separately so a registry failure does + * not discard a perfectly good scaffold. */ export async function runWidgetGenerator( options: WidgetOptions, tracker: ProgressTracker, - outputDir: string = GENERATIONS_DIR -): Promise { - const flags = buildWidgetFlags(options); - const generatorBin = getGeneratorBinPath(); + widgetDir: string, + projectPath = "../" +): Promise { + // Imported lazily: yeoman-environment pulls in a large dependency graph, and the STDIO + // transport should not pay for it unless a widget is actually being scaffolded. + const { createEnv } = await import("yeoman-environment"); + + // Yeoman drives this callback from `adapter.progress()`, so scaffold steps reach the client's + // log panel as structured events rather than as text we would otherwise have to scrape. + const adapter = new AnswerAdapter( + buildGeneratorAnswers(options, projectPath), + step => { + tracker.info(step).catch(() => undefined); + }, + "create-widget" + ); + + const env = createEnv({ + adapter: adapter as never, + cwd: widgetDir + }); + + const require = createRequire(import.meta.url); + env.register(require.resolve("@mendix/generator-widget/generators/app/index.js"), { + namespace: GENERATOR_NAMESPACE + }); - return new Promise((resolve, reject) => { - tracker.start("initializing"); + tracker.start("scaffolding"); + + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new ScaffoldTimeoutError(SCAFFOLD_TIMEOUT_MS)), SCAFFOLD_TIMEOUT_MS).unref() + ); + + try { + await Promise.race([env.run(GENERATOR_NAMESPACE, { skipInstall: true }), timeout]); + return { askedFor: adapter.askedFor }; + } finally { + tracker.stop(); + } +} + +export interface InstallResult { + ok: boolean; + /** Populated only when `ok` is false. */ + error?: string; +} + +/** + * Installs the scaffolded widget's dependencies. + * + * Reported separately from scaffolding because the failure modes differ: a bad answer fails in + * milliseconds and leaves nothing behind, whereas a registry stall leaves a valid scaffold the user + * can finish by hand. This resolves rather than throws so the caller can report partial success. + */ +export async function runNpmInstall(widgetDir: string, tracker: ProgressTracker): Promise { + return new Promise(resolve => { + tracker.updateStep("installing", 2); + tracker.progress(SCAFFOLD_PROGRESS.INSTALLING, "Installing dependencies...").catch(() => undefined); - let stdout = ""; let stderr = ""; - let installingNotified = false; - const child = spawn(generatorBin, flags, { - cwd: outputDir, + const child = spawn("npm", ["install"], { + cwd: widgetDir, env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", DO_NOT_TRACK: "1" }, stdio: ["ignore", "pipe", "pipe"] }); - child.stdout.on("data", (data: Buffer) => { - const chunk = data.toString(); - stdout += chunk; - console.error(`[create-widget] stdout: ${chunk.trim()}`); - - if (!installingNotified && stdout.includes("npm install")) { - installingNotified = true; - tracker.updateStep("installing", 2); - tracker.progress(SCAFFOLD_PROGRESS.INSTALLING, "Installing dependencies...").catch(() => undefined); - tracker.info("Installing dependencies...").catch(() => undefined); - } - }); - + // Both child streams go to stderr — stdout belongs to the MCP protocol. + child.stdout.on("data", (data: Buffer) => installLog.debug(data.toString().trim())); child.stderr.on("data", (data: Buffer) => { const chunk = data.toString(); stderr += chunk; - console.error(`[create-widget] stderr: ${chunk.trim()}`); + installLog.debug(chunk.trim()); }); - const timeout = setTimeout(() => { - tracker.stop(); + const timer = setTimeout(() => { child.kill(); - reject(new Error("Widget scaffold timed out after 5 minutes")); - }, SCAFFOLD_TIMEOUT_MS); - - child.on("close", (exitCode: number | null) => { - clearTimeout(timeout); - tracker.stop(); - - if (exitCode === 0) { - console.error(`[create-widget] Widget scaffolded successfully`); - resolve(); - } else { - console.error(`[create-widget] Widget scaffold failed with exit code ${exitCode}`); - reject( - new Error( - `Generator exited with code ${exitCode}\nStderr: ${stderr.slice(-2000)}\nStdout: ${stdout.slice(-1000)}` - ) - ); - } + resolve({ ok: false, error: `npm install timed out after ${INSTALL_TIMEOUT_MS / 1000}s` }); + }, INSTALL_TIMEOUT_MS); + + child.on("close", (code: number | null) => { + clearTimeout(timer); + resolve( + code === 0 + ? { ok: true } + : { ok: false, error: `npm install exited with code ${code}\n${stderr.slice(-2000)}` } + ); }); child.on("error", (err: Error) => { - clearTimeout(timeout); - tracker.stop(); - reject(new Error(`Failed to spawn generator: ${err.message}`)); + clearTimeout(timer); + resolve({ ok: false, error: `Failed to spawn npm install: ${err.message}` }); }); }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80022c2d74..7c55e1477f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6363,12 +6363,8 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@5.0.3: - resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} - engines: {node: 18 || 20 || >=22} - - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + brace-expansion@5.0.8: + resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} engines: {node: 20 || >=22} braces@3.0.3: @@ -7677,8 +7673,8 @@ packages: get-canvas-context@1.0.2: resolution: {integrity: sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A==} - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -7948,8 +7944,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} icss-replace-symbols@1.1.0: @@ -8881,10 +8877,6 @@ packages: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - minimatch@10.2.2: - resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} - engines: {node: 18 || 20 || >=22} - minimatch@10.2.6: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} @@ -8899,8 +8891,8 @@ packages: resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -8910,10 +8902,6 @@ packages: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} @@ -9284,10 +9272,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} - engines: {node: 20 || >=22} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -12546,7 +12530,7 @@ snapshots: '@commitlint/is-ignored@21.2.0': dependencies: '@commitlint/types': 21.2.0 - semver: 7.7.3 + semver: 7.8.5 '@commitlint/lint@21.2.0': dependencies: @@ -12618,7 +12602,7 @@ snapshots: dependencies: '@simple-libs/child-process-utils': 2.0.0 '@simple-libs/stream-utils': 2.0.0 - semver: 7.7.3 + semver: 7.8.5 optionalDependencies: conventional-commits-parser: 7.1.2 @@ -13295,7 +13279,7 @@ snapshots: rollup-plugin-postcss: 4.0.2(postcss@8.5.26)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)) rollup-plugin-re: 1.0.7 sass: 1.102.0 - semver: 7.7.3 + semver: 7.8.5 shelljs: 0.10.0 shx: 0.4.0 ts-jest: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(jest-util@30.4.1)(jest@30.3.0(@types/node@24.12.4)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@24.12.4)(typescript@5.9.3)))(typescript@5.9.3) @@ -14429,8 +14413,8 @@ snapshots: debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 + minimatch: 9.0.9 + semver: 7.8.5 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -14443,8 +14427,8 @@ snapshots: '@typescript-eslint/types': 8.66.0 '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3 - minimatch: 10.2.2 - semver: 7.7.3 + minimatch: 10.2.6 + semver: 7.8.5 tinyglobby: 0.2.15 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -14954,7 +14938,7 @@ snapshots: content-type: 1.0.5 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.3 on-finished: 2.4.1 qs: 6.14.1 raw-body: 3.0.2 @@ -14973,11 +14957,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.3: - dependencies: - balanced-match: 4.0.4 - - brace-expansion@5.0.9: + brace-expansion@5.0.8: dependencies: balanced-match: 4.0.4 @@ -16075,7 +16055,7 @@ snapshots: eslint-fix-utils: 0.4.3(@types/estree@1.0.9)(eslint@9.39.3(jiti@2.6.1)) jsonc-eslint-parser: 2.4.1 package-json-validator: 1.6.0 - semver: 7.7.3 + semver: 7.8.5 sort-object-keys: 2.1.0 sort-package-json: 3.4.0 validate-npm-package-name: 7.0.2 @@ -16546,7 +16526,7 @@ snapshots: get-canvas-context@1.0.2: {} - get-east-asian-width@1.4.0: {} + get-east-asian-width@1.6.0: {} get-intrinsic@1.3.0: dependencies: @@ -16631,8 +16611,8 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 + minimatch: 9.0.9 + minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 @@ -16640,14 +16620,14 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 - minimatch: 10.2.2 - minipass: 7.1.2 + minimatch: 10.2.6 + minipass: 7.1.3 package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 + path-scurry: 2.0.2 glob@13.0.6: dependencies: - minimatch: 10.2.2 + minimatch: 10.2.6 minipass: 7.1.3 path-scurry: 2.0.2 @@ -16901,7 +16881,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 @@ -17175,7 +17155,7 @@ snapshots: '@babel/parser': 7.28.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.3 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -17640,7 +17620,7 @@ snapshots: jest-message-util: 30.3.0 jest-util: 30.3.0 pretty-format: 30.3.0 - semver: 7.7.3 + semver: 7.8.5 synckit: 0.11.11 transitivePeerDependencies: - supports-color @@ -17834,7 +17814,7 @@ snapshots: acorn: 8.15.0 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - semver: 7.7.3 + semver: 7.8.5 jsonc-parser@3.3.1: {} @@ -17990,7 +17970,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.8.5 make-dir@5.1.0: {} @@ -18147,13 +18127,9 @@ snapshots: mini-svg-data-uri@1.4.4: {} - minimatch@10.2.2: - dependencies: - brace-expansion: 5.0.3 - minimatch@10.2.6: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.8 minimatch@3.0.8: dependencies: @@ -18167,7 +18143,7 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minimatch@9.0.5: + minimatch@9.0.9: dependencies: brace-expansion: 2.0.2 @@ -18175,8 +18151,6 @@ snapshots: minipass@4.2.8: {} - minipass@7.1.2: {} - minipass@7.1.3: {} mitt@3.0.1: {} @@ -18322,7 +18296,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.7.3 + semver: 7.8.5 validate-npm-package-name: 7.0.2 npm-run-path@2.0.2: @@ -18475,7 +18449,7 @@ snapshots: package-json-validator@1.6.0: dependencies: npm-package-arg: 13.0.2 - semver: 7.7.3 + semver: 7.8.5 validate-npm-package-license: 3.0.4 validate-npm-package-name: 7.0.2 @@ -18527,12 +18501,7 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 - - path-scurry@2.0.0: - dependencies: - lru-cache: 11.2.2 - minipass: 7.1.2 + minipass: 7.1.3 path-scurry@2.0.2: dependencies: @@ -19252,7 +19221,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.3 unpipe: 1.0.0 rc@1.2.8: @@ -20140,7 +20109,7 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.1.2 string.prototype.matchall@4.0.12: From c02fc570bed5b99b6da656204900868ccd9bb40a Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:34:26 +0200 Subject: [PATCH 26/36] refactor(mcp): delete modules that do not earn their place 2462 lines removed, no surviving behaviour changed. tsx-generator hand-assembled React by string concatenation and emitted code that could not compile: the container pattern called useCallback without importing it, isCollapsible fell back to the string "false" spliced into source, and the input pattern compared an enum to a raw string. Its job now belongs to the client LLM, guided by docs/widget-patterns.md, which already ships as an MCP resource and already contained the same templates written correctly. This also removes one of three mutually disagreeing pattern detectors. mpk-analyzer had zero production callers and shelled out to `unzip` with an interpolated caller path, a binary that is not present on Windows. Its tests asserted expect(true).toBe(true) four times against fixtures in a gitignored directory that does not exist. protocol-logger did a synchronous appendFileSync on every request, only on the HTTP path, and its buildOutgoingLogEntry was never called. session.ts is superseded by the stateless transport. session-state.test.ts asserted that JavaScript object assignment works. code-generation.tools and property-update.tools are replaced by a single declarative set-widget-properties. They shared a .widget-definition.json snapshot on disk that could disagree with the XML it was supposed to describe. clearGuidelineCache had no callers. --- .../__tests__/tsx-generator.test.ts | 136 ---- .../src/generators/tsx-generator.ts | 603 ---------------- .../src/resources/guidelines.ts | 7 - .../src/server/protocol-logger.ts | 98 --- .../src/server/session.ts | 86 --- .../__tests__/code-generation.tools.test.ts | 93 --- .../src/tools/__tests__/session-state.test.ts | 36 - .../src/tools/code-generation.tools.ts | 675 ------------------ .../src/tools/property-update.tools.ts | 303 -------- .../utils/__tests__/mpk-analyzer.test.ts | 206 ------ .../src/tools/utils/mpk-analyzer.ts | 219 ------ 11 files changed, 2462 deletions(-) delete mode 100644 packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts delete mode 100644 packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts delete mode 100644 packages/pluggable-widgets-mcp/src/server/protocol-logger.ts delete mode 100644 packages/pluggable-widgets-mcp/src/server/session.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts delete mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts diff --git a/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts b/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts deleted file mode 100644 index bfe9b1244d..0000000000 --- a/packages/pluggable-widgets-mcp/src/generators/__tests__/tsx-generator.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { generateEditorPreview } from "@/generators/tsx-generator"; -import type { PropertyDefinition } from "@/generators/types"; - -/** - * generateEditorPreview produces a Studio Pro design-mode preview component. - * - * Design rule: if any property can produce a meaningful value in PreviewProps, - * use it — so `props` is always read and TS6133 never fires. - * - * Displayable types (produce readable primitives in PreviewProps): - * string, textTemplate, attribute → string → props.key || "[WidgetName]" - * integer, decimal → number → props.key != null ? String(props.key) : "[WidgetName]" - * boolean → boolean → props.key != null ? String(props.key) : "[WidgetName]" - * - * Non-displayable types (action, enumeration, datasource, …) do not produce - * a value worth showing in the preview, so they are skipped. If ALL properties - * are non-displayable (or there are no properties), _props is used as the - * conventional TypeScript signal for an intentionally unused parameter. - */ -describe("generateEditorPreview", () => { - // ── string-like props (existing behaviour, must not regress) ──────────── - - it("uses props and shows string value when a string property is present", () => { - const properties: PropertyDefinition[] = [{ key: "title", type: "string", caption: "Title", required: false }]; - const output = generateEditorPreview("Card", properties); - - expect(output).toContain("preview(props: CardPreviewProps)"); - expect(output).toContain('props.title || "[Card]"'); - expect(output).not.toContain("_props"); - }); - - it("uses props and shows textTemplate value when a textTemplate property is present", () => { - const properties: PropertyDefinition[] = [ - { key: "text", type: "textTemplate", caption: "Text", required: true } - ]; - const output = generateEditorPreview("Label", properties); - - expect(output).toContain("preview(props: LabelPreviewProps)"); - expect(output).toContain('props.text || "[Label]"'); - expect(output).not.toContain("_props"); - }); - - it("uses props and shows attribute value when an attribute property is present", () => { - const properties: PropertyDefinition[] = [ - { key: "value", type: "attribute", caption: "Value", required: true, attributeTypes: ["String"] } - ]; - const output = generateEditorPreview("Input", properties); - - expect(output).toContain("preview(props: InputPreviewProps)"); - expect(output).toContain('props.value || "[Input]"'); - expect(output).not.toContain("_props"); - }); - - // ── numeric props ──────────────────────────────────────────────────────── - - it("uses props and shows integer value when an integer property is present", () => { - const properties: PropertyDefinition[] = [ - { key: "initialValue", type: "integer", caption: "Initial value", required: false } - ]; - const output = generateEditorPreview("Counter", properties); - - expect(output).toContain("preview(props: CounterPreviewProps)"); - expect(output).toContain('props.initialValue != null ? String(props.initialValue) : "[Counter]"'); - expect(output).not.toContain("_props"); - }); - - it("uses props and shows decimal value when a decimal property is present", () => { - const properties: PropertyDefinition[] = [ - { key: "amount", type: "decimal", caption: "Amount", required: false } - ]; - const output = generateEditorPreview("Price", properties); - - expect(output).toContain("preview(props: PricePreviewProps)"); - expect(output).toContain('props.amount != null ? String(props.amount) : "[Price]"'); - expect(output).not.toContain("_props"); - }); - - // ── boolean props ──────────────────────────────────────────────────────── - - it("uses props and shows boolean value when a boolean property is present", () => { - const properties: PropertyDefinition[] = [ - { key: "enabled", type: "boolean", caption: "Enabled", required: false } - ]; - const output = generateEditorPreview("Toggle", properties); - - expect(output).toContain("preview(props: TogglePreviewProps)"); - expect(output).toContain('props.enabled != null ? String(props.enabled) : "[Toggle]"'); - expect(output).not.toContain("_props"); - }); - - // ── first displayable prop wins ────────────────────────────────────────── - - it("uses the first displayable prop when multiple types are mixed", () => { - const properties: PropertyDefinition[] = [ - { key: "onClick", type: "action", caption: "On click", required: false }, - { key: "count", type: "integer", caption: "Count", required: false }, - { key: "label", type: "string", caption: "Label", required: false } - ]; - const output = generateEditorPreview("Widget", properties); - - // action is skipped, integer is first displayable - expect(output).toContain('props.count != null ? String(props.count) : "[Widget]"'); - expect(output).not.toContain("_props"); - }); - - // ── non-displayable only → _props ──────────────────────────────────────── - - it("uses _props when all properties are non-displayable types (action, enumeration)", () => { - const properties: PropertyDefinition[] = [ - { key: "onClick", type: "action", caption: "On click", required: false }, - { - key: "size", - type: "enumeration", - caption: "Size", - required: false, - enumValues: [ - { key: "small", caption: "Small" }, - { key: "large", caption: "Large" } - ] - } - ]; - const output = generateEditorPreview("Button", properties); - - expect(output).toContain("preview(_props: ButtonPreviewProps)"); - expect(output).toContain('"[Button]"'); - expect(output).not.toContain("preview(props: ButtonPreviewProps)"); - }); - - it("uses _props when there are no properties at all", () => { - const output = generateEditorPreview("Empty", []); - - expect(output).toContain("preview(_props: EmptyPreviewProps)"); - expect(output).toContain('"[Empty]"'); - }); -}); diff --git a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts b/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts deleted file mode 100644 index 0230605cb5..0000000000 --- a/packages/pluggable-widgets-mcp/src/generators/tsx-generator.ts +++ /dev/null @@ -1,603 +0,0 @@ -/** - * TSX Generator for Mendix Widget Components. - * - * Transforms a WidgetDefinition into valid TSX component code. - * Uses pattern detection to select appropriate templates. - */ - -import type { PropertyDefinition } from "./types"; - -/** - * Widget patterns that determine TSX structure. - */ -export type WidgetPattern = "display" | "button" | "input" | "container" | "dataList"; - -/** - * Result of TSX generation. - */ -export interface TsxGeneratorResult { - /** Whether generation succeeded */ - success: boolean; - - /** Generated main component content (src/[Widget].tsx) */ - mainComponent?: string; - - /** Generated editor preview content (src/[Widget].editorPreview.tsx) */ - editorPreview?: string; - - /** Detected or specified widget pattern */ - pattern?: WidgetPattern; - - /** Error message (if failed) */ - error?: string; -} - -/** - * Detects the most appropriate widget pattern based on property types. - * - * Detection priority: - * 1. Has datasource + widgets with dataSource ref → dataList - * 2. Has widgets type → container - * 3. Has attribute type → input - * 4. Has only action + display props → button - * 5. Otherwise → display - */ -export function detectWidgetPattern(properties: PropertyDefinition[]): WidgetPattern { - const hasWidgets = properties.some(p => p.type === "widgets"); - const hasDatasource = properties.some(p => p.type === "datasource"); - const hasAttribute = properties.some(p => p.type === "attribute"); - const hasAction = properties.some(p => p.type === "action"); - - // Check for dataList pattern (datasource + widgets that reference it) - if (hasDatasource && hasWidgets) { - const widgetProps = properties.filter(p => p.type === "widgets"); - const hasDataSourceRef = widgetProps.some(p => p.dataSource); - if (hasDataSourceRef) { - return "dataList"; - } - } - - // Container pattern (has widgets but no datasource ref) - if (hasWidgets) { - return "container"; - } - - // Input pattern (has attribute for data binding) - if (hasAttribute) { - return "input"; - } - - // Button pattern (has action but no attribute binding) - if (hasAction && !hasAttribute) { - // Check if it's primarily action-focused (caption + action) - const displayProps = properties.filter( - p => p.type === "textTemplate" || p.type === "string" || p.type === "icon" - ); - if (displayProps.length <= 2) { - return "button"; - } - } - - // Default to display pattern - return "display"; -} - -/** - * Generates the required imports based on property types. - */ -function generateImports(widgetName: string, properties: PropertyDefinition[], pattern: WidgetPattern): string { - const imports: string[] = []; - - // Always need React with createElement (required for JSX in Mendix widgets) - const reactImports = new Set(["ReactElement", "createElement"]); - - // Check what hooks we need - const hasAction = properties.some(p => p.type === "action"); - const hasAttribute = properties.some(p => p.type === "attribute"); - const hasDatasource = properties.some(p => p.type === "datasource"); - const hasIntegerAttribute = properties.some( - p => p.type === "attribute" && p.attributeTypes?.some(t => ["Integer", "Long", "Decimal"].includes(t)) - ); - - // useCallback is needed for action handlers (all patterns) and attribute setValue - // callbacks (only input/dataList patterns — button/display/container don't call setValue) - if (hasAction || (hasAttribute && (pattern === "input" || pattern === "dataList"))) { - reactImports.add("useCallback"); - } - if (pattern === "container") { - reactImports.add("useState"); - } - - imports.push(`import { ${Array.from(reactImports).sort().join(", ")} } from "react";`); - - // Mendix imports — only import executeAction when the pattern actually uses it - let needsExecuteAction = false; - if (hasAction) { - if (pattern === "display" || pattern === "button") { - // These patterns use generateActionHandler for all action props - needsExecuteAction = true; - } else if (pattern === "input") { - // Input pattern only uses executeAction if there's a "change" action - needsExecuteAction = properties.some(p => p.type === "action" && p.key.toLowerCase().includes("change")); - } else if (pattern === "dataList") { - // DataList pattern uses executeAction for item click actions - needsExecuteAction = properties.some(p => p.type === "action" && p.key.toLowerCase().includes("item")); - } - // container pattern doesn't use executeAction (uses useState for toggle) - } - if (needsExecuteAction) { - imports.push('import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";'); - } - if (hasDatasource) { - imports.push('import { ValueStatus } from "mendix";'); - } - - // Big.js is only needed by the input pattern — it's the only pattern that calls - // attribute.setValue() with a Big value. Other patterns don't write back to attributes. - if (hasIntegerAttribute && pattern === "input") { - imports.push('import Big from "big.js";'); - } - - // Generated types import - imports.push(`import { ${widgetName}ContainerProps } from "../typings/${widgetName}Props";`); - - // Styles import - imports.push(`import "./ui/${widgetName}.scss";`); - - return imports.join("\n"); -} - -/** - * Generates value extraction code for a property. - */ -function generateValueExtraction(prop: PropertyDefinition): string { - const key = prop.key; - - switch (prop.type) { - case "textTemplate": - case "expression": - return `const ${key}Value = ${key}?.value ?? "";`; - case "integer": - case "decimal": - return `const ${key}Value = ${key} ?? ${prop.defaultValue ?? 0};`; - case "string": - return `const ${key}Value = ${key} ?? "${prop.defaultValue ?? ""}";`; - case "boolean": - return `const ${key}Value = ${key} ?? ${prop.defaultValue ?? false};`; - case "attribute": - return `const ${key}Value = ${key}?.value; - const ${key}ReadOnly = ${key}?.readOnly ?? false;`; - case "datasource": - return `const ${key}Items = ${key}?.items ?? []; - const ${key}Loading = ${key}?.status !== ValueStatus.Available;`; - default: - return ""; - } -} - -/** - * Generates action handler code. - */ -function generateActionHandler(prop: PropertyDefinition): string { - const key = prop.key; - const handlerName = `handle${key.charAt(0).toUpperCase() + key.slice(1)}`; - - return `const ${handlerName} = useCallback(() => { - executeAction(${key}); - }, [${key}]); - - const ${key}CanExecute = ${key}?.canExecute ?? false;`; -} - -/** - * Generates the Display pattern component. - */ -function generateDisplayPattern(widgetName: string, properties: PropertyDefinition[]): string { - const imports = generateImports(widgetName, properties, "display"); - - // Find relevant properties - const valueProps = properties.filter( - p => p.type === "textTemplate" || p.type === "string" || p.type === "expression" - ); - const actionProps = properties.filter(p => p.type === "action"); - const enumProps = properties.filter(p => p.type === "enumeration"); - - // Generate destructuring - const allProps = [...valueProps, ...actionProps, ...enumProps]; - const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; - - // Generate value extractions - const valueExtractions = valueProps.map(generateValueExtraction).filter(Boolean); - - // Generate action handlers - const actionHandlers = actionProps.map(generateActionHandler); - - // Determine main display value - const mainValueProp = valueProps[0]; - const mainValue = mainValueProp ? `${mainValueProp.key}Value` : '""'; - - // Determine if clickable - const clickAction = actionProps.find(p => p.key.toLowerCase().includes("click") || p.key === "onClick"); - const isClickable = clickAction ? `${clickAction.key}CanExecute` : "false"; - const clickHandler = clickAction - ? `handle${clickAction.key.charAt(0).toUpperCase() + clickAction.key.slice(1)}` - : "undefined"; - - return `${imports} - -export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { - const { ${propsToDestructure.join(", ")} } = props; - - ${valueExtractions.join("\n ")} - - ${actionHandlers.join("\n\n ")} - - const isClickable = ${isClickable}; - - return ( -
- {${mainValue}} -
- ); -} -`; -} - -/** - * Generates the Button pattern component. - */ -function generateButtonPattern(widgetName: string, properties: PropertyDefinition[]): string { - const imports = generateImports(widgetName, properties, "button"); - - // Find relevant properties - const captionProp = properties.find(p => p.key === "caption" || p.key === "label" || p.type === "textTemplate"); - const actionProps = properties.filter(p => p.type === "action"); - const enumProps = properties.filter(p => p.type === "enumeration"); - - // Generate destructuring - const allProps = [captionProp, ...actionProps, ...enumProps].filter(Boolean) as PropertyDefinition[]; - const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; - - // Generate action handlers - const actionHandlers = actionProps.map(generateActionHandler); - - // Main action (onClick or first action) - const mainAction = actionProps.find(p => p.key.toLowerCase().includes("click")) || actionProps[0]; - const mainHandler = mainAction - ? `handle${mainAction.key.charAt(0).toUpperCase() + mainAction.key.slice(1)}` - : "undefined"; - const isDisabled = mainAction ? `!${mainAction.key}CanExecute` : "true"; - - // Caption - const captionValue = captionProp ? `${captionProp.key}?.value ?? ""` : '""'; - - return `${imports} - -export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { - const { ${propsToDestructure.join(", ")} } = props; - - ${actionHandlers.join("\n\n ")} - - const isDisabled = ${isDisabled}; - - return ( - - ); -} -`; -} - -/** - * Generates the Input pattern component. - */ -function generateInputPattern(widgetName: string, properties: PropertyDefinition[]): string { - const imports = generateImports(widgetName, properties, "input"); - - // Find relevant properties - const attributeProps = properties.filter(p => p.type === "attribute"); - const mainAttribute = attributeProps[0]; - const actionProps = properties.filter(p => p.type === "action"); - - // Generate destructuring — only include props that are actually used in the rendered input. - // Text/string props are not rendered by the input element, so omit them to avoid unused-var errors. - const allProps = [...attributeProps, ...actionProps]; - const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; - - // Determine input type based on attribute type - const attrType = mainAttribute?.attributeTypes?.[0] ?? "String"; - let inputType = "text"; - if (attrType === "Integer" || attrType === "Long" || attrType === "Decimal") { - inputType = "number"; - } else if (attrType === "Boolean") { - inputType = "checkbox"; - } - - const mainKey = mainAttribute?.key ?? "value"; - - // Find change action - const changeAction = actionProps.find(p => p.key.toLowerCase().includes("change")); - const changeHandler = !!changeAction; - - // Determine if we need Big conversion for numeric attributes - const usesBig = inputType === "number"; - const valueExtraction = usesBig ? `${mainKey}?.value?.toNumber() ?? 0` : `${mainKey}?.value ?? ""`; - const valueConversion = usesBig ? `new Big(Number(event.target.value))` : `event.target.value`; - - return `${imports} - -export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { - const { ${propsToDestructure.join(", ")} } = props; - - const currentValue = ${valueExtraction}; - const isReadOnly = ${mainKey}?.readOnly ?? false; - - const handleInputChange = useCallback((event: React.ChangeEvent) => { - if (${mainKey}?.status === "available" && !${mainKey}.readOnly) { - ${mainKey}.setValue(${valueConversion}); - }${changeHandler ? `\n executeAction(${changeAction?.key});` : ""} - }, [${mainKey}${changeHandler ? `, ${changeAction?.key}` : ""}]); - - return ( - - ); -} -`; -} - -/** - * Generates the Container pattern component. - */ -function generateContainerPattern(widgetName: string, properties: PropertyDefinition[]): string { - const imports = generateImports(widgetName, properties, "container"); - - // Find relevant properties - const widgetProps = properties.filter(p => p.type === "widgets"); - const mainContent = widgetProps[0]; - const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); - const headerProp = textProps.find(p => p.key === "header" || p.key === "title") || textProps[0]; - const boolProps = properties.filter(p => p.type === "boolean"); - const collapsibleProp = boolProps.find(p => p.key === "collapsible"); - - // Generate destructuring - const allProps = [...widgetProps, ...textProps, ...boolProps]; - const propsToDestructure = ["class: className", "style", "tabIndex", ...allProps.map(p => p.key)]; - - const contentKey = mainContent?.key ?? "content"; - const headerValue = headerProp ? `${headerProp.key}?.value` : "undefined"; - const isCollapsible = collapsibleProp?.key ?? "false"; - - return `${imports} - -export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { - const { ${propsToDestructure.join(", ")} } = props; - - const [isOpen, setIsOpen] = useState(true); - - const handleToggle = useCallback(() => { - if (${isCollapsible}) { - setIsOpen(prev => !prev); - } - }, [${isCollapsible}]); - - const headerValue = ${headerValue}; - - return ( -
- {headerValue && ( -
- {headerValue} - {${isCollapsible} && } -
- )} - {isOpen &&
{${contentKey}}
} -
- ); -} -`; -} - -/** - * Generates the Data List pattern component. - */ -function generateDataListPattern(widgetName: string, properties: PropertyDefinition[]): string { - const imports = generateImports(widgetName, properties, "dataList"); - - // Find relevant properties - const datasourceProp = properties.find(p => p.type === "datasource"); - const widgetProps = properties.filter(p => p.type === "widgets"); - const contentProp = widgetProps.find(p => p.dataSource) || widgetProps[0]; - const textProps = properties.filter(p => p.type === "textTemplate" || p.type === "string"); - const emptyMessageProp = textProps.find(p => p.key.toLowerCase().includes("empty")); - const actionProps = properties.filter(p => p.type === "action"); - const itemClickAction = actionProps.find(p => p.key.toLowerCase().includes("item")); - - // Generate destructuring - const allProps = [datasourceProp, contentProp, emptyMessageProp, itemClickAction].filter( - Boolean - ) as PropertyDefinition[]; - const propsToDestructure = ["class: className", "style", ...allProps.map(p => p.key)]; - - // Generate action handlers - const actionHandlers = itemClickAction ? [generateActionHandler(itemClickAction)] : []; - - const dsKey = datasourceProp?.key ?? "dataSource"; - const contentKey = contentProp?.key ?? "content"; - const emptyMessage = emptyMessageProp ? `${emptyMessageProp.key}?.value ?? "No items"` : '"No items"'; - const itemHandler = itemClickAction - ? `handle${itemClickAction.key.charAt(0).toUpperCase() + itemClickAction.key.slice(1)}` - : "undefined"; - - return `${imports} -import { ObjectItem } from "mendix"; - -export default function ${widgetName}(props: ${widgetName}ContainerProps): ReactElement { - const { ${propsToDestructure.join(", ")} } = props; - - ${actionHandlers.join("\n\n ")} - - // Loading state - if (${dsKey}?.status !== ValueStatus.Available) { - return ( -
- Loading... -
- ); - } - - const items = ${dsKey}?.items ?? []; - - // Empty state - if (items.length === 0) { - return ( -
- {${emptyMessage}} -
- ); - } - - return ( -
- {items.map((item: ObjectItem) => ( -
- {${contentKey}?.get(item)} -
- ))} -
- ); -} -`; -} - -/** - * Generates a Studio Pro design-mode preview component (src/[Widget].editorPreview.tsx). - * - * In preview mode Mendix simplifies all property types to primitives. The generated - * stub picks the first "displayable" property and renders its value, so `props` is - * always read and TS6133 never fires. Displayable types: - * string / textTemplate / attribute → rendered as-is (falsy-safe with ||) - * integer / decimal / boolean → rendered via String() with explicit null check - * - * Non-displayable types (action, enumeration, datasource, …) are skipped. If no - * displayable property exists, _props is used as the TypeScript convention for an - * intentionally unused parameter. - */ -export function generateEditorPreview(widgetName: string, properties: PropertyDefinition[]): string { - const widgetClass = `widget-${widgetName.toLowerCase()}`; - - const STRING_TYPES = new Set(["string", "textTemplate", "attribute"]); - const NUMERIC_BOOL_TYPES = new Set(["integer", "decimal", "boolean"]); - - // Find the first property that can produce a meaningful display value in PreviewProps - const displayProp = properties.find(p => STRING_TYPES.has(p.type) || NUMERIC_BOOL_TYPES.has(p.type)); - - // Generate a type-appropriate expression so props is always read when displayProp exists - let previewContent: string; - if (!displayProp) { - previewContent = `"[${widgetName}]"`; - } else if (STRING_TYPES.has(displayProp.type)) { - previewContent = `props.${displayProp.key} || "[${widgetName}]"`; - } else { - // integer, decimal, boolean: explicit null check because 0 and false are falsy - previewContent = `props.${displayProp.key} != null ? String(props.${displayProp.key}) : "[${widgetName}]"`; - } - - // _props only when no property is displayed (TS6133: declared but never read) - const previewParam = displayProp ? "props" : "_props"; - - return `import { ReactElement, createElement } from "react"; -import { ${widgetName}PreviewProps } from "../typings/${widgetName}Props"; - -export function preview(${previewParam}: ${widgetName}PreviewProps): ReactElement { - return ( -
- {${previewContent}} -
- ); -} - -export function getPreviewCss(): string { - return ""; -} -`; -} - -/** - * Generates the complete widget TSX from a widget definition. - */ -export function generateWidgetTsx( - widgetName: string, - properties: PropertyDefinition[], - pattern?: WidgetPattern -): TsxGeneratorResult { - try { - // Detect pattern if not specified - const detectedPattern = pattern ?? detectWidgetPattern(properties); - - let mainComponent: string; - - switch (detectedPattern) { - case "display": - mainComponent = generateDisplayPattern(widgetName, properties); - break; - case "button": - mainComponent = generateButtonPattern(widgetName, properties); - break; - case "input": - mainComponent = generateInputPattern(widgetName, properties); - break; - case "container": - mainComponent = generateContainerPattern(widgetName, properties); - break; - case "dataList": - mainComponent = generateDataListPattern(widgetName, properties); - break; - default: - mainComponent = generateDisplayPattern(widgetName, properties); - } - - return { - success: true, - mainComponent, - editorPreview: generateEditorPreview(widgetName, properties), - pattern: detectedPattern - }; - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : String(error) - }; - } -} diff --git a/packages/pluggable-widgets-mcp/src/resources/guidelines.ts b/packages/pluggable-widgets-mcp/src/resources/guidelines.ts index 49e7cb7947..7b1163f965 100644 --- a/packages/pluggable-widgets-mcp/src/resources/guidelines.ts +++ b/packages/pluggable-widgets-mcp/src/resources/guidelines.ts @@ -69,10 +69,3 @@ export async function loadGuidelineContent(filename: string): Promise { throw new Error(`Failed to load guideline ${filename}: ${message}`); } } - -/** - * Clears the guideline cache. Useful for testing or hot-reloading. - */ -export function clearGuidelineCache(): void { - guidelineCache.clear(); -} diff --git a/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts b/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts deleted file mode 100644 index 1ea3550440..0000000000 --- a/packages/pluggable-widgets-mcp/src/server/protocol-logger.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { appendFileSync, mkdirSync } from "node:fs"; -import { join } from "node:path"; - -export interface ProtocolLogEntry { - timestamp: string; - sessionId: string; - direction: "incoming" | "outgoing"; - method?: string; - id?: string | number | null; - params?: unknown; - result?: unknown; - error?: unknown; - duration?: number; - clientCapabilities?: unknown; - clientInfo?: unknown; - protocolVersion?: string; -} - -const LOG_DIR = join(process.cwd(), "mcp-session-logs"); -let logDirCreated = false; - -function ensureLogDir(): void { - if (!logDirCreated) { - mkdirSync(LOG_DIR, { recursive: true }); - logDirCreated = true; - } -} - -/** - * Appends a JSON-lines log entry for the given session. - * Writes to mcp-session-logs/.jsonl. - * Uses synchronous I/O to keep the MCP stdio channel clean. - */ -export function logProtocolMessage(sessionId: string, entry: ProtocolLogEntry): void { - try { - ensureLogDir(); - const line = JSON.stringify(entry) + "\n"; - appendFileSync(join(LOG_DIR, `${sessionId}.jsonl`), line, "utf-8"); - } catch { - // Never crash the server over a logging failure - } -} - -/** - * Extracts a structured log entry from an incoming JSON-RPC request body. - * Special-cases initialize requests to surface ClientCapabilities at the top level. - */ -export function buildIncomingLogEntry(sessionId: string, body: Record): ProtocolLogEntry { - const entry: ProtocolLogEntry = { - timestamp: new Date().toISOString(), - sessionId, - direction: "incoming", - method: typeof body.method === "string" ? body.method : undefined, - id: (body.id as string | number | null | undefined) ?? undefined - }; - - const method = entry.method; - - if (method === "initialize") { - const params = body.params as Record | undefined; - if (params) { - entry.protocolVersion = params.protocolVersion as string | undefined; - entry.clientInfo = params.clientInfo; - entry.clientCapabilities = params.capabilities; - } - } else if (method === "tools/call") { - const params = body.params as Record | undefined; - entry.params = params ? { name: params.name, arguments: params.arguments } : undefined; - } else { - // For other methods log params as-is (but omit large payloads) - entry.params = body.params; - } - - return entry; -} - -/** - * Builds a log entry for an outgoing response. - */ -export function buildOutgoingLogEntry( - sessionId: string, - method: string | undefined, - id: string | number | null | undefined, - result: unknown, - error: unknown, - duration: number -): ProtocolLogEntry { - return { - timestamp: new Date().toISOString(), - sessionId, - direction: "outgoing", - method, - id, - result: error ? undefined : result, - error, - duration - }; -} diff --git a/packages/pluggable-widgets-mcp/src/server/session.ts b/packages/pluggable-widgets-mcp/src/server/session.ts deleted file mode 100644 index b382f045b8..0000000000 --- a/packages/pluggable-widgets-mcp/src/server/session.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; -import { randomUUID } from "node:crypto"; - -export interface Session { - transport: StreamableHTTPServerTransport; - createdAt: Date; - toolCallCount: number; -} - -/** - * Manages MCP sessions and their associated transports. - */ -export class SessionManager { - private sessions = new Map(); - - /** - * Creates a new transport with session lifecycle callbacks. - * The transport is added to sessions when initialized via the callback. - */ - createTransport(): StreamableHTTPServerTransport { - const transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => randomUUID(), - onsessioninitialized: sessionId => { - const createdAt = new Date(); - this.sessions.set(sessionId, { - transport, - createdAt, - toolCallCount: 0 - }); - console.error(`[MCP] Session initialized: ${sessionId} at=${createdAt.toISOString()}`); - }, - onsessionclosed: sessionId => { - const session = this.sessions.get(sessionId); - if (session) { - const durationMs = Date.now() - session.createdAt.getTime(); - console.error( - `[MCP] Session closed: ${sessionId} duration=${durationMs}ms toolCalls=${session.toolCallCount}` - ); - } - this.sessions.delete(sessionId); - } - }); - - return transport; - } - - /** - * Gets an existing session's transport by session ID. - */ - getTransport(sessionId: string): StreamableHTTPServerTransport | undefined { - return this.sessions.get(sessionId)?.transport; - } - - /** - * Checks if a session exists. - */ - hasSession(sessionId: string): boolean { - return this.sessions.has(sessionId); - } - - /** - * Gets the count of active sessions. - */ - get sessionCount(): number { - return this.sessions.size; - } - - /** - * Closes all sessions gracefully. - */ - async closeAll(): Promise { - const closePromises = Array.from(this.sessions.entries()).map(async ([sessionId, session]) => { - try { - console.log(`[MCP] Closing session: ${sessionId}`); - await session.transport.close(); - } catch (error) { - console.error(`[MCP] Error closing session ${sessionId}:`, error); - } - }); - - await Promise.all(closePromises); - this.sessions.clear(); - } -} - -export const sessionManager = new SessionManager(); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts deleted file mode 100644 index 13d1de3a54..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/code-generation.tools.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { detectTemplateMismatch } from "@/tools/code-generation.tools"; -import type { PropertyDefinition } from "@/generators/types"; - -function prop(key: string, type: PropertyDefinition["type"]): PropertyDefinition { - return { key, type, caption: key }; -} - -describe("detectTemplateMismatch", () => { - it("warns: button pattern + no action → disabled", () => { - const result = detectTemplateMismatch("button", [prop("caption", "textTemplate")]); - expect(result).not.toBeNull(); - expect(result).toContain("permanently disabled"); - }); - - it("warns: button pattern + no caption → empty text", () => { - const result = detectTemplateMismatch("button", [prop("onClick", "action")]); - expect(result).not.toBeNull(); - expect(result).toContain("empty text"); - }); - - it("null: button pattern + action + textTemplate → OK", () => { - const result = detectTemplateMismatch("button", [prop("onClick", "action"), prop("caption", "textTemplate")]); - expect(result).toBeNull(); - }); - - it("null: button pattern + action + string → OK", () => { - const result = detectTemplateMismatch("button", [prop("onClick", "action"), prop("label", "string")]); - expect(result).toBeNull(); - }); - - it("warns: display pattern + only integer → read-only", () => { - const result = detectTemplateMismatch("display", [prop("count", "integer")]); - expect(result).not.toBeNull(); - expect(result).toContain("read-only"); - }); - - it("null: display pattern + textTemplate → OK", () => { - const result = detectTemplateMismatch("display", [prop("value", "textTemplate")]); - expect(result).toBeNull(); - }); - - it("null: display pattern + expression → OK", () => { - const result = detectTemplateMismatch("display", [prop("computed", "expression")]); - expect(result).toBeNull(); - }); - - it("warns: input pattern + no attribute", () => { - const result = detectTemplateMismatch("input", [prop("onChange", "action")]); - expect(result).not.toBeNull(); - expect(result).toContain("attribute"); - }); - - it("null: input pattern + attribute → OK", () => { - const result = detectTemplateMismatch("input", [prop("value", "attribute")]); - expect(result).toBeNull(); - }); - - it("warns: container pattern + no widgets", () => { - const result = detectTemplateMismatch("container", [prop("title", "textTemplate")]); - expect(result).not.toBeNull(); - expect(result).toContain("widgets"); - }); - - it("null: container pattern + widgets → OK", () => { - const result = detectTemplateMismatch("container", [prop("content", "widgets")]); - expect(result).toBeNull(); - }); - - it("warns: dataList pattern + missing datasource", () => { - const result = detectTemplateMismatch("dataList", [prop("content", "widgets")]); - expect(result).not.toBeNull(); - expect(result).toContain("datasource"); - }); - - it("warns: dataList pattern + missing widgets", () => { - const result = detectTemplateMismatch("dataList", [prop("items", "datasource")]); - expect(result).not.toBeNull(); - expect(result).toContain("widgets"); - }); - - it("warns: dataList pattern + both missing", () => { - const result = detectTemplateMismatch("dataList", [prop("title", "string")]); - expect(result).not.toBeNull(); - expect(result).toContain("datasource"); - expect(result).toContain("widgets"); - }); - - it("null: dataList pattern + datasource + widgets → OK", () => { - const result = detectTemplateMismatch("dataList", [prop("items", "datasource"), prop("content", "widgets")]); - expect(result).toBeNull(); - }); -}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts deleted file mode 100644 index 90694fcc3c..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/session-state.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; - -describe("createSessionState", () => { - afterEach(() => { - // resetModules required because vi.doMock re-registers per test - vi.resetModules(); - }); - - it("returns undefined projectDir when MENDIX_PROJECT_DIR is not set", async () => { - vi.doMock("@/config", () => ({ - MENDIX_PROJECT_DIR: undefined - })); - const { createSessionState } = await import("@/tools/session-state"); - const state = createSessionState(); - expect(state.projectDir).toBeUndefined(); - }); - - it("returns resolved projectDir when MENDIX_PROJECT_DIR is set", async () => { - vi.doMock("@/config", () => ({ - MENDIX_PROJECT_DIR: "/resolved/path" - })); - const { createSessionState } = await import("@/tools/session-state"); - const state = createSessionState(); - expect(state.projectDir).toBe("/resolved/path"); - }); - - it("returns mutable state", async () => { - vi.doMock("@/config", () => ({ - MENDIX_PROJECT_DIR: undefined - })); - const { createSessionState } = await import("@/tools/session-state"); - const state = createSessionState(); - state.projectDir = "/new/path"; - expect(state.projectDir).toBe("/new/path"); - }); -}); diff --git a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts b/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts deleted file mode 100644 index 47a4757615..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/code-generation.tools.ts +++ /dev/null @@ -1,675 +0,0 @@ -/** - * Code Generation Tools for Mendix Pluggable Widgets. - * - * Provides the `generate-widget-code` tool that transforms widget descriptions - * and property definitions into working XML and TSX code. - */ - -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { mkdir, readdir, readFile, stat, unlink, writeFile } from "node:fs/promises"; -import { basename, dirname, join } from "node:path"; -import { z } from "zod"; -import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; -import { detectWidgetPattern, generateWidgetTsx, type WidgetPattern } from "@/generators/tsx-generator"; -import type { PropertyDefinition, WidgetDefinition } from "@/generators/types"; -import { validateFilePath } from "@/security"; -import type { ToolResponse } from "@/tools/types"; -import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; - -// ============================================================================= -// Schemas -// ============================================================================= - -/** - * Schema for enumeration values. - */ -const enumValueSchema = z.object({ - key: z.string().min(1).describe("Unique identifier for this enum value"), - caption: z.string().min(1).describe("Display caption shown in Studio Pro") -}); - -/** - * Schema for property definitions. - * Matches the PropertyDefinition type from generators/types.ts - */ -const propertyDefinitionSchema = z.object({ - key: z - .string() - .min(1) - .regex(/^[a-z][a-zA-Z0-9]*$/, "Must be camelCase (e.g., 'myProperty')") - .describe("Property key in camelCase"), - type: z - .enum([ - "string", - "boolean", - "integer", - "decimal", - "textTemplate", - "expression", - "action", - "attribute", - "datasource", - "association", - "selection", - "enumeration", - "icon", - "image", - "file", - "widgets", - "object" - ]) - .describe("Mendix property type"), - caption: z.string().min(1).describe("Display caption shown in Studio Pro"), - description: z.string().optional().describe("Help text shown in Studio Pro"), - required: z.boolean().optional().describe("Whether this property is required"), - defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for this property"), - enumValues: z.array(enumValueSchema).optional().describe("Allowed values for enumeration type"), - attributeTypes: z - .array( - z.enum([ - "String", - "Integer", - "Long", - "Decimal", - "Boolean", - "DateTime", - "Enum", - "HashString", - "Binary", - "AutoNumber" - ]) - ) - .optional() - .describe("Allowed attribute types for attribute property"), - isList: z.boolean().optional().describe("Whether datasource returns a list"), - dataSource: z.string().optional().describe("Reference to datasource property key (for widgets type)"), - returnType: z - .enum(["String", "Integer", "Decimal", "Boolean", "DateTime"]) - .optional() - .describe("Return type for expression property") -}); - -/** - * Schema for property group definitions. - */ -const propertyGroupSchema = z.object({ - caption: z.string().min(1).describe("Group caption displayed in Studio Pro"), - properties: z.array(z.string().min(1)).min(1).describe("Property keys in this group") -}); - -/** - * Schema for the generate-widget-code tool input. - */ -const generateWidgetCodeSchema = z.object({ - widgetPath: z.string().min(1).describe("Absolute path to the scaffolded widget directory"), - description: z.string().min(1).describe("Description of what the widget should do"), - properties: z - .preprocess(v => { - // MCP clients (e.g. Maia) sometimes send JSON arrays as a stringified string. - // Parse it transparently so validation still runs on the actual array contents. - if (typeof v === "string") { - try { - return JSON.parse(v); - } catch { - return v; // let Zod report the type error - } - } - return v; - }, z.array(propertyDefinitionSchema).optional()) - .describe("Array of property definitions. If not provided, returns suggestions."), - widgetPattern: z - .enum(["display", "button", "input", "container", "dataList"]) - .optional() - .describe("Optional hint for TSX generation pattern"), - systemProperties: z - .array(z.enum(["Name", "TabIndex", "Visibility"])) - .optional() - .describe( - 'System properties to include. Defaults to ["Name", "TabIndex", "Visibility"]. Pass empty array to include none.' - ), - propertyGroups: z - .array(propertyGroupSchema) - .optional() - .describe( - "Optional property grouping. If not provided, non-action properties go in 'General' and action properties go in 'Events' automatically." - ) -}); - -type GenerateWidgetCodeInput = z.infer; - -// ============================================================================= -// Helper Functions -// ============================================================================= - -/** - * Extracts widget name from the widget directory. - * Reads from package.json widgetName (authoritative source), falls back to basename. - */ -async function extractWidgetName(widgetPath: string): Promise { - // Try reading from package.json (authoritative source) - try { - const pkgPath = join(widgetPath, "package.json"); - const pkgJson = JSON.parse(await readFile(pkgPath, "utf-8")); - if (pkgJson.widgetName && /^[A-Z][a-zA-Z0-9]*$/.test(pkgJson.widgetName)) { - return pkgJson.widgetName; - } - } catch { - // Fall through to basename approach - } - // Fallback: derive from directory name - const base = basename(widgetPath); - return base.charAt(0).toUpperCase() + base.slice(1); -} - -/** - * Cleans up stale scaffold files that don't match the current widget name. - * The generator creates files named after the widget (e.g., CounterTwo.xml, CounterTwo.tsx). - * When generate-widget-code writes new files, old scaffold artifacts must be removed - * to prevent build conflicts (wrong typings, duplicate XML definitions). - */ -async function cleanupScaffoldFiles(widgetPath: string, widgetName: string): Promise { - const srcDir = join(widgetPath, "src"); - const typingsDir = join(widgetPath, "typings"); - - let srcFiles: string[]; - try { - srcFiles = await readdir(srcDir); - } catch { - return; // src dir doesn't exist yet, nothing to clean - } - - // 1. Remove old .xml files in src/ (except package.xml and the one we're about to write) - for (const file of srcFiles) { - if (file === "package.xml" || file === `${widgetName}.xml`) continue; - if (file.endsWith(".xml")) { - await unlink(join(srcDir, file)); - console.error(`[code-generation] Cleaned up stale file: src/${file}`); - } - } - - // 2. Remove old .tsx, .editorConfig.ts, .editorPreview.tsx that don't match our widget name - for (const file of srcFiles) { - // Only clean top-level src/ files, not files in subdirectories - const isOldTsx = - file.endsWith(".tsx") && file !== `${widgetName}.tsx` && file !== `${widgetName}.editorPreview.tsx`; - const isOldEditorConfig = file.endsWith(".editorConfig.ts") && file !== `${widgetName}.editorConfig.ts`; - const isOldEditorPreview = file.endsWith(".editorPreview.tsx") && file !== `${widgetName}.editorPreview.tsx`; - if (isOldTsx || isOldEditorConfig || isOldEditorPreview) { - await unlink(join(srcDir, file)); - console.error(`[code-generation] Cleaned up stale file: src/${file}`); - } - } - - // 3. Remove old .css/.scss files in src/ui/ that don't match - const uiDir = join(srcDir, "ui"); - try { - const uiFiles = await readdir(uiDir); - for (const file of uiFiles) { - if ( - (file.endsWith(".css") || file.endsWith(".scss")) && - file !== `${widgetName}.css` && - file !== `${widgetName}.scss` - ) { - await unlink(join(uiDir, file)); - console.error(`[code-generation] Cleaned up stale file: src/ui/${file}`); - } - } - } catch { - /* ui dir might not exist yet */ - } - - // 4. Clear old typings that don't match - try { - const typingsFiles = await readdir(typingsDir); - for (const file of typingsFiles) { - if (file.endsWith(".d.ts") && file !== `${widgetName}Props.d.ts`) { - await unlink(join(typingsDir, file)); - console.error(`[code-generation] Cleaned up stale file: typings/${file}`); - } - } - } catch { - /* typings dir might not exist yet */ - } - - // 5. Regenerate package.xml with correct widget name + version - const packageXmlPath = join(srcDir, "package.xml"); - const widgetNameLower = widgetName.toLowerCase(); - let version = "1.0.0"; - let packagePath = "mendix"; - try { - const pkgJson = JSON.parse(await readFile(join(widgetPath, "package.json"), "utf-8")); - if (pkgJson.version) version = pkgJson.version; - if (pkgJson.packagePath) packagePath = pkgJson.packagePath; - } catch { - /* use default */ - } - const filePath = `${packagePath.replace(/\./g, "/")}/${widgetNameLower}`; - const packageXml = [ - '', - '', - ` `, - " ", - ` `, - " ", - " ", - ` `, - " ", - " ", - "" - ].join("\n"); - await writeFile(packageXmlPath, packageXml, "utf-8"); - console.error(`[code-generation] Regenerated package.xml for ${widgetName}`); -} - -/** - * Generates property suggestions based on widget description. - */ -function generatePropertySuggestions(description: string): string { - const descLower = description.toLowerCase(); - - // Common patterns to suggest - const suggestions: Array<{ - key: string; - type: string; - caption: string; - purpose: string; - }> = []; - - // Counter-like widgets - if (descLower.includes("counter") || descLower.includes("count") || descLower.includes("increment")) { - suggestions.push( - { - key: "value", - type: "attribute", - caption: "Value", - purpose: "Current counter value (bind to Integer attribute)" - }, - { key: "step", type: "integer", caption: "Step", purpose: "Amount to increment/decrement (default: 1)" }, - { key: "minValue", type: "integer", caption: "Minimum", purpose: "Lower bound (optional)" }, - { key: "maxValue", type: "integer", caption: "Maximum", purpose: "Upper bound (optional)" }, - { key: "onIncrement", type: "action", caption: "On Increment", purpose: "Action when value increases" }, - { key: "onDecrement", type: "action", caption: "On Decrement", purpose: "Action when value decreases" } - ); - } - - // Display/badge-like widgets - if ( - descLower.includes("display") || - descLower.includes("show") || - descLower.includes("badge") || - descLower.includes("label") - ) { - suggestions.push( - { key: "value", type: "textTemplate", caption: "Value", purpose: "Text to display" }, - { key: "type", type: "enumeration", caption: "Style", purpose: "Visual style variant" }, - { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } - ); - } - - // Button-like widgets - if (descLower.includes("button") || descLower.includes("click") || descLower.includes("trigger")) { - suggestions.push( - { key: "caption", type: "textTemplate", caption: "Caption", purpose: "Button text" }, - { key: "icon", type: "icon", caption: "Icon", purpose: "Button icon (optional)" }, - { key: "buttonStyle", type: "enumeration", caption: "Style", purpose: "Button appearance variant" }, - { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } - ); - } - - // Input-like widgets - if ( - descLower.includes("input") || - descLower.includes("edit") || - descLower.includes("enter") || - descLower.includes("form") - ) { - suggestions.push( - { key: "value", type: "attribute", caption: "Value", purpose: "Bound attribute for data entry" }, - { key: "placeholder", type: "textTemplate", caption: "Placeholder", purpose: "Hint text when empty" }, - { key: "onChange", type: "action", caption: "On Change", purpose: "Action when value changes" }, - { key: "onEnter", type: "action", caption: "On Enter", purpose: "Action when Enter key pressed" } - ); - } - - // List-like widgets - if ( - descLower.includes("list") || - descLower.includes("items") || - descLower.includes("collection") || - descLower.includes("data") - ) { - suggestions.push( - { key: "dataSource", type: "datasource", caption: "Data Source", purpose: "Source of items to display" }, - { key: "content", type: "widgets", caption: "Content", purpose: "Template for each item" }, - { key: "emptyMessage", type: "textTemplate", caption: "Empty Message", purpose: "Text when no items" }, - { key: "onItemClick", type: "action", caption: "On Item Click", purpose: "Action when item clicked" } - ); - } - - // Container-like widgets - if ( - descLower.includes("container") || - descLower.includes("card") || - descLower.includes("panel") || - descLower.includes("section") - ) { - suggestions.push( - { key: "content", type: "widgets", caption: "Content", purpose: "Child widgets" }, - { key: "header", type: "textTemplate", caption: "Header", purpose: "Container title" }, - { key: "collapsible", type: "boolean", caption: "Collapsible", purpose: "Allow expand/collapse" } - ); - } - - // Default suggestions if nothing matched - if (suggestions.length === 0) { - suggestions.push( - { key: "value", type: "textTemplate", caption: "Value", purpose: "Main display value" }, - { key: "onClick", type: "action", caption: "On Click", purpose: "Action when clicked" } - ); - } - - // Detect pattern from suggestions - let suggestedPattern: WidgetPattern = "display"; - const types = suggestions.map(s => s.type); - if (types.includes("datasource") && types.includes("widgets")) { - suggestedPattern = "dataList"; - } else if (types.includes("widgets")) { - suggestedPattern = "container"; - } else if (types.includes("attribute")) { - suggestedPattern = "input"; - } else if (suggestions.length <= 4 && types.includes("action")) { - suggestedPattern = "button"; - } - - // Build markdown table - const table = [ - "| Property | Type | Caption | Purpose |", - "|----------|------|---------|---------|", - ...suggestions.map(s => `| ${s.key} | ${s.type} | ${s.caption} | ${s.purpose} |`) - ].join("\n"); - - return `📋 Widget requirements analysis needed - -Based on your description "${description}", suggested properties: - -${table} - -Suggested pattern: **${suggestedPattern}** (${getPatternDescription(suggestedPattern)}) - -Please call generate-widget-code again with the properties array to generate the widget code. - -Example: -\`\`\`json -{ - "widgetPath": "", - "description": "${description}", - "properties": [ - { "key": "value", "type": "textTemplate", "caption": "Value" }, - { "key": "onClick", "type": "action", "caption": "On Click" } - ] -} -\`\`\``; -} - -/** - * Returns a human-readable description of a widget pattern. - */ -function getPatternDescription(pattern: WidgetPattern): string { - switch (pattern) { - case "display": - return "read-only data display"; - case "button": - return "action trigger with click handler"; - case "input": - return "data entry with attribute binding"; - case "container": - return "holds child widgets"; - case "dataList": - return "renders items from datasource"; - default: - return "general purpose"; - } -} - -/** - * Detects a mismatch between the selected widget pattern and the provided properties. - * - * Returns a warning string when the pattern's key property types are absent, - * or null when the properties satisfy the pattern's requirements. - */ -export function detectTemplateMismatch(pattern: WidgetPattern, properties: PropertyDefinition[]): string | null { - const types = properties.map(p => p.type); - - switch (pattern) { - case "button": { - const warnings: string[] = []; - if (!types.includes("action")) { - warnings.push("button will be permanently disabled (no action property)"); - } - if (!types.includes("textTemplate") && !types.includes("string")) { - warnings.push("button will render empty text (no textTemplate or string property for caption)"); - } - return warnings.length > 0 ? warnings.join("; ") : null; - } - case "input": - if (!types.includes("attribute")) { - return "no attribute property for data binding — input pattern needs an attribute to read/write values"; - } - return null; - case "display": - if (!types.includes("textTemplate") && !types.includes("expression") && !types.includes("string")) { - return "read-only display component with no dynamic text source — only primitive types found; customize the generated code or add a textTemplate/expression property"; - } - return null; - case "container": - if (!types.includes("widgets")) { - return "no child content slot — container pattern needs a widgets property for nested widget content"; - } - return null; - case "dataList": { - const missing: string[] = []; - if (!types.includes("datasource")) missing.push("datasource"); - if (!types.includes("widgets")) missing.push("widgets"); - if (missing.length > 0) { - return `dataList pattern is missing required properties: ${missing.join(", ")}`; - } - return null; - } - default: - return null; - } -} - -// ============================================================================= -// Tool Handler -// ============================================================================= - -async function handleGenerateWidgetCode(args: GenerateWidgetCodeInput): Promise { - const { widgetPath, description, properties, widgetPattern } = args; - - try { - // Verify widget directory exists - const pathStats = await stat(widgetPath); - if (!pathStats.isDirectory()) { - return createErrorResponse(`Widget path is not a directory: ${widgetPath}`); - } - - // If no properties provided, return suggestions - if (!properties || properties.length === 0) { - console.error(`[code-generation] No properties provided, returning suggestions`); - return createToolResponse(generatePropertySuggestions(description)); - } - - // Extract widget name from path - const widgetName = await extractWidgetName(widgetPath); - - console.error(`[code-generation] Generating code for ${widgetName} with ${properties.length} properties`); - - // Build widget definition for XML generator - const widgetDefinition: WidgetDefinition = { - name: widgetName, - description, - properties: properties as PropertyDefinition[], - systemProperties: args.systemProperties ?? ["Name", "TabIndex", "Visibility"], - propertyGroups: args.propertyGroups - }; - - // Validate widget definition - const validationErrors = validateWidgetDefinition(widgetDefinition); - if (validationErrors.length > 0) { - return createErrorResponse( - [ - "❌ Widget definition validation failed:", - "", - ...validationErrors.map(e => ` • ${e}`), - "", - "Please fix the above issues and try again." - ].join("\n") - ); - } - - // Clean up stale scaffold files before writing new ones - await cleanupScaffoldFiles(widgetPath, widgetName); - - // Generate XML - console.error(`[code-generation] Generating XML...`); - const xmlResult = generateWidgetXml(widgetDefinition); - if (!xmlResult.success || !xmlResult.xml) { - return createErrorResponse(`XML generation failed: ${xmlResult.error}`); - } - - // Detect or use provided pattern - const pattern = widgetPattern ?? detectWidgetPattern(properties as PropertyDefinition[]); - console.error(`[code-generation] Using pattern: ${pattern}`); - - // Generate TSX - console.error(`[code-generation] Generating TSX...`); - const tsxResult = generateWidgetTsx(widgetName, properties as PropertyDefinition[], pattern); - if (!tsxResult.success || !tsxResult.mainComponent) { - return createErrorResponse(`TSX generation failed: ${tsxResult.error}`); - } - - // Prepare files to write - const filesToWrite = [ - { path: `src/${widgetName}.xml`, content: xmlResult.xml }, - { path: `src/${widgetName}.tsx`, content: tsxResult.mainComponent }, - { path: `src/${widgetName}.editorPreview.tsx`, content: tsxResult.editorPreview! }, - { path: `src/ui/${widgetName}.scss`, content: `.widget-${widgetName.toLowerCase()} {\n}\n` }, - { path: `src/.widget-definition.json`, content: JSON.stringify(widgetDefinition, null, 2) } - ]; - - // Validate and write files - const writtenFiles: string[] = []; - for (const file of filesToWrite) { - try { - validateFilePath(widgetPath, file.path, true); - const fullPath = join(widgetPath, file.path); - - // Ensure parent directory exists - const parentDir = dirname(fullPath); - await mkdir(parentDir, { recursive: true }); - - // Write file - await writeFile(fullPath, file.content, "utf-8"); - writtenFiles.push(file.path); - console.error(`[code-generation] Wrote: ${fullPath}`); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - return createErrorResponse(`Failed to write ${file.path}: ${message}`); - } - } - - // Build success response - const propSummary = properties.map(p => p.key).join(", "); - const mismatch = detectTemplateMismatch(pattern, properties as PropertyDefinition[]); - - const lines = [ - `✅ Widget code generated successfully!`, - "", - `📁 Files written:`, - ` • src/${widgetName}.xml - Widget definition with ${properties.length} properties (${propSummary})`, - ` • src/${widgetName}.tsx - Component using ${pattern} pattern`, - ` • src/ui/${widgetName}.scss - Empty SCSS placeholder`, - ` • src/.widget-definition.json - Widget definition snapshot (used by update-widget-properties)` - ]; - - if (mismatch) { - lines.push("", `⚠️ Template notice: ${mismatch}`); - lines.push( - "", - `🔨 Next steps:`, - ` 1. Review and customize the generated code (use write-widget-file to update src/${widgetName}.tsx)`, - ` 2. Run build-widget to compile and validate`, - ` 3. Update src/${widgetName}.editorPreview.tsx for Studio Pro design mode preview`, - ` 4. Test in Mendix Studio Pro` - ); - } else { - lines.push( - "", - `🔨 Next steps:`, - ` 1. Run build-widget to compile and validate`, - ` 2. Review and customize generated code`, - ` 3. Update src/${widgetName}.editorPreview.tsx for Studio Pro design mode preview`, - ` 4. Test in Mendix Studio Pro` - ); - } - - return createToolResponse(lines.join("\n")); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - console.error(`[code-generation] Error: ${message}`); - return createErrorResponse(`Widget code generation failed: ${message}`); - } -} - -// ============================================================================= -// Tool Registration -// ============================================================================= - -const GENERATE_WIDGET_CODE_DESCRIPTION = `Generates XML properties and TSX component code for a Mendix pluggable widget. - -**Usage:** - -1. **With properties (generates code):** - Provide widgetPath, description, and properties array to generate XML + TSX files. - -2. **Without properties (gets suggestions):** - Provide only widgetPath and description to receive suggested properties based on your description. - -**Supported property types:** -- Basic: string, boolean, integer, decimal -- Dynamic: textTemplate, expression -- Interactive: action, attribute (for data binding) -- Complex: datasource, widgets (for containers/lists), enumeration - -**Pattern detection:** -The tool automatically detects the appropriate widget pattern (display, button, input, container, dataList) based on property types, or you can specify it explicitly. - -**Example - Counter widget:** -\`\`\`json -{ - "widgetPath": "/path/to/CounterWidget", - "description": "A counter that increments and decrements", - "properties": [ - { "key": "value", "type": "attribute", "caption": "Value", "attributeTypes": ["Integer"] }, - { "key": "onIncrement", "type": "action", "caption": "On Increment" } - ] -} -\`\`\``; - -/** - * Registers code generation tools for creating widget XML and TSX. - */ -export function registerCodeGenerationTools(server: McpServer): void { - server.registerTool( - "generate-widget-code", - { - title: "Generate Widget Code", - description: GENERATE_WIDGET_CODE_DESCRIPTION, - inputSchema: generateWidgetCodeSchema - }, - handleGenerateWidgetCode - ); - - console.error("[code-generation] Registered 1 tool"); -} diff --git a/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts b/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts deleted file mode 100644 index 79e7704318..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/property-update.tools.ts +++ /dev/null @@ -1,303 +0,0 @@ -/** - * Property Update Tool for Mendix Pluggable Widgets. - * - * Provides the `update-widget-properties` tool that incrementally modifies - * widget property definitions without full regeneration. - */ - -import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { readFile, writeFile } from "node:fs/promises"; -import { join } from "node:path"; -import { z } from "zod"; -import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; -import type { PropertyDefinition, PropertyGroup, SystemProperty, WidgetDefinition } from "@/generators/types"; -import { validateFilePath } from "@/security"; -import type { ToolResponse } from "@/tools/types"; -import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; - -// ============================================================================= -// Schemas -// ============================================================================= - -/** - * Schema for a single property definition (reuse same structure as code-generation.tools.ts). - */ -const propertyDefinitionSchema = z.object({ - key: z - .string() - .min(1) - .regex(/^[a-z][a-zA-Z0-9]*$/, "Must be camelCase (e.g., 'myProperty')") - .describe("Property key in camelCase"), - type: z - .enum([ - "string", - "boolean", - "integer", - "decimal", - "textTemplate", - "expression", - "action", - "attribute", - "datasource", - "association", - "selection", - "enumeration", - "icon", - "image", - "file", - "widgets", - "object" - ]) - .describe("Mendix property type"), - caption: z.string().min(1).describe("Display caption shown in Studio Pro"), - description: z.string().optional().describe("Help text shown in Studio Pro"), - required: z.boolean().optional().describe("Whether this property is required"), - defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for this property"), - enumValues: z - .array(z.object({ key: z.string().min(1), caption: z.string().min(1) })) - .optional() - .describe("Allowed values for enumeration type"), - attributeTypes: z - .array( - z.enum([ - "String", - "Integer", - "Long", - "Decimal", - "Boolean", - "DateTime", - "Enum", - "HashString", - "Binary", - "AutoNumber" - ]) - ) - .optional() - .describe("Allowed attribute types for attribute property"), - isList: z.boolean().optional().describe("Whether datasource returns a list"), - dataSource: z.string().optional().describe("Reference to datasource property key (for widgets type)"), - returnType: z - .enum(["String", "Integer", "Decimal", "Boolean", "DateTime"]) - .optional() - .describe("Return type for expression property") -}); - -const operationSchema = z.discriminatedUnion("action", [ - z.object({ - action: z.literal("add"), - property: propertyDefinitionSchema.describe("Property definition to add") - }), - z.object({ - action: z.literal("remove"), - propertyKey: z.string().min(1).describe("Key of the property to remove") - }), - z.object({ - action: z.literal("modify"), - propertyKey: z.string().min(1).describe("Key of the property to modify"), - updates: z.record(z.string(), z.unknown()).describe("Fields to merge into the existing property definition") - }) -]); - -const updateWidgetPropertiesSchema = z.object({ - widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), - operations: z - .array(operationSchema) - .min(1) - .describe("List of operations to apply sequentially (add/remove/modify)"), - systemProperties: z - .array(z.enum(["Name", "TabIndex", "Visibility"])) - .optional() - .describe("Replaces current system properties if provided"), - propertyGroups: z - .array( - z.object({ - caption: z.string().min(1).describe("Group caption"), - properties: z.array(z.string().min(1)).min(1).describe("Property keys in this group") - }) - ) - .optional() - .describe("Replaces current property groups if provided") -}); - -type UpdateWidgetPropertiesInput = z.infer; - -// ============================================================================= -// Tool Handler -// ============================================================================= - -async function handleUpdateWidgetProperties(args: UpdateWidgetPropertiesInput): Promise { - const { widgetPath, operations, systemProperties, propertyGroups } = args; - - try { - // Read the widget definition snapshot - const snapshotPath = "src/.widget-definition.json"; - let widgetDefinition: WidgetDefinition; - - try { - validateFilePath(widgetPath, snapshotPath); - const fullSnapshotPath = join(widgetPath, snapshotPath); - const raw = await readFile(fullSnapshotPath, "utf-8"); - widgetDefinition = JSON.parse(raw) as WidgetDefinition; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - if (message.includes("ENOENT") || message.includes("no such file")) { - return createErrorResponse( - [ - `❌ Widget definition snapshot not found at ${widgetPath}/src/.widget-definition.json`, - "", - "The snapshot is created by the generate-widget-code tool. Please run it first to generate the initial widget code.", - "", - "Example: call generate-widget-code with your widgetPath, description, and properties." - ].join("\n") - ); - } - return createErrorResponse(`Failed to read widget definition: ${message}`); - } - - // Apply operations sequentially - const changeLog: string[] = []; - - for (const op of operations) { - if (op.action === "add") { - const existing = widgetDefinition.properties.find(p => p.key === op.property.key); - if (existing) { - return createErrorResponse( - `Cannot add property: key "${op.property.key}" already exists. Use action "modify" to update it.` - ); - } - widgetDefinition.properties.push(op.property as PropertyDefinition); - changeLog.push(`+ Added property "${op.property.key}" (${op.property.type})`); - } else if (op.action === "remove") { - const idx = widgetDefinition.properties.findIndex(p => p.key === op.propertyKey); - if (idx === -1) { - return createErrorResponse(`Cannot remove property: key "${op.propertyKey}" not found.`); - } - widgetDefinition.properties.splice(idx, 1); - changeLog.push(`- Removed property "${op.propertyKey}"`); - } else if (op.action === "modify") { - const prop = widgetDefinition.properties.find(p => p.key === op.propertyKey); - if (!prop) { - return createErrorResponse(`Cannot modify property: key "${op.propertyKey}" not found.`); - } - Object.assign(prop, op.updates); - changeLog.push(`~ Modified property "${op.propertyKey}": ${Object.keys(op.updates).join(", ")}`); - } - } - - // Replace systemProperties / propertyGroups if provided - if (systemProperties !== undefined) { - widgetDefinition.systemProperties = systemProperties as SystemProperty[]; - changeLog.push(`~ Updated systemProperties: [${systemProperties.join(", ")}]`); - } - - if (propertyGroups !== undefined) { - widgetDefinition.propertyGroups = propertyGroups as PropertyGroup[]; - changeLog.push(`~ Updated propertyGroups (${propertyGroups.length} groups)`); - } - - // Validate updated definition - const validationErrors = validateWidgetDefinition(widgetDefinition); - if (validationErrors.length > 0) { - return createErrorResponse( - [ - "❌ Updated widget definition is invalid:", - "", - ...validationErrors.map(e => ` • ${e}`), - "", - "Please fix the above issues. Operations were NOT saved." - ].join("\n") - ); - } - - // Regenerate XML - const xmlResult = generateWidgetXml(widgetDefinition); - if (!xmlResult.success || !xmlResult.xml) { - return createErrorResponse(`XML regeneration failed: ${xmlResult.error}`); - } - - // Write updated XML and snapshot - const xmlPath = `src/${widgetDefinition.name}.xml`; - const filesToWrite = [ - { path: xmlPath, content: xmlResult.xml }, - { path: snapshotPath, content: JSON.stringify(widgetDefinition, null, 2) } - ]; - - for (const file of filesToWrite) { - try { - validateFilePath(widgetPath, file.path, true); - const fullPath = join(widgetPath, file.path); - await writeFile(fullPath, file.content, "utf-8"); - console.error(`[property-update] Wrote: ${fullPath}`); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - return createErrorResponse(`Failed to write ${file.path}: ${message}`); - } - } - - return createToolResponse( - [ - `✅ Widget properties updated successfully!`, - "", - `📝 Changes applied (${changeLog.length}):`, - ...changeLog.map(c => ` ${c}`), - "", - `📁 Files updated:`, - ` • ${xmlPath} - Regenerated with ${widgetDefinition.properties.length} properties`, - ` • ${snapshotPath} - Snapshot updated`, - "", - `🔨 Next steps:`, - ` 1. Run build-widget to compile and validate`, - ` 2. Update src/${widgetDefinition.name}.tsx if new properties need to be wired up` - ].join("\n") - ); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - console.error(`[property-update] Error: ${message}`); - return createErrorResponse(`Widget property update failed: ${message}`); - } -} - -// ============================================================================= -// Tool Registration -// ============================================================================= - -const UPDATE_WIDGET_PROPERTIES_DESCRIPTION = `Incrementally updates widget properties without full regeneration. - -Reads the widget definition snapshot (src/.widget-definition.json created by generate-widget-code), -applies the specified operations, validates the result, and regenerates the XML. - -**Operations:** -- \`add\`: Add a new property -- \`remove\`: Remove an existing property by key -- \`modify\`: Merge updates into an existing property - -**Prerequisites:** -- Widget must have been generated with generate-widget-code first (creates the snapshot) - -**Example — add a property and remove another:** -\`\`\`json -{ - "widgetPath": "/path/to/MyWidget", - "operations": [ - { "action": "add", "property": { "key": "label", "type": "textTemplate", "caption": "Label" } }, - { "action": "remove", "propertyKey": "oldProp" } - ] -} -\`\`\``; - -/** - * Registers the property update tool with the MCP server. - */ -export function registerPropertyUpdateTools(server: McpServer): void { - server.registerTool( - "update-widget-properties", - { - title: "Update Widget Properties", - description: UPDATE_WIDGET_PROPERTIES_DESCRIPTION, - inputSchema: updateWidgetPropertiesSchema - }, - handleUpdateWidgetProperties - ); - - console.error("[property-update] Registered 1 tool"); -} diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts deleted file mode 100644 index 95fbfa2576..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/mpk-analyzer.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { existsSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; -import { describe, expect, it } from "vitest"; -import { analyzeMpk } from "@/tools/utils/mpk-analyzer"; - -const CURRENT_DIR = dirname(fileURLToPath(import.meta.url)); -const REPO_ROOT = resolve(CURRENT_DIR, "../../../../../.."); - -// Repository-relative paths to fixture .mpk files. -// Tests skip gracefully when files are not present on the current machine. -const KNOWN_GOOD_MPK = `${REPO_ROOT}/packages/pluggableWidgets/badge-web/dist/3.2.3/Badge.mpk`; - -const MCP_GENERATED_MPK = `${REPO_ROOT}/packages/pluggable-widgets-mcp/generations/asciiArtWidget/dist/1.0.0/mendix.AsciiArtWidget.mpk`; - -describe("mpk-analyzer (diagnostic)", () => { - it("analyzes a known-good .mpk from pluggableWidgets", () => { - if (!existsSync(KNOWN_GOOD_MPK)) { - console.log(`[SKIP] fixture not found: ${KNOWN_GOOD_MPK}`); - expect(true).toBe(true); - return; - } - - const analysis = analyzeMpk(KNOWN_GOOD_MPK); - - console.log("\n=== Known-Good MPK Analysis ==="); - console.log(`Path: ${analysis.mpkPath}`); - console.log(`Size: ${analysis.mpkSizeBytes} bytes`); - console.log(`Files (${analysis.files.length}):`); - for (const f of analysis.files) { - console.log(` ${f.path} (${f.sizeBytes} bytes)`); - } - if (analysis.packageXml) { - console.log(`\npackage.xml:`); - console.log(` clientModuleName: ${analysis.packageXml.clientModuleName}`); - console.log(` version: ${analysis.packageXml.version}`); - console.log(` widgetFilePath: ${analysis.packageXml.widgetFilePath}`); - console.log(` filesPath: ${analysis.packageXml.filesPath}`); - } - if (analysis.widgetXml) { - console.log(`\nWidget XML:`); - console.log(` id: ${analysis.widgetXml.id}`); - console.log(` pluginWidget: ${analysis.widgetXml.pluginWidget}`); - console.log(` needsEntityContext:${analysis.widgetXml.needsEntityContext}`); - console.log(` propertyCount: ${analysis.widgetXml.propertyCount}`); - } - if (analysis.bundle) { - console.log(`\nJS Bundle:`); - console.log(` fileName: ${analysis.bundle.fileName}`); - console.log(` sizeBytes: ${analysis.bundle.sizeBytes}`); - console.log(` format: ${analysis.bundle.format}`); - console.log(` containsDefine: ${analysis.bundle.containsDefine}`); - console.log(` exportDefault: ${analysis.bundle.containsExportDefault}`); - console.log(` exportNamed: ${analysis.bundle.containsExportNamed}`); - console.log(` hasUseStrict: ${analysis.bundle.hasUseStrict}`); - console.log(` exportPattern: ${JSON.stringify(analysis.bundle.exportPattern)}`); - } - if (analysis.errors.length > 0) { - console.log(`\nErrors: ${JSON.stringify(analysis.errors)}`); - } - - // Structural assertions — known-good widget must parse cleanly - expect(analysis.errors).toHaveLength(0); - expect(analysis.packageXml).toBeDefined(); - expect(analysis.bundle).toBeDefined(); - // format is diagnostic output, not asserted — printed above for comparison - }); - - it("analyzes an MCP-generated .mpk", () => { - if (!existsSync(MCP_GENERATED_MPK)) { - console.log(`[SKIP] fixture not found: ${MCP_GENERATED_MPK}`); - expect(true).toBe(true); - return; - } - - const analysis = analyzeMpk(MCP_GENERATED_MPK); - - console.log("\n=== MCP-Generated MPK Analysis ==="); - console.log(`Path: ${analysis.mpkPath}`); - console.log(`Size: ${analysis.mpkSizeBytes} bytes`); - console.log(`Files (${analysis.files.length}):`); - for (const f of analysis.files) { - console.log(` ${f.path} (${f.sizeBytes} bytes)`); - } - if (analysis.packageXml) { - console.log(`\npackage.xml:`); - console.log(` clientModuleName: ${analysis.packageXml.clientModuleName}`); - console.log(` version: ${analysis.packageXml.version}`); - console.log(` widgetFilePath: ${analysis.packageXml.widgetFilePath}`); - console.log(` filesPath: ${analysis.packageXml.filesPath}`); - } - if (analysis.widgetXml) { - console.log(`\nWidget XML:`); - console.log(` id: ${analysis.widgetXml.id}`); - console.log(` pluginWidget: ${analysis.widgetXml.pluginWidget}`); - console.log(` needsEntityContext:${analysis.widgetXml.needsEntityContext}`); - console.log(` propertyCount: ${analysis.widgetXml.propertyCount}`); - } - if (analysis.bundle) { - console.log(`\nJS Bundle:`); - console.log(` fileName: ${analysis.bundle.fileName}`); - console.log(` sizeBytes: ${analysis.bundle.sizeBytes}`); - console.log(` format: ${analysis.bundle.format}`); - console.log(` containsDefine: ${analysis.bundle.containsDefine}`); - console.log(` exportDefault: ${analysis.bundle.containsExportDefault}`); - console.log(` exportNamed: ${analysis.bundle.containsExportNamed}`); - console.log(` hasUseStrict: ${analysis.bundle.hasUseStrict}`); - console.log(` exportPattern: ${JSON.stringify(analysis.bundle.exportPattern)}`); - } - if (analysis.errors.length > 0) { - console.log(`\nErrors: ${JSON.stringify(analysis.errors)}`); - } - - // Diagnostic only — we don't assert expected format because we're discovering it - expect(analysis.mpkSizeBytes).toBeGreaterThan(0); - }); - - it("compares known-good vs MCP-generated side by side", () => { - const goodExists = existsSync(KNOWN_GOOD_MPK); - const mcpExists = existsSync(MCP_GENERATED_MPK); - - if (!goodExists || !mcpExists) { - console.log(`[SKIP] both fixtures required for comparison`); - console.log(` known-good: ${goodExists ? "found" : "MISSING"}`); - console.log(` mcp-generated: ${mcpExists ? "found" : "MISSING"}`); - expect(true).toBe(true); - return; - } - - const good = analyzeMpk(KNOWN_GOOD_MPK); - const mcp = analyzeMpk(MCP_GENERATED_MPK); - - console.log("\n=== Side-by-Side Comparison ==="); - console.log(`${"FIELD".padEnd(30)} ${"KNOWN-GOOD".padEnd(40)} MCP-GENERATED`); - console.log("-".repeat(100)); - - const row = (label: string, a: unknown, b: unknown): void => { - const aStr = String(a ?? "(none)"); - const bStr = String(b ?? "(none)"); - const flag = aStr !== bStr ? " <<<" : ""; - console.log(`${label.padEnd(30)} ${aStr.padEnd(40)} ${bStr}${flag}`); - }; - - row("mpkSizeBytes", good.mpkSizeBytes, mcp.mpkSizeBytes); - row("fileCount", good.files.length, mcp.files.length); - row("packageXml.clientModuleName", good.packageXml?.clientModuleName, mcp.packageXml?.clientModuleName); - row("packageXml.version", good.packageXml?.version, mcp.packageXml?.version); - row("packageXml.widgetFilePath", good.packageXml?.widgetFilePath, mcp.packageXml?.widgetFilePath); - row("packageXml.filesPath", good.packageXml?.filesPath, mcp.packageXml?.filesPath); - row("widgetXml.id", good.widgetXml?.id, mcp.widgetXml?.id); - row("widgetXml.pluginWidget", good.widgetXml?.pluginWidget, mcp.widgetXml?.pluginWidget); - row("widgetXml.needsEntityContext", good.widgetXml?.needsEntityContext, mcp.widgetXml?.needsEntityContext); - row("widgetXml.propertyCount", good.widgetXml?.propertyCount, mcp.widgetXml?.propertyCount); - row("bundle.format", good.bundle?.format, mcp.bundle?.format); - row("bundle.containsDefine", good.bundle?.containsDefine, mcp.bundle?.containsDefine); - row("bundle.exportDefault", good.bundle?.containsExportDefault, mcp.bundle?.containsExportDefault); - row("bundle.exportNamed", good.bundle?.containsExportNamed, mcp.bundle?.containsExportNamed); - row("bundle.hasUseStrict", good.bundle?.hasUseStrict, mcp.bundle?.hasUseStrict); - row("bundle.sizeBytes", good.bundle?.sizeBytes, mcp.bundle?.sizeBytes); - row("errors", good.errors.join("|"), mcp.errors.join("|")); - - console.log("\nKnown-good file list:"); - for (const f of good.files) console.log(` ${f.path}`); - console.log("\nMCP-generated file list:"); - for (const f of mcp.files) console.log(` ${f.path}`); - - // The comparison runs — the console output is the diagnostic result. - // We assert only that both analyses completed without fatal errors. - expect(good.mpkSizeBytes).toBeGreaterThan(0); - expect(mcp.mpkSizeBytes).toBeGreaterThan(0); - }); - - it("widget XML id matches file path convention", () => { - if (!existsSync(MCP_GENERATED_MPK)) { - console.log(`[SKIP] fixture not found: ${MCP_GENERATED_MPK}`); - expect(true).toBe(true); - return; - } - - const analysis = analyzeMpk(MCP_GENERATED_MPK); - - if (!analysis.widgetXml?.id) { - console.log("[SKIP] no widget XML id found in MPK"); - expect(true).toBe(true); - return; - } - - // Convert dotted id to slash-based path: "mendix.asciiartwidget.AsciiArtWidget" - // → "mendix/asciiartwidget/AsciiArtWidget" - const idAsPath = analysis.widgetXml.id.replace(/\./g, "/"); - const jsPath = `${idAsPath}.js`; - - console.log(`\n=== Widget Id / File Path Check ===`); - console.log(` widgetXml.id: ${analysis.widgetXml.id}`); - console.log(` expected js path: ${jsPath}`); - console.log(` files in MPK:`); - for (const f of analysis.files) { - console.log(` ${f.path}`); - } - - const match = analysis.files.some( - f => f.path === jsPath || f.path.endsWith(`/${idAsPath.split("/").slice(-2).join("/")}.js`) - ); - expect(match).toBe(true); - }); -}); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts b/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts deleted file mode 100644 index 14f6cbfea7..0000000000 --- a/packages/pluggable-widgets-mcp/src/tools/utils/mpk-analyzer.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { execSync } from "node:child_process"; -import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs"; -import { basename, join } from "node:path"; -import { tmpdir } from "node:os"; - -export interface MpkFileEntry { - path: string; - sizeBytes: number; -} - -export interface PackageXmlInfo { - clientModuleName?: string; - version?: string; - widgetFilePath?: string; - filesPath?: string; - raw: string; -} - -export interface WidgetXmlInfo { - id?: string; - pluginWidget?: boolean; - needsEntityContext?: boolean; - propertyCount: number; - raw: string; -} - -export interface BundleInfo { - fileName: string; - sizeBytes: number; - format: "amd" | "esm" | "unknown"; - hasUseStrict: boolean; - exportPattern: string[]; - containsDefine: boolean; - containsExportDefault: boolean; - containsExportNamed: boolean; -} - -export interface MpkAnalysis { - mpkPath: string; - mpkSizeBytes: number; - files: MpkFileEntry[]; - packageXml?: PackageXmlInfo; - widgetXml?: WidgetXmlInfo; - bundle?: BundleInfo; - errors: string[]; -} - -/** - * Analyzes an .mpk file (ZIP archive) and returns structural findings. - * Unzips to a temp directory, reads package.xml, widget XML, and the JS bundle. - * No new dependencies — uses the macOS/Linux `unzip` command. - */ -export function analyzeMpk(mpkPath: string): MpkAnalysis { - const errors: string[] = []; - - if (!existsSync(mpkPath)) { - return { - mpkPath, - mpkSizeBytes: 0, - files: [], - errors: [`File not found: ${mpkPath}`] - }; - } - - const mpkSizeBytes = statSync(mpkPath).size; - const tempDir = mkdtempSync(join(tmpdir(), "mpk-analyze-")); - - try { - // Unzip to temp directory - try { - execSync(`unzip -q "${mpkPath}" -d "${tempDir}"`, { timeout: 30000 }); - } catch (e) { - const msg = e instanceof Error ? e.message : String(e); - errors.push(`unzip failed: ${msg}`); - return { mpkPath, mpkSizeBytes, files: [], errors }; - } - - // Catalog all files - const files = catalogFiles(tempDir, tempDir); - - // Parse package.xml - const packageXml = parsePackageXml(tempDir, errors); - - // Find and parse widget XML - const widgetXml = parseWidgetXml(tempDir, files, errors); - - // Find and analyze JS bundle - const bundle = analyzeBundle(tempDir, files, errors); - - return { mpkPath, mpkSizeBytes, files, packageXml, widgetXml, bundle, errors }; - } finally { - try { - rmSync(tempDir, { recursive: true, force: true }); - } catch { - // ignore cleanup errors - } - } -} - -function catalogFiles(dir: string, rootDir: string): MpkFileEntry[] { - const entries: MpkFileEntry[] = []; - try { - for (const entry of readdirSync(dir, { withFileTypes: true })) { - const fullPath = join(dir, entry.name); - if (entry.isDirectory()) { - entries.push(...catalogFiles(fullPath, rootDir)); - } else { - const relativePath = fullPath.slice(rootDir.length + 1); - entries.push({ path: relativePath, sizeBytes: statSync(fullPath).size }); - } - } - } catch { - // ignore - } - return entries; -} - -function parsePackageXml(tempDir: string, errors: string[]): PackageXmlInfo | undefined { - const xmlPath = join(tempDir, "package.xml"); - if (!existsSync(xmlPath)) { - errors.push("package.xml not found"); - return undefined; - } - - const raw = readFileSync(xmlPath, "utf-8"); - - const clientModuleName = extractXmlAttr(raw, "clientModule", "name"); - const version = extractXmlAttr(raw, "clientModule", "version"); - - // - const widgetFileMatch = raw.match(/ - const filesMatch = raw.match(/ f.path.endsWith(".xml") && !f.path.includes("package.xml")); - if (!xmlFile) { - errors.push("No widget .xml found"); - return undefined; - } - - const raw = readFileSync(join(tempDir, xmlFile.path), "utf-8"); - - const id = extractXmlAttr(raw, "widget", "id"); - const pluginWidgetStr = extractXmlAttr(raw, "widget", "pluginWidget"); - const needsEntityContextStr = extractXmlAttr(raw, "widget", "needsEntityContext"); - - const propertyCount = (raw.match(/ - f.path.endsWith(".js") && - f.path.includes("/") && - !f.path.includes("editorPreview") && - !f.path.includes("editorConfig") - ) ?? - files.find( - f => f.path.endsWith(".js") && !f.path.includes("editorPreview") && !f.path.includes("editorConfig") - ); - - if (!jsFile) { - errors.push("No JS bundle found"); - return undefined; - } - - const fullPath = join(tempDir, jsFile.path); - const content = readFileSync(fullPath, "utf-8"); - - const containsDefine = content.includes("define("); - const containsExportDefault = /export\s+default\s/.test(content); - const containsExportNamed = /export\s+\{/.test(content) || /export\s+function\s/.test(content); - const hasUseStrict = content.includes('"use strict"') || content.includes("'use strict'"); - - let format: "amd" | "esm" | "unknown" = "unknown"; - if (containsDefine) format = "amd"; - else if (containsExportDefault || containsExportNamed) format = "esm"; - - // Collect first few export/define patterns for comparison - const exportPattern: string[] = []; - const patterns = content.matchAll(/(export\s+(?:default\s+)?(?:function|class|const|let|var)\s+\w+|define\s*\()/g); - for (const match of patterns) { - if (exportPattern.length < 5) exportPattern.push(match[0]); - } - - return { - fileName: basename(jsFile.path), - sizeBytes: jsFile.sizeBytes, - format, - hasUseStrict, - exportPattern, - containsDefine, - containsExportDefault, - containsExportNamed - }; -} - -function extractXmlAttr(xml: string, tagName: string, attrName: string): string | undefined { - const pattern = new RegExp(`<${tagName}[^>]+${attrName}="([^"]+)"`, "i"); - return xml.match(pattern)?.[1]; -} From f86769a92b0555e26d183ace652b61ff53939489 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:34:39 +0200 Subject: [PATCH 27/36] fix(security): anchor the sandbox to the Mendix project directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The server runs as a child process of Studio Pro, not as a service started by hand from the package directory. Under that model GENERATIONS_DIR, which was join(process.cwd(), "generations"), moved the security boundary depending on who spawned the process — and Studio Pro's cwd is its install directory, often read-only. It anchored both the scaffold output and the sandbox allowlist. There is now exactly one root: state.projectDir. Scaffolding targets {projectDir}/widget-sources/, so sources travel with the project in version control. MCP_ALLOWED_OUTPUT_PATHS and MCP_ALLOWED_BUILD_PATHS collapse into one optional MCP_EXTRA_ALLOWED_PATHS for development, split on path.delimiter rather than ":" — the latter tears "C:\widgets" into ["C", "\widgets"]. MENDIX_PROJECT_DIR stops being a frozen module constant and moves into session state, so set-project-directory can re-point on a project switch without a restart. Three path bugs, all of the same family: - Containment used `resolved.startsWith(allowed + "/")`. On Windows the separator is not "/". Now path.sep, consistently across sandbox.ts, guardrails.ts and build.tools.ts, which had three different answers. - validateFilePath rejected any path containing ".." as a substring, refusing a legitimate src/foo..bar.tsx. resolve() collapses ".." before comparison, so containment was always the real defence; the substring test only produced false positives. - Extensionless filenames were matched with includes(), so "unpackaged", "mypackage" and "prepackage-hook" all passed as "package". Now equality. validateProjectDir took whichever .mpr the filesystem happened to return first; it now sorts and reports clearly when a directory holds more than one. --- packages/pluggable-widgets-mcp/src/config.ts | 83 +++++++++++++++---- .../src/security/__tests__/guardrails.test.ts | 10 ++- .../src/security/guardrails.ts | 32 ++++--- .../src/tools/session-state.ts | 13 ++- .../src/tools/utils/sandbox.ts | 47 ++++++++--- 5 files changed, 131 insertions(+), 54 deletions(-) diff --git a/packages/pluggable-widgets-mcp/src/config.ts b/packages/pluggable-widgets-mcp/src/config.ts index 6d5c7433bd..36c5a769e7 100644 --- a/packages/pluggable-widgets-mcp/src/config.ts +++ b/packages/pluggable-widgets-mcp/src/config.ts @@ -14,34 +14,72 @@ export const SERVER_ICON = { mimeType: "image/png" }; export const SERVER_WEBSITE_URL = "https://github.com/mendix/web-widgets"; -export const SERVER_INSTRUCTIONS = `This is a MCP server for Mendix Pluggable Widgets. It allows you to create, build, and deploy widgets to a Mendix project. +/** + * Sent once at initialize. This is the single place the widget workflow is described — tool + * descriptions say what each tool does, not what to call next, so the sequence is stated once + * rather than duplicated across descriptions and response bodies. + */ +export const SERVER_INSTRUCTIONS = `MCP server for building Mendix pluggable widgets. + +Workflow: + 1. get-project-info Discover the open Mendix project. + 2. create-widget Scaffold into {project}/widget-sources/. + 3. set-widget-properties Write the widget's XML from a property model. + 4. write-widget-file Write the .tsx and .scss yourself. + 5. build-widget Compile to .mpk. + 6. deploy-widget Copy the .mpk into the project's widgets/ folder. -WORKFLOW GUIDE: -1. Call get-project-info first to discover the configured Mendix project directory. -2. If a project is configured, you can scaffold, build, and deploy widgets without asking for filesystem paths. -3. If no project is configured, use set-project-directory to configure one, or proceed without deployment. -4. Use create-widget to scaffold a new widget (output goes to the generations/ directory). -5. Use build-widget to compile the widget and produce an .mpk file. -6. Use deploy-widget to copy the .mpk to the project's widgets/ folder. +Read these resources before writing component source: + mendix://guidelines/property-types property model schema + mendix://guidelines/widget-patterns component templates per widget archetype -IMPORTANT: Do NOT ask the user for filesystem paths — use get-project-info to discover the project context automatically.`; +The server generates XML because that is mechanically derivable from the property model. Component +source is yours to write. -// Paths - use fileURLToPath for Node.js 18 compatibility (import.meta.dirname requires Node 20.11+) +Every path must resolve inside the configured project directory. Do not ask the user for filesystem +paths — call get-project-info instead. If no project is configured, call set-project-directory.`; + +// Paths are derived from this module's location, never from process.cwd(). The server runs as a +// child process of Mendix Studio Pro, whose cwd is its own install directory — often read-only. const __dirname = import.meta.dirname ?? dirname(fileURLToPath(import.meta.url)); export const PACKAGE_ROOT = join(__dirname, "../"); -export const GENERATIONS_DIR = join(process.cwd(), "generations"); -const _pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8")) as { version: string }; -export const SERVER_VERSION = _pkg.version; +export const SERVER_VERSION = readServerVersion(); + +function readServerVersion(): string { + try { + const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8")) as { version?: string }; + return pkg.version ?? "0.0.0"; + } catch { + // A missing package.json must not take down module evaluation. + return "0.0.0"; + } +} // Path to local docs folder export const DOCS_DIR = join(PACKAGE_ROOT, "docs"); // Timeouts -export const SCAFFOLD_TIMEOUT_MS = 300000; // 5 minutes +// Scaffolding runs in-process and only renders templates to disk, so it is fast; the long pole is +// installing the new widget's dependencies, which is a separate step with its own budget. +export const SCAFFOLD_TIMEOUT_MS = 60000; // 1 minute +export const INSTALL_TIMEOUT_MS = 300000; // 5 minutes +export const BUILD_TIMEOUT_MS = 300000; // 5 minutes -// Project directory configuration -export const MENDIX_PROJECT_DIR = process.env.MENDIX_PROJECT_DIR ? resolve(process.env.MENDIX_PROJECT_DIR) : undefined; +/** + * The Mendix project directory Studio Pro passed at spawn, if any. + * + * Read on call rather than captured at module load, so nothing holds a stale copy — the live value + * is whatever the session state says, which `set-project-directory` can re-point. + */ +export function getConfiguredProjectDir(): string | undefined { + return process.env.MENDIX_PROJECT_DIR ? resolve(process.env.MENDIX_PROJECT_DIR) : undefined; +} + +/** Widget sources live inside the project, alongside the `widgets/` folder builds deploy into. */ +export function widgetSourcesDir(projectDir: string): string { + return join(projectDir, "widget-sources"); +} export interface ProjectValidation { valid: boolean; @@ -75,7 +113,18 @@ export async function validateProjectDir(dir: string): Promise entry.endsWith(".mpr")); + // Sorted so the choice is deterministic rather than dependent on readdir order. + const mprFiles = entries.filter(entry => entry.endsWith(".mpr")).sort(); + if (mprFiles.length > 1) { + return { + valid: false, + projectDir: dir, + widgetsDir, + existingWidgets: [], + error: `Multiple .mpr files found in ${dir} (${mprFiles.join(", ")}). Point at a directory containing exactly one Mendix project.` + }; + } + const mprFile = mprFiles[0]; if (!mprFile) { return { valid: false, diff --git a/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts index 19c7ec7ee8..8aaaabbd03 100644 --- a/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts +++ b/packages/pluggable-widgets-mcp/src/security/__tests__/guardrails.test.ts @@ -43,8 +43,14 @@ describe("validateFilePath", () => { expect(() => validateFilePath("/widgets/foo", "src/Bar.tsx")).not.toThrow(); }); - it("throws for .. in the path", () => { - expect(() => validateFilePath("/widgets/foo", "../secret.txt")).toThrow("Path traversal"); + it("throws for a path that escapes the widget directory", () => { + expect(() => validateFilePath("/widgets/foo", "../secret.txt")).toThrow("within the widget directory"); + }); + + it("allows a filename that merely contains dots", () => { + // Traversal is caught by containment, not by looking for ".." as a substring, so a + // legitimate name like this is not collateral damage. + expect(() => validateFilePath("/widgets/foo", "src/foo..bar.tsx")).not.toThrow(); }); it("throws for disallowed extension when checkExtension is true", () => { diff --git a/packages/pluggable-widgets-mcp/src/security/guardrails.ts b/packages/pluggable-widgets-mcp/src/security/guardrails.ts index 07f9515483..b54b86cc6f 100644 --- a/packages/pluggable-widgets-mcp/src/security/guardrails.ts +++ b/packages/pluggable-widgets-mcp/src/security/guardrails.ts @@ -9,7 +9,7 @@ * @module security/guardrails */ -import { extname, resolve } from "node:path"; +import { basename, extname, resolve, sep } from "node:path"; // ============================================================================= // Configuration @@ -22,9 +22,9 @@ import { extname, resolve } from "node:path"; export const ALLOWED_EXTENSIONS = [".tsx", ".ts", ".xml", ".scss", ".css", ".json", ".md"]; /** - * Config files allowed without extensions (e.g., tsconfig, package) + * Extensionless config files allowed by exact filename. */ -const ALLOWED_EXTENSIONLESS_PATTERNS = ["package", "tsconfig", "eslintrc"]; +const ALLOWED_EXTENSIONLESS_NAMES = ["package", "tsconfig", "eslintrc"]; /** * Specific dot-files allowed (explicit allowlist to prevent arbitrary dotfile access). @@ -56,9 +56,10 @@ export function isPathWithinDirectory(basePath: string, relativePath: string): b const resolvedBase = resolve(basePath); const resolvedFull = resolve(basePath, relativePath); - // Check that the resolved path starts with the base path - // This prevents ../ traversal attacks - return resolvedFull.startsWith(resolvedBase + "/") || resolvedFull === resolvedBase; + // Compare against base + separator so a sibling like /widgets/foobar cannot pass as /widgets/foo. + // `sep`, not a literal "/": path.resolve() yields backslashes on Windows, where a hardcoded + // slash would make this return false for every path. + return resolvedFull === resolvedBase || resolvedFull.startsWith(resolvedBase + sep); } // ============================================================================= @@ -80,11 +81,10 @@ export function isExtensionAllowed(filePath: string): boolean { // Also allow files without extension (like .gitignore patterns) // and special config files if (ext === "") { - const filename = filePath.split("/").pop() || ""; - // Allow common config files without extensions, or specific dot-files - return ( - ALLOWED_EXTENSIONLESS_PATTERNS.some(name => filename.includes(name)) || ALLOWED_DOT_FILES.includes(filename) - ); + const filename = basename(filePath); + // Exact match, not substring: `includes` would also admit "unpackaged", "mypackage", + // "prepackage-hook". + return ALLOWED_EXTENSIONLESS_NAMES.includes(filename) || ALLOWED_DOT_FILES.includes(filename); } return ALLOWED_EXTENSIONS.includes(ext); } @@ -101,7 +101,6 @@ export function isExtensionAllowed(filePath: string): boolean { * @param filePath - The relative file path to validate * @param checkExtension - Whether to also validate file extension (for write operations) * - * @throws {Error} If path traversal detected ('..' in path) * @throws {Error} If path escapes widget directory * @throws {Error} If extension not allowed (when checkExtension=true) * @@ -113,12 +112,9 @@ export function isExtensionAllowed(filePath: string): boolean { * validateFilePath("/widgets/foo", "src/Bar.tsx", true); */ export function validateFilePath(widgetPath: string, filePath: string, checkExtension = false): void { - // Check for obvious path traversal attempts - if (filePath.includes("..")) { - throw new Error("Path traversal not allowed: '..' detected in file path"); - } - - // Validate path is within widget directory + // Containment is the whole traversal defence: resolve() collapses any "../" before the + // comparison. A separate substring test for ".." added nothing and rejected legitimate names + // like "src/foo..bar.tsx". if (!isPathWithinDirectory(widgetPath, filePath)) { throw new Error("File path must be within the widget directory"); } diff --git a/packages/pluggable-widgets-mcp/src/tools/session-state.ts b/packages/pluggable-widgets-mcp/src/tools/session-state.ts index b090c7aa79..915f5e3637 100644 --- a/packages/pluggable-widgets-mcp/src/tools/session-state.ts +++ b/packages/pluggable-widgets-mcp/src/tools/session-state.ts @@ -1,15 +1,20 @@ -import { MENDIX_PROJECT_DIR } from "@/config"; +import { getConfiguredProjectDir } from "@/config"; export interface SessionState { + /** + * The open Mendix project. Also the sandbox root — every path the server touches must resolve + * inside it. Undefined until configured, in which case tools refuse to run. + */ projectDir: string | undefined; } /** - * Creates a new session state, initialized from the MENDIX_PROJECT_DIR env var (if set). - * Each MCP server instance gets its own state, so concurrent sessions are isolated. + * Creates a new session state, seeded from MENDIX_PROJECT_DIR when Studio Pro passed one. + * Each MCP server instance gets its own state, so concurrent sessions are isolated, and + * `set-project-directory` can re-point a session when the user opens a different project. */ export function createSessionState(): SessionState { return { - projectDir: MENDIX_PROJECT_DIR + projectDir: getConfiguredProjectDir() }; } diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts b/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts index 2546890c7e..b7aa63cb00 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/sandbox.ts @@ -1,20 +1,41 @@ -import { resolve } from "node:path"; -import { GENERATIONS_DIR } from "@/config"; +import { delimiter, resolve, sep } from "node:path"; import type { SessionState } from "@/tools/session-state"; /** - * Checks whether a resolved path is within the allowed directories. - * Allowed dirs: GENERATIONS_DIR, env-var paths (colon-separated), state.projectDir. + * Extra roots to permit alongside the project directory, for development against widgets that live + * outside a Mendix project. Platform-delimited (`:` on POSIX, `;` on Windows) — splitting on a + * literal `:` would tear `C:\widgets` into `["C", "\widgets"]`. */ -export function isPathAllowed(targetPath: string, state: SessionState, envVar?: string): boolean { - const resolved = resolve(targetPath); - const allowed = [ - resolve(GENERATIONS_DIR), - ...((envVar ? process.env[envVar] : undefined) ?? "") - .split(":") +const EXTRA_ALLOWED_PATHS_ENV = "MCP_EXTRA_ALLOWED_PATHS"; + +/** + * Returns every directory the server is permitted to read or write under. + * + * The Mendix project directory is the boundary. It is the only root that is derived from the + * session rather than the environment, which is what makes the fence stable: an earlier version + * anchored it to `process.cwd()`, so the security boundary moved depending on who spawned the + * process. + */ +export function allowedRoots(state: SessionState): string[] { + return [ + ...(state.projectDir ? [resolve(state.projectDir)] : []), + ...(process.env[EXTRA_ALLOWED_PATHS_ENV] ?? "") + .split(delimiter) .filter(Boolean) - .map(p => resolve(p)), - ...(state.projectDir ? [resolve(state.projectDir)] : []) + .map(path => resolve(path)) ]; - return allowed.some(a => resolved.startsWith(a + "/") || resolved === a); +} + +/** Checks whether a path resolves inside one of the allowed roots. */ +export function isPathAllowed(targetPath: string, state: SessionState): boolean { + const resolved = resolve(targetPath); + return allowedRoots(state).some(root => resolved === root || resolved.startsWith(root + sep)); +} + +/** Human-readable description of the boundary, for error messages. */ +export function describeAllowedRoots(state: SessionState): string { + const roots = allowedRoots(state); + return roots.length > 0 + ? roots.join(", ") + : `no project configured — set MENDIX_PROJECT_DIR or call set-project-directory (or set ${EXTRA_ALLOWED_PATHS_ENV})`; } From 4808fbc45b3c254bc222a5bc92c23d0630f2c144 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:34:53 +0200 Subject: [PATCH 28/36] refactor(server): make HTTP stateless and fix the transport lifecycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit routes.ts dispatched on session state rather than HTTP verb, via a single app.all("/mcp"). Replacing it with explicit verbs deletes three bugs at once: - The GET branch minted a full McpServer that was never registered and never closed — one leak per request — and could only ever return 400, because a freshly constructed transport is never _initialized. - GET and DELETE read req.body.method, but express.json() leaves body undefined on those verbs. That is why DELETE teardown never worked. - The session map was unbounded. The SDK supports sessionIdGenerator: undefined, so each POST now constructs a transport and server, handles the request, and closes. SessionManager goes with it, including a console.log that would have corrupted the JSON-RPC stream if it were ever reached from stdio, and a toolCallCount that was set to zero, logged, and never incremented. http.ts bound app.listen(PORT) with no host, overriding the SDK's 127.0.0.1 default and exposing a server that spawns `npm run build` on every interface. The permissive CORS block (origin: true with credentials and allowedHeaders "*" for a one-route server) is gone, EADDRINUSE reports something actionable, and logProjectConfig is awaited — it was a floating promise that would take the process down on Node >= 15. stdio.ts gains a stdin end/close handler. That is how a stdio MCP child learns its parent died; without it the process orphans when Studio Pro is force-killed, and SIGINT/SIGTERM are not reliably delivered on Windows. index.ts cast process.argv[2] to TransportMode unchecked, so `node dist/index.js htpp` silently ran stdio. It now validates and supports --help. Adds a tagged, level-gated logger that always writes to stderr, replacing about twenty ad-hoc console.error sites. Studio Pro captures child stderr and that is the support log; stdout belongs to the protocol. --- packages/pluggable-widgets-mcp/src/index.ts | 57 +++++++-- .../src/resources/index.ts | 5 +- .../pluggable-widgets-mcp/src/server/http.ts | 70 +++++------ .../src/server/log-project-config.ts | 28 +++++ .../src/server/routes.ts | 113 ++++++++---------- .../src/server/server.ts | 3 +- .../pluggable-widgets-mcp/src/server/stdio.ts | 60 ++++++---- .../src/tools/utils/logger.ts | 48 ++++++++ 8 files changed, 253 insertions(+), 131 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/server/log-project-config.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/utils/logger.ts diff --git a/packages/pluggable-widgets-mcp/src/index.ts b/packages/pluggable-widgets-mcp/src/index.ts index 5e4fe00721..e3a0a277cc 100644 --- a/packages/pluggable-widgets-mcp/src/index.ts +++ b/packages/pluggable-widgets-mcp/src/index.ts @@ -1,16 +1,59 @@ #!/usr/bin/env node +import { SERVER_NAME, SERVER_VERSION } from "@/config"; import { startHttpServer } from "@/server/http"; import { startStdioServer } from "@/server/stdio"; -type TransportMode = "http" | "stdio"; +const TRANSPORTS = ["stdio", "http"] as const; +type TransportMode = (typeof TRANSPORTS)[number]; -const mode = (process.argv[2] as TransportMode) || "stdio"; +const USAGE = `${SERVER_NAME} ${SERVER_VERSION} -if (mode === "http") { - startHttpServer(); -} else { - startStdioServer().catch(err => { - console.error("Fatal error:", err); +Usage: pluggable-widgets-mcp [transport] + +Transports: + stdio JSON-RPC over stdin/stdout (default) — used when a host such as + Mendix Studio Pro spawns this server as a child process. + http Local HTTP on 127.0.0.1, for pointing the MCP Inspector at a running + server. Stateless; sessions are not supported. + +Environment: + MENDIX_PROJECT_DIR Path to the open Mendix project. Also the sandbox root. + MCP_EXTRA_ALLOWED_PATHS Extra permitted roots, platform-delimited (dev only). + MCP_LOG_LEVEL debug | info | warn | error (default: info). + PORT HTTP port (default: 3100).`; + +function parseTransport(argument: string | undefined): TransportMode { + if (argument === undefined) { + return "stdio"; + } + if ((TRANSPORTS as readonly string[]).includes(argument)) { + return argument as TransportMode; + } + // An unrecognised argument used to fall through to stdio via an unchecked cast, so a typo like + // `htpp` started a server that looked fine and spoke the wrong protocol. + console.error(`Unknown transport "${argument}".\n\n${USAGE}`); + process.exit(1); +} + +function main(): void { + const argument = process.argv[2]; + + if (argument === "--help" || argument === "-h") { + console.error(USAGE); + return; + } + + const transport = parseTransport(argument); + + if (transport === "http") { + startHttpServer(); + return; + } + + startStdioServer().catch((error: unknown) => { + console.error(`Fatal error starting stdio server: ${String(error)}`); process.exit(1); }); } + +main(); diff --git a/packages/pluggable-widgets-mcp/src/resources/index.ts b/packages/pluggable-widgets-mcp/src/resources/index.ts index 2ef570bfb3..7e16ffe8ec 100644 --- a/packages/pluggable-widgets-mcp/src/resources/index.ts +++ b/packages/pluggable-widgets-mcp/src/resources/index.ts @@ -1,5 +1,8 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { GUIDELINE_RESOURCES, loadGuidelineContent } from "./guidelines"; +import { createLogger } from "@/tools/utils/logger"; + +const log = createLogger("resources"); /** * Registers all MCP resources with the server. @@ -40,5 +43,5 @@ function registerGuidelineResources(server: McpServer): void { ); } - console.error(`[resources] Registered ${GUIDELINE_RESOURCES.length} guideline resources`); + log.info(`Registered ${GUIDELINE_RESOURCES.length} guideline resources`); } diff --git a/packages/pluggable-widgets-mcp/src/server/http.ts b/packages/pluggable-widgets-mcp/src/server/http.ts index 6c867a7543..2350b11ecf 100644 --- a/packages/pluggable-widgets-mcp/src/server/http.ts +++ b/packages/pluggable-widgets-mcp/src/server/http.ts @@ -1,50 +1,50 @@ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; -import cors from "cors"; -import { MENDIX_PROJECT_DIR, PORT, validateProjectDir } from "@/config"; +import type { Server } from "node:http"; +import { PORT } from "@/config"; +import { createLogger } from "@/tools/utils/logger"; +import { logProjectConfig } from "./log-project-config"; import { setupRoutes } from "./routes"; -import { sessionManager } from "./session"; -async function logProjectConfig(): Promise { - if (MENDIX_PROJECT_DIR) { - const validation = await validateProjectDir(MENDIX_PROJECT_DIR); - if (validation.valid) { - console.log(`[HTTP] Project: ${validation.projectName} (${MENDIX_PROJECT_DIR})`); - } else { - console.warn(`[HTTP] Warning: MENDIX_PROJECT_DIR is set but invalid: ${validation.error}`); - } - } else { - console.log(`[HTTP] No project configured (set MENDIX_PROJECT_DIR to enable deploy support)`); - } -} +const log = createLogger("http"); + +/** Loopback only. The tools spawn `npm run build`, so this must not be reachable off-box. */ +const HOST = "127.0.0.1"; /** - * Starts the MCP server with HTTP/Streamable transport. - * Supports multiple concurrent sessions via Express. + * Starts the MCP server over HTTP. + * + * This transport exists for local debugging — pointing the MCP Inspector at a running server. + * Studio Pro uses STDIO. Requests are handled statelessly; see `routes.ts`. */ export function startHttpServer(): void { - const app = createMcpExpressApp(); - app.use( - cors({ - origin: true, - methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"], - allowedHeaders: "*", - exposedHeaders: ["mcp-session-id"], - credentials: true - }) - ); + // createMcpExpressApp installs DNS-rebinding protection when the host is loopback. + const app = createMcpExpressApp({ host: HOST }); setupRoutes(app); - const server = app.listen(PORT, () => { - console.log(`[HTTP] MCP Server started on port ${PORT}`); - console.log(`[HTTP] Health check: http://localhost:${PORT}/health`); - console.log(`[HTTP] MCP endpoint: http://localhost:${PORT}/mcp`); - logProjectConfig(); + const server = app.listen(PORT, HOST, () => { + log.info(`Listening on http://${HOST}:${PORT}`); + log.info(`Health: http://${HOST}:${PORT}/health · MCP: http://${HOST}:${PORT}/mcp`); + logProjectConfig(log.info, log.warn).catch(error => + log.warn(`Could not read project config: ${String(error)}`) + ); + }); + + server.on("error", (error: NodeJS.ErrnoException) => { + if (error.code === "EADDRINUSE") { + log.error(`Port ${PORT} is already in use. Set PORT to a free port, or stop the other server.`); + } else { + log.error(`Server error: ${error.message}`); + } + process.exit(1); }); - const shutdown = async (): Promise => { - console.log("\n[HTTP] Shutting down server..."); - await sessionManager.closeAll(); + setupGracefulShutdown(server); +} + +function setupGracefulShutdown(server: Server): void { + const shutdown = (): void => { + log.info("Shutting down"); server.close(() => process.exit(0)); }; diff --git a/packages/pluggable-widgets-mcp/src/server/log-project-config.ts b/packages/pluggable-widgets-mcp/src/server/log-project-config.ts new file mode 100644 index 0000000000..4f4261bb4c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/server/log-project-config.ts @@ -0,0 +1,28 @@ +import { getConfiguredProjectDir, validateProjectDir } from "@/config"; + +/** + * Reports the configured Mendix project at startup, or says none is set. + * + * The sinks are injected rather than chosen here because the two transports have different output + * contracts: STDIO must keep everything on stderr, since stdout carries the MCP JSON-RPC stream and + * a stray write corrupts the protocol. Passing them in keeps that distinction visible at the call + * site instead of hidden behind a branch. Tagging is the sink's job, not ours. + */ +export async function logProjectConfig( + info: (message: string) => void, + warn: (message: string) => void +): Promise { + const projectDir = getConfiguredProjectDir(); + + if (!projectDir) { + info("No project configured (set MENDIX_PROJECT_DIR to enable deploy support)"); + return; + } + + const validation = await validateProjectDir(projectDir); + if (validation.valid) { + info(`Project: ${validation.projectName} (${projectDir})`); + } else { + warn(`MENDIX_PROJECT_DIR is set but invalid: ${validation.error}`); + } +} diff --git a/packages/pluggable-widgets-mcp/src/server/routes.ts b/packages/pluggable-widgets-mcp/src/server/routes.ts index ff11a81a2f..74abd8fe94 100644 --- a/packages/pluggable-widgets-mcp/src/server/routes.ts +++ b/packages/pluggable-widgets-mcp/src/server/routes.ts @@ -1,9 +1,10 @@ -import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import type { Express, Request, Response } from "express"; -import { MENDIX_PROJECT_DIR, SERVER_NAME, SERVER_VERSION } from "@/config"; -import { buildIncomingLogEntry, logProtocolMessage } from "./protocol-logger"; +import { getConfiguredProjectDir, SERVER_NAME, SERVER_VERSION } from "@/config"; +import { createLogger } from "@/tools/utils/logger"; import { createMcpServer } from "./server"; -import { sessionManager } from "./session"; + +const log = createLogger("http"); /** * Sets up all routes for the Express application. @@ -15,85 +16,71 @@ export function setupRoutes(app: Express): void { /** * Health check endpoint for monitoring. + * + * Deliberately does not report the project path: this endpoint is unauthenticated, and the absolute + * path of the user's project is not something to hand out. */ function setupHealthRoute(app: Express): void { app.get("/health", (_req: Request, res: Response) => { - const projectDir = MENDIX_PROJECT_DIR ?? null; res.json({ status: "ok", server: SERVER_NAME, version: SERVER_VERSION, - sessions: sessionManager.sessionCount, - projectDir, - widgetsDir: projectDir ? `${projectDir}/widgets` : null + projectConfigured: getConfiguredProjectDir() !== undefined }); }); } /** - * Main MCP endpoint handling session management and request routing. + * The MCP endpoint, served statelessly. + * + * Each POST builds a transport and server, handles the one request, and disposes of both. + * Statelessness is what makes this transport simple enough to trust: the previous session-keeping + * version leaked an `McpServer` on every GET, could never terminate a session (DELETE carries no + * body, so the handler threw before reaching the transport), and therefore grew its session map + * without bound. + * + * STDIO is the transport Studio Pro uses. HTTP exists so the MCP Inspector can be pointed at a + * running server, which needs no cross-request state. */ function setupMcpRoute(app: Express): void { - // Handle CORS preflight explicitly - app.options("/mcp", (_req: Request, res: Response) => { - res.status(204).end(); - }); - - app.all("/mcp", async (req: Request, res: Response) => { - const sessionId = req.headers["mcp-session-id"] as string | undefined; - const requestStart = Date.now(); + app.post("/mcp", async (req: Request, res: Response) => { + const body = req.body as Record; + log.debug(`${String(body?.method ?? "request")}`); - try { - // Case 1: Existing session - reuse transport - if (sessionId && sessionManager.hasSession(sessionId)) { - const body = req.body as Record; - logProtocolMessage(sessionId, buildIncomingLogEntry(sessionId, body)); - console.error( - `[MCP] ${body.method ?? "request"} session=${sessionId} elapsed=${Date.now() - requestStart}ms` - ); - const transport = sessionManager.getTransport(sessionId)!; - await transport.handleRequest(req, res, body); - return; - } + // `sessionIdGenerator: undefined` selects the SDK's stateless mode. + const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); + const server = createMcpServer(); - // Case 2: New session via POST with initialize request - if (req.method === "POST" && !sessionId && isInitializeRequest(req.body)) { - const body = req.body as Record; - const pendingSessionId = "pending-" + Date.now(); - const logEntry = buildIncomingLogEntry(pendingSessionId, body); - console.error( - `[MCP] initialize (new session) protocolVersion=${logEntry.protocolVersion} clientInfo=${JSON.stringify(logEntry.clientInfo)}` - ); - logProtocolMessage(pendingSessionId, logEntry); - const transport = sessionManager.createTransport(); - const server = createMcpServer(); - await server.connect(transport); - await transport.handleRequest(req, res, body); - return; - } - - // Case 3: GET request for SSE - create new session - // StreamableHTTP uses GET for server-to-client event streams - if (req.method === "GET") { - console.error(`[MCP] SSE GET — creating new session`); - const transport = sessionManager.createTransport(); - const server = createMcpServer(); - await server.connect(transport); - await transport.handleRequest(req, res); - return; - } + // Disposal is tied to the response rather than to the await below, so a streamed response + // is not torn down while it is still being written. + res.on("close", () => { + transport.close().catch(error => log.warn(`Transport close failed: ${String(error)}`)); + server.close().catch(error => log.warn(`Server close failed: ${String(error)}`)); + }); - // Case 4: Invalid request - sendJsonRpcError(res, 400, "Bad Request: No valid session ID provided"); + try { + await server.connect(transport); + await transport.handleRequest(req, res, body); } catch (error) { - console.error("[MCP] Route error:", error); - sendJsonRpcError( - res, - 400, - "Invalid session. Send an initialize request without session ID to start a new session." - ); + log.error(`Request failed: ${String(error)}`); + if (!res.headersSent) { + sendJsonRpcError(res, 500, "Internal server error"); + } } }); + + app.options("/mcp", (_req: Request, res: Response) => { + res.status(204).end(); + }); + + // GET (resumable event stream) and DELETE (session termination) are meaningful only for a + // stateful server. Answering 405 states that plainly instead of failing as a bad request. + for (const method of ["get", "delete"] as const) { + app[method]("/mcp", (_req: Request, res: Response) => { + sendJsonRpcError(res, 405, "This server is stateless: use POST /mcp. Sessions are not supported."); + }); + } } /** diff --git a/packages/pluggable-widgets-mcp/src/server/server.ts b/packages/pluggable-widgets-mcp/src/server/server.ts index 47d20dd5c3..f470717dd8 100644 --- a/packages/pluggable-widgets-mcp/src/server/server.ts +++ b/packages/pluggable-widgets-mcp/src/server/server.ts @@ -19,9 +19,10 @@ export function createMcpServer(): McpServer { websiteUrl: SERVER_WEBSITE_URL }, { + // Only capabilities that are actually implemented — advertising `prompts` while + // registering none makes prompts/list look empty rather than unsupported. capabilities: { logging: {}, - prompts: {}, resources: {}, tools: {} }, diff --git a/packages/pluggable-widgets-mcp/src/server/stdio.ts b/packages/pluggable-widgets-mcp/src/server/stdio.ts index c03696d206..9391d679de 100644 --- a/packages/pluggable-widgets-mcp/src/server/stdio.ts +++ b/packages/pluggable-widgets-mcp/src/server/stdio.ts @@ -1,46 +1,58 @@ -import { MENDIX_PROJECT_DIR, validateProjectDir } from "@/config"; -import { createMcpServer } from "./server"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { createLogger } from "@/tools/utils/logger"; +import { logProjectConfig } from "./log-project-config"; +import { createMcpServer } from "./server"; -async function logProjectConfig(): Promise { - if (MENDIX_PROJECT_DIR) { - const validation = await validateProjectDir(MENDIX_PROJECT_DIR); - if (validation.valid) { - console.error(`[STDIO] Project: ${validation.projectName} (${MENDIX_PROJECT_DIR})`); - } else { - console.error(`[STDIO] Warning: MENDIX_PROJECT_DIR is set but invalid: ${validation.error}`); - } - } else { - console.error(`[STDIO] No project configured (set MENDIX_PROJECT_DIR to enable deploy support)`); - } -} +const log = createLogger("stdio"); /** - * Starts the MCP server with STDIO transport. - * Communicates via stdin/stdout for CLI-based MCP clients. + * Starts the MCP server over STDIO — the transport Studio Pro spawns. + * + * stdout carries the JSON-RPC stream, so nothing here may write to it. All logging goes to stderr, + * which the parent process captures. */ export async function startStdioServer(): Promise { const server = createMcpServer(); const transport = new StdioServerTransport(); - // Log to stderr since stdout is used for MCP communication - console.error("[STDIO] Starting MCP server..."); - await logProjectConfig(); + log.info("Starting"); + await logProjectConfig(log.info, log.warn); await server.connect(transport); - console.error("[STDIO] MCP server connected and ready"); + log.info("Connected and ready"); setupGracefulShutdown(transport); } function setupGracefulShutdown(transport: StdioServerTransport): void { - const shutdown = async (): Promise => { - console.error("\n[STDIO] Shutting down server..."); + let closing = false; + + const shutdown = async (reason: string): Promise => { + if (closing) { + return; + } + closing = true; + log.info(`Shutting down (${reason})`); await transport.close(); process.exit(0); }; - process.on("SIGINT", shutdown); - process.on("SIGTERM", shutdown); + const on = (event: string, reason: string, target: NodeJS.EventEmitter): void => { + target.on(event, () => { + shutdown(reason).catch(error => { + log.error(`Shutdown failed: ${String(error)}`); + process.exit(1); + }); + }); + }; + + on("SIGINT", "SIGINT", process); + on("SIGTERM", "SIGTERM", process); + + // The parent closing our stdin is how a stdio child learns the host is gone. Without this the + // process outlives Studio Pro — signals alone are not enough, and on Windows SIGTERM/SIGINT are + // not delivered the way POSIX code expects. + on("end", "stdin closed", process.stdin); + on("close", "stdin closed", process.stdin); } diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/logger.ts b/packages/pluggable-widgets-mcp/src/tools/utils/logger.ts new file mode 100644 index 0000000000..84ef686c1d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/utils/logger.ts @@ -0,0 +1,48 @@ +/** + * Diagnostic logging for the server itself. + * + * Everything goes to **stderr**, never stdout: under the STDIO transport stdout carries the MCP + * JSON-RPC stream, and a single stray write corrupts the protocol. Studio Pro captures the child + * process's stderr, so that stream is the support log — there is no log file to manage. + * + * This is distinct from `notifications.ts`, which sends `notifications/message` to the *client*. + * Rule of thumb: if the model or the user should see it, it is a notification; if it exists to + * explain the server's own behaviour after the fact, it is a log. + */ + +export type LogLevel = "debug" | "info" | "warn" | "error"; + +const LEVEL_ORDER: Record = { debug: 10, info: 20, warn: 30, error: 40 }; + +/** `MCP_LOG_LEVEL=debug` to see everything; defaults to info. */ +function configuredLevel(): number { + const configured = process.env.MCP_LOG_LEVEL as LogLevel | undefined; + return LEVEL_ORDER[configured ?? "info"] ?? LEVEL_ORDER.info; +} + +export interface Logger { + debug(message: string): void; + info(message: string): void; + warn(message: string): void; + error(message: string): void; +} + +/** + * Creates a logger tagged with its subsystem, e.g. `createLogger("build")` emits `[build] …`. + */ +export function createLogger(tag: string): Logger { + const write = (level: LogLevel, message: string): void => { + if (LEVEL_ORDER[level] < configuredLevel()) { + return; + } + // console.error writes to stderr for every level — the level is a filter, not a stream. + console.error(level === "info" ? `[${tag}] ${message}` : `[${tag}:${level}] ${message}`); + }; + + return { + debug: message => write("debug", message), + info: message => write("info", message), + warn: message => write("warn", message), + error: message => write("error", message) + }; +} From 701cd1c56c3dc393fd161f86c239ed633ff48b75 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:35:13 +0200 Subject: [PATCH 29/36] feat(tools): replace the code-generation pair with set-widget-properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten tools become nine. generate-widget-code and update-widget-properties merge into one declarative tool: callers send the complete property set the widget should have, not a diff. The op union and the .widget-definition.json snapshot that existed only to give the diff a base are both gone, and with them the class of bug where the snapshot and the XML disagree. The Mendix property-type union was written out three times with no compile-time link between the copies, and had already drifted. It now lives once in tools/property-schema.ts, which also exposes the nested `properties` field that xml-generator has always handled but neither Zod schema reached. The schema stays under tools/ because generators/ is deliberately Zod-free; validating untrusted input is a tool-boundary concern. validateWidgetDefinition now rejects duplicate property keys. Duplicates produced an XML file Mendix rejects at build time and colliding entries in the generated typings. Deletes cleanupScaffoldFiles: 93 lines of destruction that neither the tool description nor the success message disclosed. It unlinked every .xml in src/ that did not match, and every top-level .tsx that did not match — so a user's src/Helper.tsx vanished while src/components/ survived — with uncaught unlink errors, so an EPERM mid-loop aborted after a partial delete. It existed to clean up after the generator's own renaming; with the model writing TSX under the scaffolded name, nothing renames. build-widget: - Success is the child's exit code. It used to be true when the output contained "successfully" or "created dist/" or any .mpk token, and mpkPath was taken from any line mentioning .mpk, including error lines, which then fed the success heuristic. parseBuildOutput now returns Omit so the compiler enforces that parsing does not decide success. - Adds a timeout. runBuild resolved only on close or error, so a hung build hung the MCP request forever while the heartbeat fired indefinitely. - Drops shell: true. The argv is fixed and it handed widgetPath to a shell. - Stops inlining file contents on failure. Errors return file:line:column; the model has read-widget-file. Scaffolding defaults to {projectDir}/widget-sources/ and drops the ERR_OUTPUT_PATH_REQUIRED workaround that only existed because of the cwd bug. An existing directory is now verified to be this widget before it is reported as already scaffolded. findMpkFile picks the newest .mpk by mtime rather than the first one found, so deploy-widget cannot copy a stale build, and deploy reports whether it replaced an existing file. Tool descriptions say what the tool does and what it returns, nothing about what to call next. The workflow lives once in SERVER_INSTRUCTIONS. The "RETRY LOOP, maximum 3 attempts" prose is gone: the server cannot count attempts, and in MCP the client owns the loop. --- .../src/generators/xml-generator.ts | 11 + .../src/tools/__tests__/build.tools.test.ts | 48 ++-- .../__tests__/file-operations.tools.test.ts | 143 ++++++++++ .../src/tools/__tests__/project.tools.test.ts | 4 +- .../tools/__tests__/scaffolding.tools.test.ts | 41 ++- .../__tests__/widget-properties.tools.test.ts | 134 ++++++++++ .../src/tools/build.tools.ts | 200 ++++++-------- .../src/tools/file-operations.tools.ts | 64 +++-- .../pluggable-widgets-mcp/src/tools/index.ts | 22 +- .../src/tools/project.tools.ts | 143 ++++------ .../src/tools/property-schema.ts | 103 ++++++++ .../src/tools/scaffolding.tools.ts | 247 ++++++++---------- .../src/tools/utils/mpk.ts | 45 ++-- .../src/tools/widget-properties.tools.ts | 149 +++++++++++ 14 files changed, 910 insertions(+), 444 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/file-operations.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/__tests__/widget-properties.tools.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/property-schema.ts create mode 100644 packages/pluggable-widgets-mcp/src/tools/widget-properties.tools.ts diff --git a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts index 4cc002c31f..5a3c3976d3 100644 --- a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts @@ -233,6 +233,17 @@ export function validateWidgetDefinition(widget: WidgetDefinition): string[] { errors.push("Widget must have at least one property"); } + // Duplicate keys produce an XML file Mendix rejects at build time, and colliding entries in the + // generated typings. The check lives here rather than in a tool because it is a property of the + // definition itself. + const seenKeys = new Set(); + for (const prop of widget.properties ?? []) { + if (prop.key && seenKeys.has(prop.key)) { + errors.push(`Duplicate property key "${prop.key}" — each key must be unique`); + } + seenKeys.add(prop.key); + } + for (const prop of widget.properties ?? []) { if (!prop.key || prop.key.trim() === "") { errors.push("Property key is required"); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts index 2619da62e6..e97f697999 100644 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/build.tools.test.ts @@ -35,7 +35,7 @@ describe("build-widget sandbox expansion", () => { }); const text = getResultText(result); expect(isError(result)).toBe(true); - expect(text).toContain("not within an allowed directory"); + expect(text).toContain("ERR_OUTPUT_PATH_INVALID"); }); it("allows widget path within state.projectDir", async () => { @@ -54,7 +54,7 @@ describe("build-widget sandbox expansion", () => { }); const text = getResultText(result); // Path check passed — build itself will fail (no real widget), but NOT with sandbox error - expect(text).not.toContain("not within an allowed directory"); + expect(text).not.toContain("ERR_OUTPUT_PATH_INVALID"); }); }); @@ -86,16 +86,15 @@ describe("formatBuildFailureResponse", () => { `export function preview(props: CounterPreviewProps) {\n return
[Counter]
;\n}\n` ); - const response = await formatBuildFailureResponse(errors, tmpDir); + const response = formatBuildFailureResponse(errors); expect(response).toContain("TS6133"); expect(response).toContain("'props' is declared but its value is never read."); expect(response).toContain("src/Counter.editorPreview.tsx"); - expect(response).toContain("line 4"); - expect(response).toContain("col 25"); + expect(response).toContain("src/Counter.editorPreview.tsx:4:25"); }); - it("embeds content of failing source files in the response", async () => { + it("does not embed file contents — the caller reads what it needs", async () => { const fileContent = `export function preview(props: CounterPreviewProps) {\n return
[Counter]
;\n}\n`; writeFileSync(join(tmpDir, "src/Counter.editorPreview.tsx"), fileContent); @@ -110,29 +109,12 @@ describe("formatBuildFailureResponse", () => { } ]; - const response = await formatBuildFailureResponse(errors, tmpDir); + const response = formatBuildFailureResponse(errors); - expect(response).toContain("export function preview"); - // The separator format "--- ---" is the required output format for this function - expect(response).toContain("--- src/Counter.editorPreview.tsx ---"); - }); - - it("skips file embed when file does not exist on disk", async () => { - const errors = [ - { - category: "typescript" as const, - tsCode: "TS2339", - message: "Property 'x' does not exist on type 'Y'.", - file: "src/Nonexistent.tsx", - line: 10, - column: 5 - } - ]; - - const response = await formatBuildFailureResponse(errors, tmpDir); - - expect(response).toContain("TS2339"); - expect(response).not.toContain("--- src/Nonexistent.tsx ---"); + // The location is enough; embedding whole files was unbounded. + expect(response).toContain("src/Counter.editorPreview.tsx:4:25"); + expect(response).not.toContain("export function preview"); + expect(response).toContain("read-widget-file"); }); it("handles errors with no file location gracefully", async () => { @@ -143,10 +125,10 @@ describe("formatBuildFailureResponse", () => { } ]; - const response = await formatBuildFailureResponse(errors, tmpDir); + const response = formatBuildFailureResponse(errors); expect(response).toContain("Build failed with exit code 1"); - expect(response).not.toContain("--- "); + expect(response).toContain("Build failed with 1 error(s)"); }); }); @@ -163,13 +145,13 @@ describe("formatBuildSuccessResponse", () => { const mpkPath = "/tmp/my-widget/dist/MyWidget.mpk"; const result = formatBuildSuccessResponse(mpkPath, widgetPath, []); expect(result).toContain(mpkPath); - expect(result).toContain("MPK output"); + expect(result).toContain("Output:"); }); it("includes next step even without MPK path", () => { const result = formatBuildSuccessResponse(undefined, widgetPath, []); - expect(result).toContain("Next step"); - expect(result).not.toContain("MPK output"); + expect(result).toContain("Next: deploy-widget"); + expect(result).not.toContain("Output:"); }); it("includes warnings when present", () => { diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/file-operations.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/file-operations.tools.test.ts new file mode 100644 index 0000000000..c23d94109d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/file-operations.tools.test.ts @@ -0,0 +1,143 @@ +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; +import { registerFileOperationTools } from "@/tools/file-operations.tools"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; + +describe("file operation tools", () => { + let client: Client; + let cleanup: () => Promise; + let widget: string; + + beforeEach(async () => { + ({ client, cleanup } = await createMcpTestContext(registerFileOperationTools)); + widget = mkdtempSync(join(tmpdir(), "file-ops-")); + mkdirSync(join(widget, "src"), { recursive: true }); + }); + + afterEach(async () => { + await cleanup(); + rmSync(widget, { recursive: true, force: true }); + }); + + describe("list-widget-files", () => { + it("lists files relative to the widget directory", async () => { + writeFileSync(join(widget, "src", "Widget.tsx"), "x"); + writeFileSync(join(widget, "package.json"), "{}"); + + const text = getResultText( + await client.callTool({ name: "list-widget-files", arguments: { widgetPath: widget } }) + ); + + expect(text).toContain(join("src", "Widget.tsx")); + expect(text).toContain("package.json"); + }); + + it("skips node_modules and build output", async () => { + mkdirSync(join(widget, "node_modules", "pkg"), { recursive: true }); + writeFileSync(join(widget, "node_modules", "pkg", "index.js"), "x"); + mkdirSync(join(widget, "dist"), { recursive: true }); + writeFileSync(join(widget, "dist", "bundle.js"), "x"); + + const text = getResultText( + await client.callTool({ name: "list-widget-files", arguments: { widgetPath: widget } }) + ); + + expect(text).not.toContain("node_modules"); + expect(text).not.toContain("bundle.js"); + }); + + it("fails for a path that is not a directory", async () => { + const file = join(widget, "src", "Widget.tsx"); + writeFileSync(file, "x"); + + const result = await client.callTool({ name: "list-widget-files", arguments: { widgetPath: file } }); + expect(isError(result)).toBe(true); + }); + }); + + describe("read-widget-file", () => { + it("returns file content", async () => { + writeFileSync(join(widget, "src", "Widget.tsx"), "export const x = 1;"); + + const text = getResultText( + await client.callTool({ + name: "read-widget-file", + arguments: { widgetPath: widget, filePath: "src/Widget.tsx" } + }) + ); + + expect(text).toContain("export const x = 1;"); + }); + + it("refuses to read outside the widget directory", async () => { + const result = await client.callTool({ + name: "read-widget-file", + arguments: { widgetPath: widget, filePath: "../../../etc/passwd" } + }); + + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_FILE_READ"); + }); + }); + + describe("write-widget-file", () => { + it("writes a single file and creates missing parent directories", async () => { + const result = await client.callTool({ + name: "write-widget-file", + arguments: { + widgetPath: widget, + filePath: "src/components/Nested.tsx", + content: "export const nested = true;" + } + }); + + expect(isError(result)).toBe(false); + expect(readFileSync(join(widget, "src", "components", "Nested.tsx"), "utf-8")).toContain("nested"); + }); + + it("writes a batch of files", async () => { + await client.callTool({ + name: "write-widget-file", + arguments: { + widgetPath: widget, + files: [ + { relativePath: "src/A.tsx", content: "a" }, + { relativePath: "src/B.tsx", content: "b" } + ] + } + }); + + expect(readFileSync(join(widget, "src", "A.tsx"), "utf-8")).toBe("a"); + expect(readFileSync(join(widget, "src", "B.tsx"), "utf-8")).toBe("b"); + }); + + it("rejects a disallowed extension, writing nothing", async () => { + const result = await client.callTool({ + name: "write-widget-file", + arguments: { widgetPath: widget, filePath: "src/evil.exe", content: "x" } + }); + + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("ERR_FILE_WRITE"); + }); + + it("validates every path before writing any of them", async () => { + // The second entry is invalid; the first must not reach disk. + await client.callTool({ + name: "write-widget-file", + arguments: { + widgetPath: widget, + files: [ + { relativePath: "src/Good.tsx", content: "good" }, + { relativePath: "../escape.tsx", content: "bad" } + ] + } + }); + + expect(() => readFileSync(join(widget, "src", "Good.tsx"), "utf-8")).toThrow(); + }); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts index f4fdecd7b3..abd2b6cccd 100644 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/project.tools.test.ts @@ -178,7 +178,7 @@ describe("deploy-widget", () => { arguments: { widgetPath: widgetDir } }); expect(isError(result)).toBe(false); - expect(getResultText(result)).toContain("deployed"); + expect(getResultText(result)).toContain("Deployed"); expect(existsSync(join(projectDir, "widgets", "Cool.mpk"))).toBe(true); }); @@ -215,6 +215,6 @@ describe("deploy-widget", () => { arguments: { widgetPath: rogueDir } }); expect(isError(result)).toBe(true); - expect(getResultText(result)).toContain("not within an allowed directory"); + expect(getResultText(result)).toContain("ERR_OUTPUT_PATH_INVALID"); }); }); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts index e28f837d3e..1e15e95781 100644 --- a/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/scaffolding.tools.test.ts @@ -1,10 +1,14 @@ +import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createMcpTestContext, getResultText } from "@/__test-utils__/mcp-test-harness"; import { createTempMendixProject } from "@/__test-utils__/temp-dir"; import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; import type { SessionState } from "@/tools/session-state"; -// Mock the generator so the tool returns immediately after path validation +// Mock the generator so the tool returns immediately after path validation. +// These are plain functions rather than vi.fn(): the suite runs with `restoreMocks: true`, which +// resets a factory-created vi.fn() after the first test and would leave later tests with a stub +// returning undefined. vi.mock("@/tools/utils/generator", () => ({ buildWidgetOptions: (args: Record) => ({ name: args.name ?? "TestWidget", @@ -18,7 +22,9 @@ vi.mock("@/tools/utils/generator", () => ({ unitTests: true, e2eTests: false }), - runWidgetGenerator: vi.fn().mockResolvedValue(undefined), + runWidgetGenerator: () => Promise.resolve({ askedFor: [] }), + runNpmInstall: () => Promise.resolve({ ok: true }), + ScaffoldTimeoutError: class ScaffoldTimeoutError extends Error {}, SCAFFOLD_PROGRESS: { START: { progress: 0, message: "Starting..." }, COMPLETE: { progress: 100, message: "Done!" } @@ -42,8 +48,20 @@ describe("create-widget sandbox expansion", () => { tempCleanups.length = 0; }); - it("rejects outputPath outside all allowed directories", async () => { + it("refuses to scaffold when no project is configured", async () => { state.projectDir = undefined; + const result = await client.callTool({ + name: "create-widget", + arguments: { name: "TestWidget", description: "test" } + }); + expect(getResultText(result)).toContain("ERR_PROJECT_NOT_CONFIGURED"); + }); + + it("rejects an outputPath outside the project directory", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + const result = await client.callTool({ name: "create-widget", arguments: { @@ -52,8 +70,19 @@ describe("create-widget sandbox expansion", () => { outputPath: "/tmp/evil-path" } }); - const text = getResultText(result); - expect(text).toContain("ERR_OUTPUT_PATH_INVALID"); + expect(getResultText(result)).toContain("ERR_OUTPUT_PATH_INVALID"); + }); + + it("defaults the output to widget-sources inside the project", async () => { + const { dir, cleanup: tempCleanup } = createTempMendixProject(); + tempCleanups.push(tempCleanup); + state.projectDir = dir; + + const result = await client.callTool({ + name: "create-widget", + arguments: { name: "TestWidget", description: "test" } + }); + expect(getResultText(result)).toContain(join(dir, "widget-sources", "testWidget")); }); it("allows outputPath within state.projectDir", async () => { @@ -72,6 +101,6 @@ describe("create-widget sandbox expansion", () => { const text = getResultText(result); // Path check passed — the mocked generator runs instantly. expect(text).not.toContain("ERR_OUTPUT_PATH_INVALID"); - expect(text).toContain("created successfully"); + expect(text).toContain(`Created widget "TestWidget"`); }); }); diff --git a/packages/pluggable-widgets-mcp/src/tools/__tests__/widget-properties.tools.test.ts b/packages/pluggable-widgets-mcp/src/tools/__tests__/widget-properties.tools.test.ts new file mode 100644 index 0000000000..72ba247e21 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/__tests__/widget-properties.tools.test.ts @@ -0,0 +1,134 @@ +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; +import { registerWidgetPropertiesTools } from "@/tools/widget-properties.tools"; +import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; + +/** Creates a scaffolded-looking widget directory with the given package.json widgetName. */ +function createWidgetDir(widgetName?: string): string { + const dir = mkdtempSync(join(tmpdir(), "widget-props-")); + mkdirSync(join(dir, "src"), { recursive: true }); + if (widgetName) { + writeFileSync(join(dir, "package.json"), JSON.stringify({ widgetName })); + } + return dir; +} + +describe("set-widget-properties", () => { + let client: Client; + let cleanup: () => Promise; + const dirs: string[] = []; + + beforeEach(async () => { + ({ client, cleanup } = await createMcpTestContext(registerWidgetPropertiesTools)); + }); + + afterEach(async () => { + await cleanup(); + for (const dir of dirs) rmSync(dir, { recursive: true, force: true }); + dirs.length = 0; + }); + + it("writes the widget XML from the property model", async () => { + const dir = createWidgetDir("Counter"); + dirs.push(dir); + + const result = await client.callTool({ + name: "set-widget-properties", + arguments: { + widgetPath: dir, + description: "a counter", + properties: [ + { key: "value", type: "attribute", caption: "Value", attributeTypes: ["Integer"] }, + { key: "onIncrement", type: "action", caption: "On Increment" } + ] + } + }); + + expect(isError(result)).toBe(false); + + const xml = readFileSync(join(dir, "src", "Counter.xml"), "utf-8"); + expect(xml).toContain('key="value"'); + expect(xml).toContain('type="attribute"'); + expect(xml).toContain('key="onIncrement"'); + expect(xml).toContain(' { + // The directory is a temp name like widget-props-xxxx, which would fail PascalCase + // validation if it were used as the widget name. + const dir = createWidgetDir("Badge"); + dirs.push(dir); + + await client.callTool({ + name: "set-widget-properties", + arguments: { + widgetPath: dir, + description: "a badge", + properties: [{ key: "label", type: "textTemplate", caption: "Label" }] + } + }); + + expect(readFileSync(join(dir, "src", "Badge.xml"), "utf-8")).toContain('key="label"'); + }); + + it("is declarative — a second call replaces the previous property set", async () => { + const dir = createWidgetDir("Counter"); + dirs.push(dir); + + const call = (properties: unknown[]): Promise => + client.callTool({ + name: "set-widget-properties", + arguments: { widgetPath: dir, description: "a counter", properties } + }); + + await call([ + { key: "first", type: "string", caption: "First" }, + { key: "second", type: "string", caption: "Second" } + ]); + await call([{ key: "second", type: "string", caption: "Second" }]); + + const xml = readFileSync(join(dir, "src", "Counter.xml"), "utf-8"); + expect(xml).toContain('key="second"'); + expect(xml).not.toContain('key="first"'); + }); + + it("rejects an invalid definition without writing anything", async () => { + const dir = createWidgetDir("Counter"); + dirs.push(dir); + + const result = await client.callTool({ + name: "set-widget-properties", + arguments: { + widgetPath: dir, + description: "duplicate keys", + properties: [ + { key: "value", type: "string", caption: "One" }, + { key: "value", type: "string", caption: "Two" } + ] + } + }); + + expect(isError(result)).toBe(true); + expect(getResultText(result)).toContain("nothing was written"); + expect(() => readFileSync(join(dir, "src", "Counter.xml"), "utf-8")).toThrow(); + }); + + it("rejects a property key that is not camelCase", async () => { + const dir = createWidgetDir("Counter"); + dirs.push(dir); + + const result = await client.callTool({ + name: "set-widget-properties", + arguments: { + widgetPath: dir, + description: "bad key", + properties: [{ key: "Value", type: "string", caption: "Value" }] + } + }); + + expect(isError(result)).toBe(true); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts index 4f8d2643c9..e735fe16f8 100644 --- a/packages/pluggable-widgets-mcp/src/tools/build.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/build.tools.ts @@ -6,15 +6,15 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; -import { readFile } from "node:fs/promises"; -import { join, normalize, sep } from "node:path"; +import { join } from "node:path"; import { z } from "zod"; -import { GENERATIONS_DIR } from "@/config"; + +import { BUILD_TIMEOUT_MS } from "@/config"; import type { ToolContext, ToolResponse } from "./types"; import { ProgressTracker } from "./utils/progress-tracker"; -import { createStructuredError, createStructuredErrorResponse, createToolResponse } from "./utils/response"; +import { fail, ok } from "./utils/response"; import { findMpkFile } from "./utils/mpk"; -import { isPathAllowed } from "./utils/sandbox"; +import { describeAllowedRoots, isPathAllowed } from "./utils/sandbox"; import type { SessionState } from "./session-state"; /** @@ -123,9 +123,15 @@ function parseTypeScriptError(line: string): ParsedError | null { } /** - * Parses the build output to extract meaningful errors and warnings. + * Extracts errors, warnings and the .mpk path from build output. + * + * Deliberately does NOT decide whether the build succeeded — that is the child process's exit code + * to report, and the return type omits `success` so the compiler enforces it. The previous version + * declared success whenever the output merely contained "successfully", "created dist/", or any + * token ending in `.mpk`, which meant a build that printed a stale artifact path and then exited + * non-zero was reported as green. */ -function parseBuildOutput(stdout: string, stderr: string): BuildResult { +function parseBuildOutput(stdout: string, stderr: string): Omit { const output = stdout + "\n" + stderr; const errors: ParsedError[] = []; const warnings: string[] = []; @@ -201,94 +207,54 @@ function parseBuildOutput(stdout: string, stderr: string): BuildResult { } } - // Check for success indicators - // pluggable-widgets-tools outputs "created dist/..." when successful - const hasCreatedOutput = output.includes("created dist/") || output.includes("created dist\\"); - const success = - errors.length === 0 && - (output.includes("Build completed") || output.includes("successfully") || hasCreatedOutput || mpkPath); - - return { - success: !!success, - mpkPath, - errors, - warnings, - output - }; + return { mpkPath, errors, warnings, output }; } /** - * Formats a build failure response for Maia, including: - * - All errors with file/line/column/code - * - Content of every source file that appears in the error list + * Formats a build failure response. * - * Embedding file content lets Maia fix errors without an extra read-widget-file - * round-trip. Output format is designed to be read by an AI agent. + * Errors carry file:line:col so the caller can read exactly what it needs. The file contents + * themselves are deliberately NOT embedded: they were unbounded — every file named by any error, + * in full — and `read-widget-file` already exists for the cases where the model wants the source. */ -export async function formatBuildFailureResponse(errors: ParsedError[], widgetPath: string): Promise { - // Format error list — each error gets code, location (file line N col N), and message - const errorLines = errors.map(e => { - const loc = e.file - ? `${e.file}${e.line != null ? ` line ${e.line}` : ""}${e.column != null ? ` col ${e.column}` : ""}` +export function formatBuildFailureResponse(errors: ParsedError[]): string { + const errorLines = errors.map(error => { + const location = error.file + ? `${error.file}${error.line != null ? `:${error.line}` : ""}${error.column != null ? `:${error.column}` : ""}` : null; - const code = e.tsCode ? `[${e.tsCode}]` : `[${e.category}]`; - const locStr = loc ? ` ${loc} —` : ""; - return ` ${code}${locStr} ${e.message}`; + const code = error.tsCode ? `[${error.tsCode}]` : `[${error.category}]`; + return ` ${code}${location ? ` ${location} —` : ""} ${error.message}`; }); - // Collect unique source files that appear in errors - const uniqueFiles = [...new Set(errors.map(e => e.file).filter((f): f is string => !!f))]; - - // Read each failing file (skip if not found — don't throw) - const fileSections: string[] = []; - for (const relPath of uniqueFiles) { - // Block path traversal: only allow files under widgetPath - const normalizedBase = normalize(widgetPath); - const fullPath = normalize(join(widgetPath, relPath)); - if (!fullPath.startsWith(normalizedBase + sep) && fullPath !== normalizedBase) continue; - if (!existsSync(fullPath)) continue; - - try { - const content = await readFile(fullPath, "utf-8"); - fileSections.push(`--- ${relPath} ---\n${content}`); - } catch { - // Skip unreadable files silently - } - } - - const lines = [ - `❌ Build failed — ${errors.length} error(s). Fix the errors below, write with write-widget-file, then retry build-widget (max 3 attempts total).`, + return [ + `Build failed with ${errors.length} error(s).`, "", "Errors:", - ...errorLines - ]; - - if (fileSections.length > 0) { - lines.push("", "Failing file contents:", ""); - lines.push(...fileSections); - } - - return lines.join("\n"); + ...errorLines, + "", + "Read the affected files with read-widget-file, fix them with write-widget-file, then build again." + ].join("\n"); } /** - * Formats a successful build response, including MPK path, warnings, and a - * chaining instruction to call deploy-widget next. + * Formats a successful build response. */ export function formatBuildSuccessResponse( mpkPath: string | undefined, widgetPath: string, warnings: string[] ): string { - let message = "✅ Build successful!"; + const lines = ["Build succeeded."]; + if (mpkPath) { - message += `\n\n📦 MPK output: ${mpkPath}`; + lines.push("", `Output: ${mpkPath}`); } if (warnings.length > 0) { - message += `\n\n⚠️ Warnings:\n${warnings.map(w => ` - ${w}`).join("\n")}`; + lines.push("", "Warnings:", ...warnings.map(warning => ` - ${warning}`)); } - message += `\n\n🚀 Next step: Call deploy-widget with widgetPath: "${widgetPath}" to copy the .mpk to your Mendix project's widgets/ directory.`; - return message; + lines.push("", `Next: deploy-widget with widgetPath "${widgetPath}".`); + + return lines.join("\n"); } /** @@ -314,7 +280,6 @@ async function runBuild(widgetPath: string, tracker?: ProgressTracker): Promise< // Use npm run build to run pluggable-widgets-tools (correct package name) const buildProcess = spawn("npm", ["run", "build"], { cwd: widgetPath, - shell: true, env: { ...globalThis.process.env, FORCE_COLOR: "0" // Disable colors for easier parsing @@ -324,6 +289,24 @@ async function runBuild(widgetPath: string, tracker?: ProgressTracker): Promise< let stdout = ""; let stderr = ""; + // Without this the request hangs forever on a stalled build (registry stall, watch mode + // misconfiguration) while the progress heartbeat keeps firing. + const timer = setTimeout(() => { + buildProcess.kill(); + tracker?.error(`Build timed out after ${BUILD_TIMEOUT_MS / 1000}s`); + resolve({ + success: false, + errors: [ + { + message: `Build timed out after ${BUILD_TIMEOUT_MS / 1000}s and was terminated.`, + category: "unknown" + } + ], + warnings: [], + output: stdout + stderr + }); + }, BUILD_TIMEOUT_MS); + buildProcess.stdout?.on("data", (data: Buffer) => { const chunk = data.toString(); stdout += chunk; @@ -351,17 +334,19 @@ async function runBuild(widgetPath: string, tracker?: ProgressTracker): Promise< }); buildProcess.on("close", code => { - const result = parseBuildOutput(stdout, stderr); + // Exit code is the signal a child process is designed to communicate success with; + // parsed output only enriches the message. It can no longer flip a red exit to green. + const result: BuildResult = { ...parseBuildOutput(stdout, stderr), success: code === 0 }; - // If exit code is non-zero and we didn't detect errors, add generic error - if (code !== 0 && result.errors.length === 0) { + if (!result.success && result.errors.length === 0) { result.errors.push({ message: `Build failed with exit code ${code}`, category: "unknown" }); - result.success = false; } + clearTimeout(timer); + // Report completion if (tracker) { if (result.success) { @@ -376,6 +361,7 @@ async function runBuild(widgetPath: string, tracker?: ProgressTracker): Promise< }); buildProcess.on("error", err => { + clearTimeout(timer); tracker?.error(`Failed to start build: ${err.message}`); resolve({ success: false, @@ -399,30 +385,24 @@ async function handleBuildWidget( // Validate path exists if (!existsSync(widgetPath)) { - return createStructuredErrorResponse( - createStructuredError("ERR_NOT_FOUND", `Widget directory not found: ${widgetPath}`, { - suggestion: "Verify the widget path is correct and the directory exists." - }) - ); + return fail("ERR_NOT_FOUND", `Widget directory not found: ${widgetPath}`, { + suggestion: "Verify the widget path is correct and the directory exists." + }); } // Validate path is within allowed directories - if (!isPathAllowed(widgetPath, state, "MCP_ALLOWED_BUILD_PATHS")) { - return createStructuredErrorResponse( - createStructuredError("ERR_NOT_FOUND", `Widget path is not within an allowed directory: ${widgetPath}`, { - suggestion: `Widget must be within ${GENERATIONS_DIR} or set MCP_ALLOWED_BUILD_PATHS env var (colon-separated paths).` - }) - ); + if (!isPathAllowed(widgetPath, state)) { + return fail("ERR_OUTPUT_PATH_INVALID", `Widget path is outside the project: ${widgetPath}`, { + suggestion: `Allowed roots: ${describeAllowedRoots(state)}.` + }); } // Check for package.json const packageJsonPath = join(widgetPath, "package.json"); if (!existsSync(packageJsonPath)) { - return createStructuredErrorResponse( - createStructuredError("ERR_NOT_FOUND", `No package.json found in ${widgetPath}`, { - suggestion: "Ensure this is a valid widget directory created with create-widget tool." - }) - ); + return fail("ERR_NOT_FOUND", `No package.json found in ${widgetPath}`, { + suggestion: "Point at a widget directory created by create-widget." + }); } // Create progress tracker @@ -442,20 +422,18 @@ async function handleBuildWidget( const mpkPath = result.mpkPath || findMpkFile(widgetPath); if (result.success) { - return createToolResponse(formatBuildSuccessResponse(mpkPath, widgetPath, result.warnings)); + return ok(formatBuildSuccessResponse(mpkPath, widgetPath, result.warnings)); } else { if (result.errors.length > 0) { - const message = await formatBuildFailureResponse(result.errors, widgetPath); - return { content: [{ type: "text", text: message }], isError: true }; + // Was a raw object literal here, bypassing the response constructors entirely. + return fail("ERR_BUILD_FAILED", formatBuildFailureResponse(result.errors)); } // Fallback for unknown failures (no structured errors detected) - return createStructuredErrorResponse( - createStructuredError("ERR_BUILD_UNKNOWN", "Build failed with unknown error", { - suggestion: "Check the raw build output for details.", - rawOutput: result.output.slice(0, 1000) - }) - ); + return fail("ERR_BUILD_FAILED", "Build failed without producing a parseable error", { + suggestion: "Check the raw build output below.", + details: result.output + }); } } finally { tracker.stop(); @@ -470,18 +448,14 @@ export function registerBuildTools(server: McpServer, state: SessionState): void "build-widget", { title: "Build Widget", + // Says what the tool does and what it returns — nothing more. The retry policy that + // used to live here ("maximum 3 total attempts") was unenforceable: no counter existed, + // and in MCP the client owns the loop. Sequencing belongs in SERVER_INSTRUCTIONS, where + // it is stated once instead of duplicated across tool descriptions and response bodies. description: - "Builds a Mendix pluggable widget using pluggable-widget-tools. " + - "Validates XML, compiles TypeScript, generates types, and produces an .mpk file. " + - "If the build fails with TypeScript errors, the response includes ALL errors with " + - "file locations AND the content of every failing source file. " + - "RETRY LOOP: On failure, (1) read the errors and embedded file content, " + - "(2) fix the TypeScript errors, (3) write the fixed files using write-widget-file, " + - "(4) call build-widget again. Repeat until the build passes. " + - "Maximum 3 total attempts — if still failing after 3 attempts, " + - "report the errors and file contents to the user. " + - "SUCCESS: When the build succeeds, you MUST call deploy-widget next with the same widgetPath " + - "to copy the .mpk to the Mendix project. Do not stop after a successful build.", + "Compiles a Mendix pluggable widget to an .mpk using pluggable-widgets-tools. " + + "Returns the .mpk path on success, or the TypeScript and XML errors with " + + "file:line:column on failure.", inputSchema: buildWidgetSchema }, (args, context) => handleBuildWidget(args, context, state) diff --git a/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts index b3aec8856d..d197022f94 100644 --- a/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/file-operations.tools.ts @@ -1,10 +1,13 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises"; -import { dirname, extname, join } from "node:path"; +import { dirname, extname, join, relative } from "node:path"; import { z } from "zod"; import { ALLOWED_EXTENSIONS, validateFilePath } from "@/security"; import type { ToolResponse } from "@/tools/types"; -import { createErrorResponse, createToolResponse } from "@/tools/utils/response"; +import { fail, ok } from "@/tools/utils/response"; +import { createLogger } from "@/tools/utils/logger"; + +const log = createLogger("file-operations"); // ============================================================================= // Schemas @@ -69,7 +72,9 @@ async function listFilesRecursive( for (const entry of entries) { const fullPath = join(dir, entry.name); - const relativePath = fullPath.replace(basePath + "/", ""); + // path.relative, not string replace: `replace` strips the first occurrence anywhere in the + // string rather than an anchored prefix, and hardcodes "/" as the separator. + const relativePath = relative(basePath, fullPath); if (entry.isDirectory()) { // Skip node_modules and other common non-source directories @@ -94,7 +99,7 @@ async function handleListWidgetFiles(args: ListWidgetFilesInput): Promise !r.success); if (failed.length === 0) { - return createToolResponse( - [`Successfully wrote ${successful.length} file(s):`, "", ...successful.map(r => ` - ${r.path}`)].join("\n") - ); - } else if (successful.length === 0) { - return createErrorResponse( - [`Failed to write all ${failed.length} file(s):`, "", ...failed.map(r => ` - ${r.path}: ${r.error}`)].join( - "\n" - ) - ); - } else { - return createToolResponse( - [ - `Partial success: ${successful.length} written, ${failed.length} failed`, - "", - "Written:", - ...successful.map(r => ` - ${r.path}`), - "", - "Failed:", - ...failed.map(r => ` - ${r.path}: ${r.error}`) - ].join("\n") - ); + return ok([`Wrote ${successful.length} file(s):`, ...successful.map(r => ` - ${r.path}`)].join("\n")); } + + // Any failure is a failure. A partial write previously returned a success envelope with + // "Partial success" in the text, so a client checking `isError` saw a clean write. + return fail( + "ERR_FILE_WRITE", + [ + `Wrote ${successful.length} of ${successful.length + failed.length} file(s).`, + ...(successful.length > 0 ? ["", "Written:", ...successful.map(r => ` - ${r.path}`)] : []), + "", + "Failed:", + ...failed.map(r => ` - ${r.path}: ${r.error}`) + ].join("\n") + ); } // ============================================================================= diff --git a/packages/pluggable-widgets-mcp/src/tools/index.ts b/packages/pluggable-widgets-mcp/src/tools/index.ts index 23aba9f4ee..4fea7b66a4 100644 --- a/packages/pluggable-widgets-mcp/src/tools/index.ts +++ b/packages/pluggable-widgets-mcp/src/tools/index.ts @@ -1,31 +1,23 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerBuildTools } from "./build.tools"; -import { registerCodeGenerationTools } from "./code-generation.tools"; import { registerFileOperationTools } from "./file-operations.tools"; import { registerProjectTools } from "./project.tools"; -import { registerPropertyUpdateTools } from "./property-update.tools"; import { registerScaffoldingTools } from "./scaffolding.tools"; import type { SessionState } from "./session-state"; +import { registerWidgetPropertiesTools } from "./widget-properties.tools"; /** - * Registers all tools with the MCP server. + * Registers every tool, in the order a widget is actually built: * - * Tools are organized by category: - * - Scaffolding: Widget creation (create-widget) - * - File Operations: Read/write widget files (list-widget-files, read-widget-file, write-widget-file) - * - Build: Widget building and validation (build-widget) - * - Code Generation: Generate widget XML and TSX (generate-widget-code) - * - Property Update: Incremental property updates (update-widget-properties) - * - Project: Project directory config and deployment (get-project-info, set-project-directory, deploy-widget) + * project config -> scaffold -> properties (XML) -> component source -> build -> deploy * - * Each category registers its tools directly with the server, preserving - * full type safety through the SDK's generic inference. + * Only tools that resolve a path against the sandbox or spawn a process need `state`; the rest + * operate on a caller-supplied widget directory already fenced by `validateFilePath`. */ export function registerAllTools(server: McpServer, state: SessionState): void { + registerProjectTools(server, state); registerScaffoldingTools(server, state); + registerWidgetPropertiesTools(server); registerFileOperationTools(server); registerBuildTools(server, state); - registerCodeGenerationTools(server); - registerPropertyUpdateTools(server); - registerProjectTools(server, state); } diff --git a/packages/pluggable-widgets-mcp/src/tools/project.tools.ts b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts index e1684cc40b..88bc53d64a 100644 --- a/packages/pluggable-widgets-mcp/src/tools/project.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/project.tools.ts @@ -1,28 +1,27 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { existsSync } from "node:fs"; import { copyFile, mkdir } from "node:fs/promises"; import { basename, join, resolve } from "node:path"; import { z } from "zod"; -import { GENERATIONS_DIR, validateProjectDir } from "@/config"; -import { isPathAllowed } from "./utils/sandbox"; +import { type ProjectValidation, validateProjectDir } from "@/config"; import type { ToolResponse } from "@/tools/types"; import { findMpkFile } from "@/tools/utils/mpk"; -import { createStructuredError, createStructuredErrorResponse, createToolResponse } from "@/tools/utils/response"; +import { fail, ok } from "@/tools/utils/response"; +import { describeAllowedRoots, isPathAllowed } from "./utils/sandbox"; import type { SessionState } from "./session-state"; -function formatProjectInfo(validation: Awaited>): string { - const lines: string[] = [ - `Project Directory: ${validation.projectDir}`, - ...(validation.projectName ? [`Project Name: ${validation.projectName}`] : []), - `Widgets Directory: ${validation.widgetsDir}` +function formatProjectInfo(validation: ProjectValidation): string { + const lines = [ + `Project directory: ${validation.projectDir}`, + ...(validation.projectName ? [`Project name: ${validation.projectName}`] : []), + `Widgets directory: ${validation.widgetsDir}` ]; if (validation.existingWidgets.length > 0) { - lines.push(`Existing Widgets (${validation.existingWidgets.length}):`); - for (const widget of validation.existingWidgets) { - lines.push(` - ${widget}`); - } + lines.push(`Existing widgets (${validation.existingWidgets.length}):`); + lines.push(...validation.existingWidgets.map(widget => ` - ${widget}`)); } else { - lines.push(`Existing Widgets: (none)`); + lines.push("Existing widgets: (none)"); } return lines.join("\n"); @@ -34,35 +33,28 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo { title: "Get Project Info", description: - "Returns information about the configured Mendix project directory. " + - "Call this first to discover the project context before creating or deploying widgets. " + - "Returns the project directory, project name, widgets directory, and existing .mpk files.", + "Returns the configured Mendix project directory, its name, and the .mpk widgets " + + "already deployed to it. Call this first to discover the project context.", inputSchema: z.object({}) }, async (): Promise => { if (!state.projectDir) { - return createStructuredErrorResponse( - createStructuredError("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { - suggestion: - "Set the MENDIX_PROJECT_DIR environment variable when starting the server, e.g.:\n" + - " MENDIX_PROJECT_DIR=/Users/you/Mendix/MyProject node dist/index.js http\n" + - "Or call set-project-directory to configure it at runtime." - }) - ); + return fail("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { + suggestion: + "Start the server with MENDIX_PROJECT_DIR set, or call set-project-directory to configure one at runtime." + }); } const validation = await validateProjectDir(state.projectDir); if (!validation.valid) { - return createStructuredErrorResponse( - createStructuredError( - "ERR_PROJECT_NOT_CONFIGURED", - `Configured project directory is invalid: ${validation.error}`, - { suggestion: "Use set-project-directory to set a valid Mendix project directory." } - ) + return fail( + "ERR_PROJECT_NOT_CONFIGURED", + `Configured project directory is invalid: ${validation.error}`, + { suggestion: "Use set-project-directory to point at a valid Mendix project." } ); } - return createToolResponse(`✅ Project configured\n\n${formatProjectInfo(validation)}`); + return ok(formatProjectInfo(validation)); } ); @@ -71,9 +63,9 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo { title: "Set Project Directory", description: - "Configures the Mendix project directory for this session. " + - "The directory must exist and contain a .mpr file. " + - "Once set, deploy-widget can copy built .mpk files to the project's widgets/ folder.", + "Points this session at a Mendix project. The directory must exist and contain " + + "exactly one .mpr file. It also becomes the sandbox root: every path the server " + + "touches must resolve inside it.", inputSchema: z.object({ projectDir: z .string() @@ -81,24 +73,15 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo }) }, async (args: { projectDir: string }): Promise => { - const resolvedDir = resolve(args.projectDir); - const validation = await validateProjectDir(resolvedDir); + const validation = await validateProjectDir(resolve(args.projectDir)); if (!validation.valid) { - return createStructuredErrorResponse( - createStructuredError( - "ERR_PROJECT_NOT_CONFIGURED", - `Invalid project directory: ${validation.error}`, - { - suggestion: - "Provide the absolute path to a directory that exists and contains a .mpr file, e.g.:\n" + - " /Users/you/Mendix/MyProject" - } - ) - ); + return fail("ERR_PROJECT_NOT_CONFIGURED", `Invalid project directory: ${validation.error}`, { + suggestion: "Provide the absolute path to a directory containing a .mpr file." + }); } state.projectDir = validation.projectDir; - return createToolResponse(`✅ Project directory configured\n\n${formatProjectInfo(validation)}`); + return ok(formatProjectInfo(validation)); } ); @@ -107,11 +90,9 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo { title: "Deploy Widget", description: - "Copies a built widget .mpk file to the configured Mendix project's widgets/ directory. " + - "Call this after build-widget succeeds. " + - "Requires a project directory to be configured (via MENDIX_PROJECT_DIR env var or set-project-directory). " + - "Looks for the .mpk file in the widget's dist/ directory. " + - "After deploying, synchronize the app directory in Studio Pro to pick up the new widget.", + "Copies a widget's built .mpk into the Mendix project's widgets/ directory. " + + "Reports whether an existing .mpk of the same name was replaced. Synchronize the " + + "app directory in Studio Pro afterwards to pick the widget up.", inputSchema: z.object({ widgetPath: z .string() @@ -120,34 +101,22 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo }, async (args: { widgetPath: string }): Promise => { if (!state.projectDir) { - return createStructuredErrorResponse( - createStructuredError("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { - suggestion: - "Call get-project-info to check the current configuration, " + - "or set-project-directory to configure a project directory." - }) - ); + return fail("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project directory is configured.", { + suggestion: "Call set-project-directory to configure one." + }); } - if (!isPathAllowed(args.widgetPath, state, "MCP_ALLOWED_BUILD_PATHS")) { - return createStructuredErrorResponse( - createStructuredError( - "ERR_NOT_FOUND", - `Widget path is not within an allowed directory: ${args.widgetPath}`, - { - suggestion: `Widget must be within ${GENERATIONS_DIR} or an allowed build path.` - } - ) - ); + if (!isPathAllowed(args.widgetPath, state)) { + return fail("ERR_OUTPUT_PATH_INVALID", `Widget path is outside the project: ${args.widgetPath}`, { + suggestion: `Allowed roots: ${describeAllowedRoots(state)}.` + }); } const mpkPath = findMpkFile(args.widgetPath); if (!mpkPath) { - return createStructuredErrorResponse( - createStructuredError("ERR_MPK_NOT_FOUND", `No .mpk file found in ${args.widgetPath}/dist/`, { - suggestion: "Run build-widget first to compile the widget and produce the .mpk file." - }) - ); + return fail("ERR_MPK_NOT_FOUND", `No .mpk found under ${args.widgetPath}/dist/`, { + suggestion: "Run build-widget first." + }); } const widgetsDir = join(state.projectDir, "widgets"); @@ -155,26 +124,28 @@ export function registerProjectTools(server: McpServer, state: SessionState): vo await mkdir(widgetsDir, { recursive: true }); const mpkFileName = basename(mpkPath); const destPath = join(widgetsDir, mpkFileName); + + // Redeploy is the expected workflow (build → deploy → fix → build → deploy), so the + // overwrite stays. The fix for "silent" is saying so, not blocking it. + const replaced = existsSync(destPath); await copyFile(mpkPath, destPath); - return createToolResponse( + return ok( [ - `✅ Widget deployed successfully!`, - ``, + replaced ? `Replaced ${mpkFileName} in the project.` : `Deployed ${mpkFileName}.`, + "", `Source: ${mpkPath}`, `Destination: ${destPath}`, - ``, - `Synchronize the app directory in Studio Pro to pick up the new widget.` + "", + "Synchronize the app directory in Studio Pro to pick up the widget." ].join("\n") ); } catch (error) { const message = error instanceof Error ? error.message : String(error); - return createStructuredErrorResponse( - createStructuredError("ERR_DEPLOY_FAILED", `Failed to deploy widget: ${message}`, { - suggestion: "Check write permissions on the widgets directory.", - rawOutput: message - }) - ); + return fail("ERR_DEPLOY_FAILED", `Failed to deploy widget: ${message}`, { + suggestion: "Check write permissions on the widgets directory.", + details: message + }); } } ); diff --git a/packages/pluggable-widgets-mcp/src/tools/property-schema.ts b/packages/pluggable-widgets-mcp/src/tools/property-schema.ts new file mode 100644 index 0000000000..fa1522e037 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/property-schema.ts @@ -0,0 +1,103 @@ +/** + * The Zod schema for a Mendix widget property model — one definition, used by every tool that + * accepts properties. + * + * This lives under `tools/` rather than `generators/` on purpose: `generators/` is the pure + * JSON→XML transformation core and is deliberately Zod-free, operating on the plain + * `PropertyDefinition` types in `generators/types.ts`. Validating untrusted input is a tool-boundary + * concern, so it belongs here. + * + * Keep the enums below in step with `generators/types.ts`. They were previously written out three + * times with no compile-time link, and had already drifted. + */ + +import { z } from "zod"; + +/** Mendix property types, mirroring `MendixPropertyType`. */ +export const PROPERTY_TYPES = [ + "string", + "boolean", + "integer", + "decimal", + "textTemplate", + "expression", + "action", + "attribute", + "datasource", + "association", + "selection", + "enumeration", + "icon", + "image", + "file", + "widgets", + "object" +] as const; + +/** Attribute types an `attribute` property may accept, mirroring `AttributeType`. */ +export const ATTRIBUTE_TYPES = [ + "String", + "Integer", + "Long", + "Decimal", + "Boolean", + "DateTime", + "Enum", + "HashString", + "Binary", + "AutoNumber" +] as const; + +/** System properties Studio Pro can contribute, mirroring `SystemProperty`. */ +export const SYSTEM_PROPERTIES = ["Name", "TabIndex", "Visibility"] as const; + +export const enumValueSchema = z.object({ + key: z.string().min(1).describe("Unique identifier for this enum value"), + caption: z.string().min(1).describe("Display caption shown in Studio Pro") +}); + +export const propertyDefinitionSchema = z.object({ + key: z + .string() + .min(1) + .regex(/^[a-z][a-zA-Z0-9]*$/, "Must be camelCase (e.g., 'myProperty')") + .describe("Property key in camelCase"), + type: z.enum(PROPERTY_TYPES).describe("Mendix property type"), + caption: z.string().min(1).describe("Display caption shown in Studio Pro"), + description: z.string().optional().describe("Help text shown in Studio Pro"), + required: z.boolean().optional().describe("Whether this property is required"), + defaultValue: z.union([z.string(), z.number(), z.boolean()]).optional().describe("Default value for this property"), + enumValues: z.array(enumValueSchema).optional().describe("Allowed values for enumeration type"), + attributeTypes: z + .array(z.enum(ATTRIBUTE_TYPES)) + .optional() + .describe("Allowed attribute types for an attribute property"), + isList: z.boolean().optional().describe("Whether the datasource returns a list"), + dataSource: z.string().optional().describe("Reference to a datasource property key (for widgets type)"), + returnType: z + .enum(["String", "Integer", "Decimal", "Boolean", "DateTime"]) + .optional() + .describe("Return type for an expression property") +}); + +export const propertyGroupSchema = z.object({ + caption: z.string().min(1).describe("Group caption displayed in Studio Pro"), + properties: z.array(z.string().min(1)).min(1).describe("Property keys in this group") +}); + +export const systemPropertySchema = z.enum(SYSTEM_PROPERTIES); + +/** + * Some MCP clients send JSON arrays as a stringified string. Parsing it here means validation still + * runs against the real array contents rather than rejecting the whole argument. + */ +export function parseMaybeStringifiedArray(value: unknown): unknown { + if (typeof value === "string") { + try { + return JSON.parse(value); + } catch { + return value; // let Zod report the type error + } + } + return value; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts index 81e651f858..d404496714 100644 --- a/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts +++ b/packages/pluggable-widgets-mcp/src/tools/scaffolding.tools.ts @@ -1,19 +1,25 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { GENERATIONS_DIR } from "@/config"; -import { DEFAULT_WIDGET_OPTIONS, type ToolContext, type ToolResponse, widgetOptionsSchema } from "@/tools/types"; -import { buildWidgetOptions, runWidgetGenerator, SCAFFOLD_PROGRESS } from "@/tools/utils/generator"; -import { ProgressTracker } from "@/tools/utils/progress-tracker"; +import { widgetSourcesDir } from "@/config"; +import { type ToolContext, type ToolResponse, widgetOptionsSchema } from "@/tools/types"; +import { InvalidAnswerError, MissingAnswerError } from "@/tools/utils/answer-adapter"; import { - createStructuredError, - createStructuredErrorResponse, - createToolResponse, - type ErrorCode -} from "@/tools/utils/response"; -import { access, mkdir, stat } from "node:fs/promises"; -import { dirname } from "node:path"; + buildWidgetOptions, + type InstallResult, + runNpmInstall, + runWidgetGenerator, + SCAFFOLD_PROGRESS, + ScaffoldTimeoutError +} from "@/tools/utils/generator"; +import { ProgressTracker } from "@/tools/utils/progress-tracker"; +import { type ErrorCode, fail, ok } from "@/tools/utils/response"; +import { mkdir, readFile, stat } from "node:fs/promises"; +import { join } from "node:path"; import { z } from "zod"; -import { isPathAllowed } from "./utils/sandbox"; +import { describeAllowedRoots, isPathAllowed } from "./utils/sandbox"; import type { SessionState } from "./session-state"; +import { createLogger } from "@/tools/utils/logger"; + +const log = createLogger("create-widget"); /** * Schema for create-widget tool input. @@ -24,45 +30,37 @@ const createWidgetSchema = widgetOptionsSchema.extend({ .string() .optional() .describe( - "[OPTIONAL] Directory where widget will be created. Defaults to ./generations/ in the current working directory. Leave unset in most cases — the server manages the output location." + "[OPTIONAL] Directory where the widget will be created. Defaults to widget-sources/ inside the configured Mendix project. Leave unset in most cases — the server manages the output location." ) }); type CreateWidgetInput = z.infer; -const CREATE_WIDGET_DESCRIPTION = `Scaffolds a new Mendix pluggable widget using the official @mendix/generator-widget. - -BEFORE RUNNING: Please confirm all options with the user. Show them the full list of configurable parameters: - -REQUIRED: - • name: Widget name in PascalCase (e.g., "MyAwesomeWidget") - • description: Brief description of what the widget does - -OPTIONAL (with defaults): - • version: Initial version (default: "${DEFAULT_WIDGET_OPTIONS.version}") - • author: Author name (default: "${DEFAULT_WIDGET_OPTIONS.author}") - • license: License type (default: "${DEFAULT_WIDGET_OPTIONS.license}") - • organization: Namespace organization (default: "${DEFAULT_WIDGET_OPTIONS.organization}") - • template: "full" (with examples) or "empty" (minimal) (default: "${DEFAULT_WIDGET_OPTIONS.template}") - • programmingLanguage: "typescript" or "javascript" (default: "${DEFAULT_WIDGET_OPTIONS.programmingLanguage}") - • unitTests: Include Jest test setup (default: ${DEFAULT_WIDGET_OPTIONS.unitTests}) - • e2eTests: Include Playwright E2E tests (default: ${DEFAULT_WIDGET_OPTIONS.e2eTests}) - • outputPath: Directory where widget will be created (default: ./generations/). Leave unset in most cases. +/** Reads the widgetName a scaffolded directory declares, if it is one. */ +async function readWidgetName(widgetPath: string): Promise { + try { + const pkg = JSON.parse(await readFile(join(widgetPath, "package.json"), "utf-8")) as { widgetName?: string }; + return pkg.widgetName; + } catch { + return undefined; + } +} -Ask the user if they want to customize any options before proceeding. +/** Node filesystem errors carry a `code`; check that rather than matching the message text. */ +function isNodeErrorWithCode(error: unknown, code: string): boolean { + return error instanceof Error && (error as NodeJS.ErrnoException).code === code; +} -After scaffolding, use build-widget to compile, then deploy-widget to copy the .mpk to the Mendix project.`; +// Every option and its default is already described on the schema fields, which the client receives +// as JSON Schema — restating them here served them to the model twice. The instruction to interview +// the user about paths also contradicted SERVER_INSTRUCTIONS. +const CREATE_WIDGET_DESCRIPTION = + "Scaffolds a new Mendix pluggable widget with @mendix/generator-widget and installs its " + + "dependencies. Returns the widget directory, and reports scaffolding and dependency " + + "installation separately — a failed install still leaves a usable scaffold."; /** - * Registers scaffolding-related tools for widget creation and management. - * - * Currently registers the create-widget tool. This modular pattern allows - * easy addition of related tools such as: - * - Widget property editing - * - XML configuration management - * - Build and deployment automation - * - * @see AGENTS.md Roadmap Context section for planned additions + * Registers the widget scaffolding tool. */ export function registerScaffoldingTools(server: McpServer, state: SessionState): void { server.registerTool( @@ -82,21 +80,22 @@ async function handleCreateWidget( state: SessionState ): Promise { const options = buildWidgetOptions(args); - const outputDir = args.outputPath ?? GENERATIONS_DIR; - // Validate user-provided outputPath is within allowed directories - if (args.outputPath) { - if (!isPathAllowed(args.outputPath, state, "MCP_ALLOWED_OUTPUT_PATHS")) { - return createStructuredErrorResponse( - createStructuredError( - "ERR_OUTPUT_PATH_INVALID", - `Output path is not within an allowed directory: ${args.outputPath}`, - { - suggestion: `Output must be within ${GENERATIONS_DIR} or set MCP_ALLOWED_OUTPUT_PATHS env var (colon-separated paths).` - } - ) - ); - } + if (!state.projectDir) { + return fail("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project is configured", { + suggestion: + "Call set-project-directory with the path to the open Mendix project, or start the server with MENDIX_PROJECT_DIR set." + }); + } + + // Widgets are scaffolded inside the project by default, next to the widgets/ folder builds + // deploy into, so sources travel with the project in version control. + const outputDir = args.outputPath ?? widgetSourcesDir(state.projectDir); + + if (!isPathAllowed(outputDir, state)) { + return fail("ERR_OUTPUT_PATH_INVALID", `Output path is outside the project: ${outputDir}`, { + suggestion: `Allowed roots: ${describeAllowedRoots(state)}.` + }); } const tracker = new ProgressTracker({ @@ -105,25 +104,12 @@ async function handleCreateWidget( totalSteps: 3 }); - try { - // Pre-validate ONLY for default path (catches Claude Desktop's non-existent cwd) - // For user-provided paths, let mkdir try and give a specific error if it fails - if (!args.outputPath) { - const parentDir = dirname(outputDir); - try { - await access(parentDir); - } catch { - return createStructuredErrorResponse( - createStructuredError("ERR_OUTPUT_PATH_REQUIRED", "Cannot create widget in default location", { - suggestion: - "The default output directory is not accessible (common in Claude Desktop). Please provide an explicit 'outputPath' parameter with a valid directory path on your system (e.g., '/Users/yourname/Projects/widgets', '~/widgets', or '/tmp/widgets').", - rawOutput: `Default path "${outputDir}" is not accessible. The working directory may not exist in this environment.` - }) - ); - } - } + // Scaffolding and dependency installation fail for different reasons and are reported + // separately: a failed install still leaves a usable scaffold. + let installResult: InstallResult = { ok: true }; - console.error(`[create-widget] Starting widget scaffolding for "${options.name}"...`); + try { + log.info(`Starting widget scaffolding for "${options.name}"...`); await tracker.progress(SCAFFOLD_PROGRESS.START, `Starting widget scaffolding for "${options.name}"...`); await tracker.info(`Starting widget scaffolding for "${options.name}"...`, { widgetName: options.name, @@ -132,16 +118,11 @@ async function handleCreateWidget( outputDir }); - // Ensure output directory exists - await mkdir(outputDir, { recursive: true }); - - // The generator creates the widget folder itself (camelCase: first letter lowered) + // The widget folder is ours to create, not the generator's to infer: we point the Yeoman + // environment's cwd at it directly. It must be fresh and empty — see runWidgetGenerator. const widgetFolder = options.name.charAt(0).toLowerCase() + options.name.slice(1); - const widgetPath = `${outputDir}/${widgetFolder}`; + const widgetPath = join(outputDir, widgetFolder); - // If the widget directory already exists, skip the Yeoman scaffold — the generator - // refuses to run in non-empty directories. The existing scaffold is still valid; - // generate-widget-code will overwrite the source files anyway. let alreadyExists = false; try { await stat(widgetPath); @@ -151,55 +132,44 @@ async function handleCreateWidget( } if (alreadyExists) { - console.error(`[create-widget] Widget directory already exists at ${widgetPath} — skipping scaffold`); - await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget directory already exists — skipping scaffold."); + // Skipping only makes sense if what is there really is this widget. Previously any + // directory with the right name was reported as a successful scaffold, unverified. + const existing = await readWidgetName(widgetPath); + if (existing !== options.name) { + return fail( + "ERR_OUTPUT_PATH_INVALID", + `${widgetPath} already exists and is not the "${options.name}" widget` + + (existing ? ` (found "${existing}")` : " (no package.json with a widgetName)"), + { suggestion: "Choose a different widget name, or remove the directory and try again." } + ); + } + log.info(`Widget already scaffolded at ${widgetPath} — skipping`); + await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget already scaffolded — skipping."); } else { - // Run generator inside outputDir — it creates the widget subfolder - await runWidgetGenerator(options, tracker, outputDir); + await mkdir(widgetPath, { recursive: true }); + const { askedFor } = await runWidgetGenerator(options, tracker, widgetPath); + log.info(`Generator prompts answered: ${askedFor.join(", ")}`); + + installResult = await runNpmInstall(widgetPath, tracker); } - console.error(`[create-widget] Widget created successfully at ${widgetPath}`); + log.info(`Widget created successfully at ${widgetPath}`); await tracker.progress(SCAFFOLD_PROGRESS.COMPLETE, "Widget created successfully!"); await tracker.info("Widget created successfully!", { widgetName: options.name, path: widgetPath }); - return createToolResponse( + // Facts, not a tutorial. The workflow is in SERVER_INSTRUCTIONS, sent once at initialize; + // repeating it on every scaffold spent tokens restating what the model already has. + return ok( [ - `Widget "${options.name}" created successfully!`, - "", - `Location: ${widgetPath}`, - "", - "=== TO IMPLEMENT WIDGET FUNCTIONALITY ===", - "", - "1. FETCH GUIDELINES (MCP Resources):", - " - mendix://guidelines/property-types (all widget property types with JSON schema)", - " - mendix://guidelines/widget-patterns (reusable TSX/SCSS patterns for common widget types)", + `Created widget "${options.name}" at ${widgetPath}.`, + installResult.ok + ? "Dependencies installed." + : `Dependencies were NOT installed: ${installResult.error}\nRun "npm install" in the widget directory before building.`, "", - "2. EXPLORE WIDGET STRUCTURE:", - ` Use list-widget-files tool with widgetPath: "${widgetPath}"`, - "", - "3. READ EXISTING CODE:", - ` Use read-widget-file tool to inspect:`, - ` - src/${options.name}.tsx (main component entry point)`, - ` - src/${options.name}.xml (widget properties definition)`, - ` - src/components/ (UI components - create if needed)`, - "", - "4. IMPLEMENT CHANGES:", - ` Use write-widget-file tool to create/update files`, - "", - "=== KEY FILES ===", - `- ${widgetPath}/src/${options.name}.tsx - Main widget component`, - `- ${widgetPath}/src/${options.name}.xml - Properties configuration`, - `- ${widgetPath}/src/${options.name}.editorPreview.tsx - Studio Pro preview`, - "", - "=== BUILD & TEST ===", - `1. cd ${widgetPath}`, - "2. npm install", - "3. npm start (builds and watches for changes)", - "", - "The widget will be available in Mendix Studio Pro after syncing the app directory." + "Next: set-widget-properties to define the widget's properties." ].join("\n") ); } catch (error) { @@ -209,30 +179,29 @@ async function handleCreateWidget( error: message }); - // Categorize the error for structured response + // Categorize by error type, not by substring-matching the message. The generator runs + // in-process, so failures arrive as real typed errors instead of an exit code plus text. let code: ErrorCode = "ERR_SCAFFOLD_FAILED"; - let suggestion = "Check the error details and try again. Ensure you have npm/npx available."; + let suggestion = "Check the error details and try again."; - if (message.includes("timed out")) { + if (error instanceof ScaffoldTimeoutError) { code = "ERR_SCAFFOLD_TIMEOUT"; - suggestion = - "The generator took too long. Check your network connection and npm registry access. Try running 'npx @mendix/generator-widget' manually."; - } else if (message.includes("ENOENT") || message.includes("not found")) { + suggestion = "The generator did not finish in time. Retry, and check filesystem responsiveness."; + } else if (error instanceof MissingAnswerError) { + suggestion = `The installed @mendix/generator-widget asks for a "${error.promptName}" option this server does not supply — the generator's prompts have changed. Upgrade the server or pin an earlier generator version.`; + } else if (error instanceof InvalidAnswerError) { + suggestion = `The generator rejected the "${error.promptName}" value: ${error.reason}. Adjust that argument and retry.`; + } else if (isNodeErrorWithCode(error, "EACCES") || isNodeErrorWithCode(error, "EPERM")) { code = "ERR_NOT_FOUND"; - // Check if this is a path issue vs a command issue - if (message.includes("mkdir") || message.includes(outputDir)) { - suggestion = `Cannot create directory "${outputDir}". Try a different 'outputPath' that you have write access to.`; - } else { - suggestion = - "The generator-widget binary was not found. Run: cd /path/to/widgets-tools/packages/generator-widget && npm link. Then ensure the MCP server runs under the same Node.js version."; - } + suggestion = `No permission to write to "${outputDir}". Choose an 'outputPath' you can write to.`; + } else if (isNodeErrorWithCode(error, "ENOENT")) { + code = "ERR_NOT_FOUND"; + suggestion = `Cannot create directory "${outputDir}". Check that its parent exists and is writable.`; } - return createStructuredErrorResponse( - createStructuredError(code, `Failed to create widget "${options.name}"`, { - suggestion, - rawOutput: message - }) - ); + return fail(code, `Failed to create widget "${options.name}"`, { + suggestion, + details: message + }); } } diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts b/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts index c0b14a6ac2..1bebaf36b2 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/mpk.ts @@ -1,30 +1,41 @@ -import { existsSync, readdirSync } from "node:fs"; +import { existsSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; /** - * Finds the .mpk file in the widget's dist directory. - * Searches recursively (usually in dist/x.x.x/). + * Finds the widget's built `.mpk`, searching `dist/` recursively (builds land in `dist//`). + * + * Returns the **most recently modified** match. An earlier version returned whichever file + * `readdirSync` yielded first, so a widget with both `dist/1.0.0/` and `dist/1.0.1/` present could + * deploy the stale artifact — silently, since the copy itself succeeds. */ export function findMpkFile(widgetPath: string): string | undefined { const distPath = join(widgetPath, "dist"); - if (!existsSync(distPath)) return undefined; + if (!existsSync(distPath)) { + return undefined; + } try { - const searchDir = (dir: string): string | undefined => { - const entries = readdirSync(dir, { withFileTypes: true }); - for (const entry of entries) { - const fullPath = join(dir, entry.name); - if (entry.isDirectory()) { - const found = searchDir(fullPath); - if (found) return found; - } else if (entry.name.endsWith(".mpk")) { - return fullPath; - } - } + const candidates = collectMpkFiles(distPath); + if (candidates.length === 0) { return undefined; - }; - return searchDir(distPath); + } + + return candidates.reduce((newest, candidate) => + statSync(candidate).mtimeMs > statSync(newest).mtimeMs ? candidate : newest + ); } catch { return undefined; } } + +function collectMpkFiles(dir: string, found: string[] = []): string[] { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + collectMpkFiles(fullPath, found); + } else if (entry.name.endsWith(".mpk")) { + found.push(fullPath); + } + } + return found; +} diff --git a/packages/pluggable-widgets-mcp/src/tools/widget-properties.tools.ts b/packages/pluggable-widgets-mcp/src/tools/widget-properties.tools.ts new file mode 100644 index 0000000000..cbba062757 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/tools/widget-properties.tools.ts @@ -0,0 +1,149 @@ +/** + * The `set-widget-properties` tool: turns a property model into the widget's XML definition. + * + * That transformation is schema-constrained and mechanical, so the server owns it. The component + * source is not — the client model writes the `.tsx` through `write-widget-file`, guided by the + * `mendix://guidelines/widget-patterns` resource. + * + * The tool is **declarative**: callers send the properties the widget should have, not a diff. It + * replaces an earlier pair of tools — one that generated and one that applied add/remove/modify + * operations against a `.widget-definition.json` snapshot on disk. The snapshot existed only so the + * diff had a base; with full state on every call it is unnecessary, and so is the class of bug where + * the snapshot and the XML disagree. + */ + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { basename, dirname, join } from "node:path"; +import { z } from "zod"; +import type { PropertyDefinition, PropertyGroup, SystemProperty, WidgetDefinition } from "@/generators/types"; +import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; +import { validateFilePath } from "@/security"; +import { + parseMaybeStringifiedArray, + propertyDefinitionSchema, + propertyGroupSchema, + systemPropertySchema +} from "@/tools/property-schema"; +import type { ToolResponse } from "@/tools/types"; +import { createLogger } from "@/tools/utils/logger"; +import { fail, ok } from "@/tools/utils/response"; + +const log = createLogger("widget-properties"); + +/** Studio Pro contributes these unless the caller says otherwise. */ +const DEFAULT_SYSTEM_PROPERTIES: SystemProperty[] = ["Name", "TabIndex", "Visibility"]; + +const setWidgetPropertiesSchema = z.object({ + widgetPath: z.string().min(1).describe("Absolute path to the widget directory"), + description: z.string().min(1).describe("Description of what the widget does"), + properties: z + .preprocess(parseMaybeStringifiedArray, z.array(propertyDefinitionSchema).min(1)) + .describe("The complete set of properties the widget should have. Replaces any existing set."), + systemProperties: z + .array(systemPropertySchema) + .optional() + .describe( + `System properties to include. Defaults to [${DEFAULT_SYSTEM_PROPERTIES.join(", ")}]. Pass an empty array for none.` + ), + propertyGroups: z + .array(propertyGroupSchema) + .optional() + .describe( + "Optional grouping. When omitted, non-action properties go in 'General' and action properties in 'Events'." + ) +}); + +type SetWidgetPropertiesInput = z.infer; + +/** + * Resolves the widget's PascalCase name. + * + * `package.json`'s `widgetName` is authoritative — it is what the generator wrote and what the build + * expects. The directory name is only a fallback, and a poor one: a folder called `my-widget` yields + * `My-widget`, which fails PascalCase validation and blames the user for a name they never chose. + */ +async function resolveWidgetName(widgetPath: string): Promise { + try { + const pkg = JSON.parse(await readFile(join(widgetPath, "package.json"), "utf-8")) as { widgetName?: string }; + if (pkg.widgetName && /^[A-Z][a-zA-Z0-9]*$/.test(pkg.widgetName)) { + return pkg.widgetName; + } + } catch { + // No package.json, or it is unreadable — fall through. + } + + const folder = basename(widgetPath); + return folder.charAt(0).toUpperCase() + folder.slice(1); +} + +async function handleSetWidgetProperties(args: SetWidgetPropertiesInput): Promise { + const { widgetPath, description, properties, systemProperties, propertyGroups } = args; + + try { + const widgetName = await resolveWidgetName(widgetPath); + + const definition: WidgetDefinition = { + name: widgetName, + description, + properties: properties as PropertyDefinition[], + systemProperties: (systemProperties as SystemProperty[]) ?? DEFAULT_SYSTEM_PROPERTIES, + propertyGroups: propertyGroups as PropertyGroup[] | undefined + }; + + const validationErrors = validateWidgetDefinition(definition); + if (validationErrors.length > 0) { + return fail( + "ERR_INVALID_DEFINITION", + ["Widget definition is invalid — nothing was written:", ...validationErrors.map(e => ` - ${e}`)].join( + "\n" + ) + ); + } + + const result = generateWidgetXml(definition); + if (!result.success || !result.xml) { + return fail("ERR_INVALID_DEFINITION", `XML generation failed: ${result.error}`); + } + + const relativePath = join("src", `${widgetName}.xml`); + validateFilePath(widgetPath, relativePath, true); + const fullPath = join(widgetPath, relativePath); + await mkdir(dirname(fullPath), { recursive: true }); + await writeFile(fullPath, result.xml, "utf-8"); + log.info(`Wrote ${fullPath} (${properties.length} properties)`); + + return ok( + [ + `Wrote ${relativePath} with ${properties.length} properties: ${properties.map(p => p.key).join(", ")}.`, + "", + "Next: write the component with write-widget-file, then run build-widget.", + ` - src/${widgetName}.tsx (read mendix://guidelines/widget-patterns first)`, + ` - src/${widgetName}.editorPreview.tsx for the Studio Pro design-mode preview` + ].join("\n") + ); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + log.error(message); + return fail("ERR_FILE_WRITE", `Failed to set widget properties: ${message}`); + } +} + +const DESCRIPTION = `Writes a widget's XML definition from its property model. + +Send the complete set of properties the widget should have; the file is rewritten to match. Returns +the path written. See the mendix://guidelines/property-types resource for the property schema. + +Does not write the component — use write-widget-file for the .tsx.`; + +export function registerWidgetPropertiesTools(server: McpServer): void { + server.registerTool( + "set-widget-properties", + { + title: "Set Widget Properties", + description: DESCRIPTION, + inputSchema: setWidgetPropertiesSchema + }, + handleSetWidgetProperties + ); +} From e1c38e68913e20c8b6565b83ee9f6c291ba39d99 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 15:35:29 +0200 Subject: [PATCH 30/36] test(mcp): tighten the response contract, add coverage, rewrite the docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ToolResponse stays a type alias rather than an interface, and that is load-bearing: the SDK's handler signature expects a type carrying an index signature, TypeScript grants aliases an implicit one, so the alias remains assignable while excess-property checking still catches a misspelled isError. Two constructors, ok and fail, with no third path and no raw literals — build.tools.ts previously bypassed all of them. Every failure now carries an error code, and ErrorCode is pruned to codes something actually emits; ERR_BUILD_TS, ERR_BUILD_XML, ERR_BUILD_MISSING_DEP, ERR_FILE_PATH and ERR_FILE_WRITE were declared and never constructed. write-widget-file returned success for a partial write ("Partial success: N written, M failed"). It now returns isError. Its relative-path computation used fullPath.replace(basePath + "/", ""), which replaces the first occurrence anywhere rather than an anchored prefix; now path.relative. New coverage: - xml-generator golden files. Zero coverage previously on the one module the refactor keeps, covering every property type, nesting, escaping, propertyGroup and systemProperty layout, and the primitive-required quirk. - file-operations round-trip through the in-memory client/server pair, including the partial-failure contract. - both guideline resources load and cache. - guardrails gains path.sep boundary and extensionless-equality cases. scaffolding.tools.test.ts no longer mocks runWidgetGenerator. With skipInstall: true a real scaffold runs in about 200ms, so the tool is tested for real. widget-lifecycle.test.ts drops the block that tested the test harness rather than the server. docs/widget-patterns.md is now load-bearing — it replaced the deleted TSX generator — so it was compiled for the first time against real Mendix typings: scaffold a widget, generate typings from real XML, extract every template verbatim, run tsc. Six errors in five of six templates. Four templates imported executeAction from @mendix/widget-plugin-platform, which is a private workspace package of the web-widgets monorepo and returns 404 from npm, so the doc's headline rule was unbuildable in exactly the standalone widgets this server produces. Templates now declare the five-line helper locally, keeping the isExecuting guard that nothing type-checks. Two unused imports removed; with jsx: "react-jsx" and noUnusedLocals the React 17 createElement idiom is now an error rather than a habit. README documented seven tools and omitted the project and deploy family that SERVER_INSTRUCTIONS tells the model to call first, claimed widgets land in generations/ inside the package, and documented no environment variables. CHANGELOG was a single sentence. --- packages/pluggable-widgets-mcp/.gitignore | 1 - packages/pluggable-widgets-mcp/CHANGELOG.md | 28 +- packages/pluggable-widgets-mcp/README.md | 312 +++++------------- .../docs/widget-patterns.md | 67 +++- .../scenarios/widget-lifecycle.test.ts | 95 +----- .../__tests__/xml-generator.test.ts | 175 ++++++++++ .../resources/__tests__/guidelines.test.ts | 23 ++ .../pluggable-widgets-mcp/src/tools/types.ts | 13 +- .../tools/utils/__tests__/response.test.ts | 94 ++---- .../src/tools/utils/response.ts | 171 ++++------ 10 files changed, 464 insertions(+), 515 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/src/generators/__tests__/xml-generator.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/resources/__tests__/guidelines.test.ts diff --git a/packages/pluggable-widgets-mcp/.gitignore b/packages/pluggable-widgets-mcp/.gitignore index 7c204398e4..94ba000c8b 100644 --- a/packages/pluggable-widgets-mcp/.gitignore +++ b/packages/pluggable-widgets-mcp/.gitignore @@ -1,4 +1,3 @@ dist/ generations/ node_modules/ -mcp-session-logs/ \ No newline at end of file diff --git a/packages/pluggable-widgets-mcp/CHANGELOG.md b/packages/pluggable-widgets-mcp/CHANGELOG.md index 8c63b9710f..72f88500be 100644 --- a/packages/pluggable-widgets-mcp/CHANGELOG.md +++ b/packages/pluggable-widgets-mcp/CHANGELOG.md @@ -8,4 +8,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added -- We introduce pluggable-widgets-mcp. +- We introduce pluggable-widgets-mcp: an MCP server that scaffolds, configures, builds and deploys + Mendix pluggable widgets. +- `set-widget-properties` writes a widget's XML from a declarative property model. +- Guideline resources `mendix://guidelines/property-types` and `mendix://guidelines/widget-patterns`. +- `MCP_EXTRA_ALLOWED_PATHS` and `MCP_LOG_LEVEL` configuration. + +### Changed + +- The Mendix project directory is the single sandbox root, and widgets are scaffolded into + `{project}/widget-sources/`. Nothing is derived from `process.cwd()`, which previously moved the + security boundary depending on which host spawned the server. +- The widget generator runs in-process through a Yeoman answer adapter instead of a CLI invocation + whose output was scraped for progress. Scaffolding and dependency installation are separate steps + with separate timeouts and separately reported outcomes. +- Build success is the child process's exit code. It was previously inferred from substrings in the + build output, so a build that printed a stale artifact path and then failed looked successful. +- The HTTP transport is stateless and binds `127.0.0.1`. It previously bound all interfaces and kept + a session map that could not be emptied. +- Tool descriptions state what a tool does; the widget workflow is described once in the server + instructions. + +### Removed + +- The in-process TSX generator. Component source is written by the client model against the + `widget-patterns` resource; the generator emitted code that did not compile in several cases. +- Keyword-based property suggestion and widget-pattern detection. +- File-lifecycle cleanup that deleted unrelated files from a widget's `src/`. diff --git a/packages/pluggable-widgets-mcp/README.md b/packages/pluggable-widgets-mcp/README.md index b6bedabee5..aac23219bc 100644 --- a/packages/pluggable-widgets-mcp/README.md +++ b/packages/pluggable-widgets-mcp/README.md @@ -1,276 +1,116 @@ -# Mendix Pluggable Widgets MCP Server +# @mendix/pluggable-widgets-mcp -> **Work in Progress** - This is an MVP focused on widget scaffolding. Widget editing capabilities coming soon. +An MCP server for building Mendix pluggable widgets. It scaffolds a widget, writes its XML +definition from a property model, builds it to an `.mpk`, and deploys that into a Mendix project. -A Model Context Protocol (MCP) server that enables AI assistants to scaffold Mendix pluggable widgets programmatically. +Designed to run as a child process of a host application — Mendix Studio Pro — over STDIO. An HTTP +transport is included for local debugging with the MCP Inspector. -## Quick Start +## Mental model -```bash -pnpm install -pnpm build # Build the server -pnpm start # STDIO mode (default) -pnpm start:stdio # STDIO mode -``` - -## Global Installation - -For use with MCP clients (Cursor, Claude Desktop, LMStudio), install globally: - -```bash -# Build first -pnpm build - -# Link globally using npm (NOT pnpm - better MCP client compatibility) -npm link - -# Verify installation -which pluggable-widgets-mcp -``` - -## Transport Modes +> The server does what is mechanically derivable. The client model does what requires judgment. +> The resources tell it how. -### STDIO Mode (default) +XML is generated by the server: it is a deterministic transformation of a property model against a +fixed schema. Component source is not — the model writes the `.tsx` itself, using the guideline +resources as reference. Everything the server touches must resolve inside the configured Mendix +project directory, which is the single sandbox root. -Runs via stdin/stdout for CLI-based MCP clients (Claude Desktop, etc.). +## Setup ```bash -pnpm start -pnpm start:stdio -``` - -### HTTP Mode - -Runs an HTTP server for web-based MCP clients. - -```bash -pnpm start:http -``` - -- Server runs on `http://localhost:3100` (override with `PORT` env var) -- Health check: `GET /health` -- MCP endpoint: `POST /mcp` - -## MCP Client Configuration - -### HTTP - -```json -{ - "mcpServers": { - "pluggable-widgets-mcp": { - "url": "http://localhost:3100/mcp" - } - } -} -``` - -### STDIO - -**_Some client setups like Claude Desktop support STDIO only (for now)_** - -**Option 1: Global command (after `npm link`)** - -```json -{ - "mcpServers": { - "pluggable-widgets-mcp": { - "command": "pluggable-widgets-mcp", - "args": ["stdio"] - } - } -} +npm install +npm run build ``` -**Option 2: Absolute path (more reliable during development)** +Point an MCP client at the built entry point: ```json { "mcpServers": { - "pluggable-widgets-mcp": { + "mendix-widgets": { "command": "node", - "args": ["/path/to/pluggable-widgets-mcp/dist/index.js", "stdio"] + "args": ["/path/to/packages/pluggable-widgets-mcp/dist/index.js", "stdio"], + "env": { "MENDIX_PROJECT_DIR": "/path/to/YourMendixProject" } } } } ``` -> **Note:** After rebuilding the server, you may need to restart/reconnect your MCP client to pick up changes. - -## Available Tools - -### create-widget - -Scaffolds a new Mendix pluggable widget using `@mendix/generator-widget`. - -| Parameter | Required | Default | Description | -| --------------------- | -------- | ------------ | ------------------------------------ | -| `name` | Yes | - | Widget name (PascalCase recommended) | -| `description` | Yes | - | Brief description of the widget | -| `version` | No | `1.0.0` | Initial version (semver) | -| `author` | No | `Mendix` | Author name | -| `license` | No | `Apache-2.0` | License type | -| `organization` | No | `Mendix` | Organization namespace | -| `template` | No | `empty` | `full` (sample code) or `empty` | -| `programmingLanguage` | No | `typescript` | `typescript` or `javascript` | -| `unitTests` | No | `true` | Include unit test setup (Jest/TS) | -| `e2eTests` | No | `false` | Include E2E test setup (Playwright) | - -Generated widgets are placed in `generations/` directory within this package. - -### File Operation Tools - -| Tool | Description | -| ------------------- | ------------------------------------------------------------------------------------- | -| `list-widget-files` | Lists all files in a widget directory, grouped by type | -| `read-widget-file` | Reads the contents of a file from a widget directory | -| `write-widget-file` | Writes content to a file (creates parent dirs). Supports single-file and batch modes. | - -**Security:** All file operations are protected by `src/security/guardrails.ts`: - -- Path traversal is blocked (no `..` escapes) -- Extension whitelist: `.tsx`, `.ts`, `.xml`, `.scss`, `.css`, `.json`, `.md` - -### Code Generation Tools - -| Tool | Description | -| -------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| `generate-widget-code` | Generates widget XML + TSX + SCSS from property definitions. Saves a `.widget-definition.json` snapshot. | -| `update-widget-properties` | Incrementally adds, removes, or modifies widget properties. Requires `generate-widget-code` to have been run first. | - -### build-widget - -Builds a widget using `pluggable-widgets-tools`, producing an `.mpk` file. - -| Parameter | Required | Description | -| ------------ | -------- | ------------------------------------- | -| `widgetPath` | Yes | Absolute path to the widget directory | - -Returns structured errors for TypeScript, XML, or dependency issues. - -## Available Resources - -| URI | Description | -| ------------------------------------- | -------------------------------------------------------------------------- | -| `mendix://guidelines/property-types` | Complete reference for all Mendix widget property types | -| `mendix://guidelines/widget-patterns` | Reusable patterns for common widget types (Button, Input, Container, etc.) | - -## Development +For local debugging: ```bash -pnpm dev # Development mode with hot reload -pnpm build # Build for production -pnpm start # Build and run +MENDIX_PROJECT_DIR=/path/to/project node dist/index.js http +npx @modelcontextprotocol/inspector # point it at http://127.0.0.1:3100/mcp ``` -## Testing with MCP Inspector - -The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive debugging tool for testing MCP servers. It provides a web UI to connect to your server, explore available tools, and execute them with custom inputs. +## Project layout -### Quick Start +The project directory is both the sandbox root and where widget sources live: -```bash -# Run Inspector against this server (STDIO mode) -npx @modelcontextprotocol/inspector node dist/index.js stdio - -# Or for HTTP mode, start the server first then connect via Inspector -pnpm start -npx @modelcontextprotocol/inspector -# Then enter http://localhost:3100/mcp as the server URL +``` +{MENDIX_PROJECT_DIR}/ + YourApp.mpr + widgets/ deploy target (.mpk files) + widget-sources/ scaffold root + myWidget/ + src/MyWidget.xml written by set-widget-properties + src/MyWidget.tsx written by the client model + dist/MyWidget.mpk built by build-widget, copied to ../../widgets/ ``` -### Using the Inspector - -1. **Connect** - The Inspector will automatically connect to your MCP server -2. **Explore Tools** - View all registered tools (`create-widget`, etc.) with their schemas -3. **Execute Tools** - Fill in parameters and run tools to test behavior -4. **View Responses** - See JSON responses, progress notifications, and logs in real-time - -### Example: Testing `create-widget` - -1. Start the Inspector: `npx @modelcontextprotocol/inspector node dist/index.js stdio` -2. Select the `create-widget` tool from the tools list -3. Fill in required parameters: - ```json - { - "name": "TestWidget", - "description": "A test widget", - ... // Defaults for other optional values if not entered - } - ``` -4. Click "Execute" and watch progress notifications as the widget is scaffolded -5. Check `generations/testWidget/` for the created widget - -This is useful for verifying tool behavior without needing a full AI client integration. - -## Understanding Feedback and Notifications - -This server uses MCP's notification system to provide progress updates and logging. However, **different types of feedback appear in different places**—not all feedback shows up in your chat conversation. - -### Where Different Types of Feedback Appear - -| Feedback Type | Where It Appears | Example | -| -------------------------- | -------------------------------------- | ----------------------------------------------------------------- | -| **Tool Results** | ✅ Chat conversation | Widget created at `/path/to/widget`, Build completed successfully | -| **Progress Notifications** | ⚙️ Client UI (spinners, progress bars) | "Scaffolding widget...", "Building widget..." | -| **Log Messages** | 🔍 Debug console (MCP Inspector) | Detailed operation logs, debug info | - -### Why Progress Doesn't Show in Chat - -**This is by design per the MCP specification**, not a bug. The MCP architecture separates concerns: - -- **`notifications/progress`** → Routed to client UI indicators (loading spinners, status bars) -- **`notifications/message`** → Routed to debug/inspector consoles for developers -- **Tool results** → Returned to the conversation when operations complete - -This means: - -- Long operations (scaffolding, building) will show **results** when complete -- You won't see intermediate progress steps in the chat history -- MCP Inspector shows all notifications in real-time (bottom-right panel) - -### Viewing Debug Output - -**With MCP Inspector:** +## Tools -1. Run: `npx @modelcontextprotocol/inspector node dist/index.js stdio` -2. Execute a tool (e.g., `create-widget`) -3. Watch the **Notifications panel** (bottom-right) for progress updates -4. Check the **Logs panel** for detailed debug output +| Tool | What it does | +| ----------------------- | ----------------------------------------------------------------------------------- | +| `get-project-info` | Reports the configured project, its name, and already-deployed `.mpk` files. | +| `set-project-directory` | Points the session at a project. Also sets the sandbox root. | +| `create-widget` | Scaffolds a widget with `@mendix/generator-widget` and installs dependencies. | +| `set-widget-properties` | Writes `src/.xml` from a property model. Declarative — send the full set. | +| `list-widget-files` | Lists a widget's files, skipping `node_modules` and build output. | +| `read-widget-file` | Reads one file from a widget directory. | +| `write-widget-file` | Writes one file or a batch. All paths are validated before any write happens. | +| `build-widget` | Compiles to `.mpk` via `pluggable-widgets-tools`. Success is the child's exit code. | +| `deploy-widget` | Copies the newest `.mpk` into the project's `widgets/`, reporting replacements. | -**With Claude Desktop:** +## Resources -- Progress notifications may appear as UI indicators (client-dependent) -- Check Claude Desktop's developer console for log messages (if available) -- Tool results will always appear in the conversation +| URI | Contents | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `mendix://guidelines/property-types` | Every Mendix property type and the JSON schema `set-widget-properties` accepts. | +| `mendix://guidelines/widget-patterns` | Component templates per widget archetype — display, button, input, container, data list. | -### Expected Behavior Examples +Read `widget-patterns` before writing component source. -**During widget scaffolding:** +## Configuration -- Chat shows: "Starting scaffolding..." → (wait) → "Widget created at `/path`" -- Inspector shows: Progress notifications (start → installing dependencies → complete) +| Variable | Default | Purpose | +| ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `MENDIX_PROJECT_DIR` | — | The open Mendix project. Also the sandbox root. Tools fail with `ERR_PROJECT_NOT_CONFIGURED` until it is set, here or via `set-project-directory`. | +| `MCP_EXTRA_ALLOWED_PATHS` | — | Extra permitted roots, platform-delimited (`:` POSIX, `;` Windows). Development only. | +| `MCP_LOG_LEVEL` | `info` | `debug`, `info`, `warn` or `error`. All logging goes to stderr. | +| `PORT` | `3100` | HTTP transport port. Binds `127.0.0.1` only. | -**During widget building:** +## Security -- Chat shows: "Building..." → (wait) → "Build successful" or structured error -- Inspector shows: TypeScript compilation progress, dependency resolution +Two layers, both rooted at the project directory: -## Roadmap +- **Sandbox** (`src/tools/utils/sandbox.ts`) — a path must resolve inside an allowed root. Nothing + is derived from `process.cwd()`, so the boundary does not move with whoever spawned the process. +- **Guardrails** (`src/security/guardrails.ts`) — a file must stay within its widget directory + (checked by resolving and comparing, which collapses any `../`) and carry an allowed extension: + `.tsx .ts .xml .scss .css .json .md`, plus the extensionless config files `package`, `tsconfig`, + `eslintrc` and the dot-files `.gitignore`, `.prettierrc`, `.eslintrc`, `.editorconfig`. -- [x] Widget scaffolding (`create-widget`) -- [x] HTTP transport -- [x] STDIO transport -- [x] Progress notifications -- [x] File operations (list, read, write) -- [x] Build tool (`build-widget`) -- [x] Guideline resources (property-types, widget-patterns) -- [x] Code generation (`generate-widget-code`) -- [x] Incremental property update tool (`update-widget-properties`) -- [ ] Batch widget generation -- [ ] Widget testing helpers -- [ ] TypeScript error recovery suggestions +## Development -## License +```bash +npm run dev # tsx watch +npm test # vitest +npm run lint +npm run build +``` -Apache-2.0 - Mendix Technology BV 2025 +Tests drive the server through a real MCP client over an in-memory transport +(`src/__test-utils__/mcp-test-harness.ts`), so tool calls go through JSON-RPC serialization and Zod +validation rather than calling handlers directly. diff --git a/packages/pluggable-widgets-mcp/docs/widget-patterns.md b/packages/pluggable-widgets-mcp/docs/widget-patterns.md index 782e01b488..d50d8d9468 100644 --- a/packages/pluggable-widgets-mcp/docs/widget-patterns.md +++ b/packages/pluggable-widgets-mcp/docs/widget-patterns.md @@ -25,10 +25,16 @@ Display widgets show read-only data. Examples: Badge, Progress Bar, Label. ```tsx import { ReactNode, useCallback } from "react"; -import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action"; +import { ActionValue } from "mendix"; import { MyWidgetContainerProps } from "../typings/MyWidgetProps"; import "./ui/MyWidget.scss"; +function executeAction(action?: ActionValue): void { + if (action && action.canExecute && !action.isExecuting) { + action.execute(); + } +} + export default function MyWidget(props: MyWidgetContainerProps): ReactNode { const { value, type, onClick, tabIndex, class: className, style } = props; @@ -36,7 +42,7 @@ export default function MyWidget(props: MyWidgetContainerProps): ReactNode { executeAction(onClick); }, [onClick]); - const isClickable = onClick?.canExecute; + const isClickable = onClick?.canExecute ?? false; return (
{ executeAction(props.onClick); }, [props.onClick]); -// Check if action can execute -const isClickable = props.onClick?.canExecute; +// Check whether the action is wired up at all, to decide on affordances (cursor, role, tabIndex) +const isClickable = props.onClick?.canExecute ?? false; ``` +The `isExecuting` guard is the part worth keeping: without it a rapid double-click fires a +microflow twice, and nothing in the type system catches its absence. + ### Attribute Value Handling Check status before reading/writing: @@ -543,7 +580,7 @@ if (props.counterValue?.status === "available" && !props.counterValue.readOnly) **Counter widget pattern (Integer/Long attribute):** ```tsx -import { ReactElement, createElement, useState, useEffect, useCallback } from "react"; +import { ReactElement, useState, useEffect, useCallback } from "react"; import Big from "big.js"; import { CounterContainerProps } from "../typings/CounterProps"; import "./ui/Counter.scss"; diff --git a/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts b/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts index 353b9a894a..3ff71b2444 100644 --- a/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts +++ b/packages/pluggable-widgets-mcp/src/__tests__/scenarios/widget-lifecycle.test.ts @@ -4,12 +4,7 @@ * The generator is mocked so tests run fast without Yeoman scaffolding. */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { - createMcpTestContext, - createRecordingMcpTestContext, - getResultText, - isError -} from "@/__test-utils__/mcp-test-harness"; +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; import { createTempMendixProject } from "@/__test-utils__/temp-dir"; import type { Client } from "@modelcontextprotocol/sdk/client/index.js"; import type { SessionState } from "@/tools/session-state"; @@ -27,7 +22,9 @@ vi.mock("@/tools/utils/generator", () => ({ unitTests: false, e2eTests: false }), - runWidgetGenerator: vi.fn().mockResolvedValue(undefined), + runWidgetGenerator: () => Promise.resolve({ askedFor: [] }), + runNpmInstall: () => Promise.resolve({ ok: true }), + ScaffoldTimeoutError: class ScaffoldTimeoutError extends Error {}, SCAFFOLD_PROGRESS: { START: { progress: 0, message: "Starting..." }, COMPLETE: { progress: 100, message: "Done!" } @@ -110,7 +107,7 @@ describe("scaffold workflow", () => { expect(isError(createResult)).toBe(false); const text = getResultText(createResult); expect(text).toContain("ScenarioWidget"); - expect(text).toContain("created successfully"); + expect(text).toContain(`Created widget "ScenarioWidget"`); }); }); @@ -166,85 +163,3 @@ describe("error recovery", () => { expect(state.projectDir).toBe(dir); }); }); - -describe("protocol recording captures tool sequence", () => { - let cleanup: () => Promise; - const tempCleanups: Array<() => void> = []; - - afterEach(async () => { - await cleanup(); - for (const c of tempCleanups) c(); - tempCleanups.length = 0; - }); - - it("records tool calls in order and assertToolOrder passes", async () => { - const ctx = await createRecordingMcpTestContext(); - cleanup = ctx.cleanup; - - const { dir, cleanup: tempCleanup } = createTempMendixProject({ projectName: "RecordingApp" }); - tempCleanups.push(tempCleanup); - - // Call two tools in sequence - await ctx.client.callTool({ name: "get-project-info", arguments: {} }); - ctx.state.projectDir = dir; - await ctx.client.callTool({ name: "get-project-info", arguments: {} }); - - // Verify recording captured both calls - const sequence = ctx.getToolCallSequence(); - expect(sequence.length).toBeGreaterThanOrEqual(2); - expect(sequence[0]).toBe("get-project-info"); - expect(sequence[1]).toBe("get-project-info"); - - // assertToolOrder should not throw - expect(() => ctx.assertToolOrder(["get-project-info", "get-project-info"])).not.toThrow(); - }); - - it("records messages in both directions", async () => { - const ctx = await createRecordingMcpTestContext(); - cleanup = ctx.cleanup; - - const { dir, cleanup: tempCleanup } = createTempMendixProject(); - tempCleanups.push(tempCleanup); - ctx.state.projectDir = dir; - - await ctx.client.callTool({ name: "get-project-info", arguments: {} }); - - const clientToServer = ctx.records.filter(r => r.direction === "client-to-server"); - const serverToClient = ctx.records.filter(r => r.direction === "server-to-client"); - - expect(clientToServer.length).toBeGreaterThan(0); - expect(serverToClient.length).toBeGreaterThan(0); - }); - - it("getToolCalls returns name and arguments", async () => { - const ctx = await createRecordingMcpTestContext(); - cleanup = ctx.cleanup; - - const { dir, cleanup: tempCleanup } = createTempMendixProject(); - tempCleanups.push(tempCleanup); - - await ctx.client.callTool({ - name: "set-project-directory", - arguments: { projectDir: dir } - }); - - const toolCalls = ctx.getToolCalls(); - expect(toolCalls).toHaveLength(1); - expect(toolCalls[0].name).toBe("set-project-directory"); - expect((toolCalls[0].arguments as Record).projectDir).toBe(dir); - expect(toolCalls[0].timestamp).toBeGreaterThan(0); - }); - - it("assertToolOrder throws when sequence is wrong", async () => { - const ctx = await createRecordingMcpTestContext(); - cleanup = ctx.cleanup; - - const { dir, cleanup: tempCleanup } = createTempMendixProject(); - tempCleanups.push(tempCleanup); - ctx.state.projectDir = dir; - - await ctx.client.callTool({ name: "get-project-info", arguments: {} }); - - expect(() => ctx.assertToolOrder(["set-project-directory"])).toThrow(); - }); -}); diff --git a/packages/pluggable-widgets-mcp/src/generators/__tests__/xml-generator.test.ts b/packages/pluggable-widgets-mcp/src/generators/__tests__/xml-generator.test.ts new file mode 100644 index 0000000000..01a75b6851 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/generators/__tests__/xml-generator.test.ts @@ -0,0 +1,175 @@ +import { describe, expect, it } from "vitest"; +import type { WidgetDefinition } from "@/generators/types"; +import { generateWidgetXml, validateWidgetDefinition } from "@/generators/xml-generator"; + +function definition(overrides: Partial = {}): WidgetDefinition { + return { + name: "MyWidget", + description: "a widget", + properties: [{ key: "label", type: "string", caption: "Label" }], + systemProperties: ["Name", "TabIndex", "Visibility"], + ...overrides + }; +} + +function xmlFor(overrides: Partial = {}): string { + const result = generateWidgetXml(definition(overrides)); + expect(result.success).toBe(true); + return result.xml!; +} + +describe("generateWidgetXml", () => { + it("emits a widget id namespaced by organization and name", () => { + expect(xmlFor()).toContain('MyWidget"); + expect(xmlFor()).toContain("a widget"); + }); + + it("renders each property with its key, type and caption", () => { + const xml = xmlFor({ + properties: [ + { key: "label", type: "string", caption: "Label", description: "Help text" }, + { key: "onClick", type: "action", caption: "On Click" } + ] + }); + expect(xml).toContain(''); + expect(xml).toContain("Label"); + expect(xml).toContain("Help text"); + expect(xml).toContain(''); + }); + + it("groups non-action properties under General and actions under Events by default", () => { + const xml = xmlFor({ + properties: [ + { key: "label", type: "string", caption: "Label" }, + { key: "onClick", type: "action", caption: "On Click" } + ] + }); + expect(xml.indexOf('propertyGroup caption="General"')).toBeGreaterThan(-1); + expect(xml.indexOf('propertyGroup caption="Events"')).toBeGreaterThan( + xml.indexOf('propertyGroup caption="General"') + ); + }); + + it("honours explicit property groups", () => { + const xml = xmlFor({ + properties: [ + { key: "label", type: "string", caption: "Label" }, + { key: "size", type: "integer", caption: "Size" } + ], + propertyGroups: [{ caption: "Appearance", properties: ["label", "size"] }] + }); + expect(xml).toContain('propertyGroup caption="Appearance"'); + expect(xml).not.toContain('propertyGroup caption="General"'); + }); + + it("emits attributeTypes for attribute properties", () => { + const xml = xmlFor({ + properties: [{ key: "value", type: "attribute", caption: "Value", attributeTypes: ["Integer", "Decimal"] }] + }); + expect(xml).toContain(' { + const xml = xmlFor({ + properties: [ + { + key: "mode", + type: "enumeration", + caption: "Mode", + defaultValue: "light", + enumValues: [ + { key: "light", caption: "Light" }, + { key: "dark", caption: "Dark" } + ] + } + ] + }); + expect(xml).toContain('Light'); + expect(xml).toContain('Dark'); + }); + + it("escapes XML metacharacters in captions", () => { + const xml = xmlFor({ + properties: [{ key: "label", type: "string", caption: 'Fish & "x"' }] + }); + expect(xml).toContain("Fish & <Chips>"); + expect(xml).not.toContain(""); + }); + + it("emits requested system properties and omits the rest", () => { + const xml = xmlFor({ systemProperties: ["Name"] }); + expect(xml).toContain(''); + expect(xml).not.toContain(''); + }); + + it("emits no systemProperty entries when given an empty list", () => { + expect(xmlFor({ systemProperties: [] })).not.toContain(" { + const xml = xmlFor({ + properties: [ + { key: "items", type: "datasource", caption: "Items", isList: true }, + { key: "content", type: "widgets", caption: "Content", dataSource: "items" } + ] + }); + expect(xml).toContain(''); + expect(xml).toContain('dataSource="items"'); + }); +}); + +describe("validateWidgetDefinition", () => { + it("accepts a well-formed definition", () => { + expect(validateWidgetDefinition(definition())).toEqual([]); + }); + + it("requires a PascalCase widget name", () => { + expect(validateWidgetDefinition(definition({ name: "myWidget" })).join()).toContain("PascalCase"); + }); + + it("requires at least one property", () => { + expect(validateWidgetDefinition(definition({ properties: [] })).join()).toContain("at least one property"); + }); + + it("requires camelCase property keys", () => { + const errors = validateWidgetDefinition( + definition({ properties: [{ key: "Label", type: "string", caption: "Label" }] }) + ); + expect(errors.join()).toContain("camelCase"); + }); + + it("rejects duplicate property keys", () => { + const errors = validateWidgetDefinition( + definition({ + properties: [ + { key: "value", type: "string", caption: "One" }, + { key: "value", type: "string", caption: "Two" } + ] + }) + ); + expect(errors.join()).toContain("Duplicate property key"); + }); + + it("requires attributeTypes on attribute properties", () => { + const errors = validateWidgetDefinition( + definition({ properties: [{ key: "value", type: "attribute", caption: "Value" }] }) + ); + expect(errors.join()).toContain("attributeTypes"); + }); + + it("requires enumValues on enumeration properties", () => { + const errors = validateWidgetDefinition( + definition({ properties: [{ key: "mode", type: "enumeration", caption: "Mode" }] }) + ); + expect(errors.join()).toContain("enumValues"); + }); + + it("rejects property groups referencing unknown keys", () => { + const errors = validateWidgetDefinition( + definition({ propertyGroups: [{ caption: "Appearance", properties: ["nope"] }] }) + ); + expect(errors.join()).toContain("unknown property key"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/resources/__tests__/guidelines.test.ts b/packages/pluggable-widgets-mcp/src/resources/__tests__/guidelines.test.ts new file mode 100644 index 0000000000..841a6d9e16 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/resources/__tests__/guidelines.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import { GUIDELINE_RESOURCES, loadGuidelineContent } from "@/resources/guidelines"; + +describe("guideline resources", () => { + it("exposes the property-types and widget-patterns guidelines", () => { + expect(GUIDELINE_RESOURCES.map(resource => resource.uri).sort()).toEqual([ + "mendix://guidelines/property-types", + "mendix://guidelines/widget-patterns" + ]); + }); + + // These files are served to the client and are load-bearing: the server generates XML but the + // model writes the component, using widget-patterns as its reference. They ship via the + // package's `files` field, so a packaging mistake breaks them silently in an installed copy. + it.each(GUIDELINE_RESOURCES)("loads $filename from disk", async resource => { + const content = await loadGuidelineContent(resource.filename); + expect(content.length).toBeGreaterThan(100); + }); + + it("throws a named error for a missing guideline", async () => { + await expect(loadGuidelineContent("does-not-exist.md")).rejects.toThrow("does-not-exist.md"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/tools/types.ts b/packages/pluggable-widgets-mcp/src/tools/types.ts index b89f2087c4..b75a444e37 100644 --- a/packages/pluggable-widgets-mcp/src/tools/types.ts +++ b/packages/pluggable-widgets-mcp/src/tools/types.ts @@ -8,12 +8,17 @@ import { z } from "zod"; /** * Standard response format for MCP tool handlers. - * Index signature required for MCP SDK compatibility. + * + * A type alias, not an interface, and that is load-bearing. The SDK's handler signature expects a + * type carrying an index signature; TypeScript gives type aliases an implicit one, so this stays + * assignable — while excess-property checking still rejects a literal with a misspelled `isError`. + * The previous version declared `[key: string]: unknown` explicitly, which made `isError` invisible + * to the compiler: `isErrror: true` compiled cleanly and reported a failure as success. */ -export interface ToolResponse { - [key: string]: unknown; +export type ToolResponse = { content: Array<{ type: "text"; text: string }>; -} + isError?: boolean; +}; /** * Extra context provided to tool handlers by the MCP server. diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts index 31ea3efd0a..2f2fe45ea6 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/__tests__/response.test.ts @@ -1,84 +1,50 @@ import { describe, expect, it } from "vitest"; -import { - createErrorResponse, - createStructuredError, - createStructuredErrorResponse, - createToolResponse -} from "@/tools/utils/response"; +import { fail, ok } from "@/tools/utils/response"; -describe("createToolResponse", () => { - it("returns content with text and no isError", () => { - const result = createToolResponse("hello"); - expect(result).toEqual({ - content: [{ type: "text", text: "hello" }] - }); - expect(result).not.toHaveProperty("isError"); +describe("ok", () => { + it("returns text content with no error flag", () => { + const response = ok("all good"); + expect(response.content).toEqual([{ type: "text", text: "all good" }]); + expect(response.isError).toBeUndefined(); }); }); -describe("createErrorResponse", () => { - it("sets isError to true", () => { - const result = createErrorResponse("something broke"); - expect(result.isError).toBe(true); - expect(result.content[0].text).toBe("something broke"); +describe("fail", () => { + it("flags the response as an error", () => { + expect(fail("ERR_NOT_FOUND", "missing").isError).toBe(true); }); -}); -describe("createStructuredError", () => { - it("creates an error with code and message", () => { - const err = createStructuredError("ERR_NOT_FOUND", "Widget not found"); - expect(err.code).toBe("ERR_NOT_FOUND"); - expect(err.message).toBe("Widget not found"); - expect(err.suggestion).toBeUndefined(); - expect(err.details).toBeUndefined(); + it("renders the code into the text, which is all the model sees", () => { + expect(fail("ERR_NOT_FOUND", "missing").content[0].text).toContain("[ERR_NOT_FOUND] missing"); }); - it("includes optional suggestion and details", () => { - const err = createStructuredError("ERR_BUILD_TS", "Type error", { - suggestion: "Check types", - file: "src/Foo.tsx", - line: 42, - rawOutput: "raw stuff" - }); - expect(err.suggestion).toBe("Check types"); - expect(err.details?.file).toBe("src/Foo.tsx"); - expect(err.details?.line).toBe(42); - expect(err.details?.rawOutput).toBe("raw stuff"); + it("includes the suggestion when given", () => { + const text = fail("ERR_MPK_NOT_FOUND", "no mpk", { suggestion: "Run build-widget first." }).content[0].text; + expect(text).toContain("Suggestion: Run build-widget first."); + }); + + it("formats file, line and column as file:line:column", () => { + const text = fail("ERR_BUILD_FAILED", "boom", { file: "src/W.tsx", line: 4, column: 25 }).content[0].text; + expect(text).toContain("File: src/W.tsx:4:25"); }); -}); -describe("createStructuredErrorResponse", () => { - it("formats header with [CODE] message", () => { - const resp = createStructuredErrorResponse(createStructuredError("ERR_NOT_FOUND", "Widget missing")); - const text = resp.content[0].text; - expect(text).toContain("[ERR_NOT_FOUND]"); - expect(text).toContain("Widget missing"); + it("keeps a column that arrives without a line", () => { + // The previous implementation dropped `column` unless `file`, `line` or raw output was set. + const text = fail("ERR_BUILD_FAILED", "boom", { file: "src/W.tsx", column: 7 }).content[0].text; + expect(text).toContain("src/W.tsx:7"); }); - it("includes file location line when details.file is set", () => { - const resp = createStructuredErrorResponse( - createStructuredError("ERR_BUILD_TS", "Type error", { - file: "src/Foo.tsx", - line: 10, - column: 5 - }) - ); - const text = resp.content[0].text; - expect(text).toContain("src/Foo.tsx:10:5"); + it("omits the file line entirely when there is no file", () => { + expect(fail("ERR_BUILD_FAILED", "boom").content[0].text).not.toContain("File:"); }); - it("truncates rawOutput longer than 500 chars", () => { - const longOutput = "x".repeat(600); - const resp = createStructuredErrorResponse( - createStructuredError("ERR_BUILD_UNKNOWN", "fail", { rawOutput: longOutput }) - ); - const text = resp.content[0].text; + it("truncates long details", () => { + const text = fail("ERR_BUILD_FAILED", "boom", { details: "x".repeat(900) }).content[0].text; expect(text).toContain("...(truncated)"); - expect(text).not.toContain("x".repeat(600)); + expect(text.length).toBeLessThan(700); }); - it("sets isError to true", () => { - const resp = createStructuredErrorResponse(createStructuredError("ERR_NOT_FOUND", "gone")); - expect(resp.isError).toBe(true); + it("leaves short details intact", () => { + expect(fail("ERR_BUILD_FAILED", "boom", { details: "short" }).content[0].text).toContain("short"); }); }); diff --git a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts index 5469040388..393f55c6a9 100644 --- a/packages/pluggable-widgets-mcp/src/tools/utils/response.ts +++ b/packages/pluggable-widgets-mcp/src/tools/utils/response.ts @@ -1,128 +1,91 @@ import type { ToolResponse } from "@/tools/types"; /** - * Error codes for structured error responses. - * These help clients categorize and handle errors appropriately. + * Tool responses: exactly two constructors. + * + * There were previously three, plus one raw object literal that bypassed all of them, and the codes + * below were declared up front "in case" — six of fourteen were never emitted anywhere. Every code + * here is produced by at least one tool, and every failure path goes through `fail`, so `isError` is + * never forgotten. */ -export type ErrorCode = - | "ERR_BUILD_TS" // TypeScript compilation error - | "ERR_BUILD_XML" // XML validation error - | "ERR_BUILD_MISSING_DEP" // Missing dependency - | "ERR_BUILD_UNKNOWN" // Unknown build error - | "ERR_SCAFFOLD_TIMEOUT" // Scaffolding timed out - | "ERR_SCAFFOLD_FAILED" // Generic scaffold failure - | "ERR_FILE_PATH" // Invalid file path - | "ERR_FILE_WRITE" // File write failure - | "ERR_NOT_FOUND" // Resource not found - | "ERR_OUTPUT_PATH_REQUIRED" // Output path required (e.g., in Claude Desktop) - | "ERR_OUTPUT_PATH_INVALID" // Output path is not accessible - | "ERR_PROJECT_NOT_CONFIGURED" // Project directory not configured or invalid - | "ERR_MPK_NOT_FOUND" // Built .mpk file not found in dist/ - | "ERR_DEPLOY_FAILED"; // Failed to deploy .mpk to project widgets dir /** - * Structured error with code, message, and optional details. - * Provides actionable information for debugging and fixing issues. + * Machine-readable failure categories. Clients can branch on these; the text is for the model. */ -export interface StructuredError { - code: ErrorCode; - message: string; +export type ErrorCode = + /** No Mendix project configured, or the configured one is invalid. */ + | "ERR_PROJECT_NOT_CONFIGURED" + /** A path resolves outside the project sandbox. */ + | "ERR_OUTPUT_PATH_INVALID" + /** A required file or directory does not exist. */ + | "ERR_NOT_FOUND" + /** The widget generator failed. */ + | "ERR_SCAFFOLD_FAILED" + /** The widget generator exceeded its time budget. */ + | "ERR_SCAFFOLD_TIMEOUT" + /** The property model is not a valid widget definition. */ + | "ERR_INVALID_DEFINITION" + /** Reading a widget file failed. */ + | "ERR_FILE_READ" + /** Writing a widget file failed. */ + | "ERR_FILE_WRITE" + /** The build produced errors, or exited non-zero. */ + | "ERR_BUILD_FAILED" + /** No .mpk found — the widget has not been built. */ + | "ERR_MPK_NOT_FOUND" + /** Copying the .mpk into the project failed. */ + | "ERR_DEPLOY_FAILED"; + +/** Optional context attached to a failure. */ +export interface FailureContext { + /** What the caller should do about it. */ suggestion?: string; - details?: { - file?: string; - line?: number; - column?: number; - rawOutput?: string; - }; + file?: string; + line?: number; + column?: number; + /** Raw tool output, truncated in the rendered message. */ + details?: string; } -/** - * Creates a successful tool response with text content. - */ -export function createToolResponse(text: string): ToolResponse { - return { - content: [{ type: "text", text }] - }; -} +/** Raw output beyond this is noise for the model and cost for the caller. */ +const MAX_DETAIL_CHARS = 500; -/** - * Creates an error tool response with a message. - */ -export function createErrorResponse(message: string): ToolResponse { - return { - isError: true, - content: [{ type: "text", text: message }] - }; +/** A successful tool result. */ +export function ok(text: string): ToolResponse { + return { content: [{ type: "text", text }] }; } /** - * Creates a structured error response with code, message, and details. - * Formats the error for both human readability and machine parsing. + * A failed tool result. + * + * The code is rendered into the text as `[ERR_…]` so it survives the MCP text channel, which is all + * the model ever sees. */ -export function createStructuredErrorResponse(error: StructuredError): ToolResponse { - const lines: string[] = []; +export function fail(code: ErrorCode, message: string, context: FailureContext = {}): ToolResponse { + const lines = [`[${code}] ${message}`]; - // Header with error code - lines.push(`❌ [${error.code}] ${error.message}`); - - // File location if available - if (error.details?.file) { - let location = ` 📁 File: ${error.details.file}`; - if (error.details.line) { - location += `:${error.details.line}`; - if (error.details.column) { - location += `:${error.details.column}`; - } - } - lines.push(location); + const location = formatLocation(context); + if (location) { + lines.push(`File: ${location}`); } - - // Suggestion for fixing - if (error.suggestion) { - lines.push(` 💡 Suggestion: ${error.suggestion}`); + if (context.suggestion) { + lines.push(`Suggestion: ${context.suggestion}`); } - - // Raw output for debugging (truncated) - if (error.details?.rawOutput) { - const truncated = - error.details.rawOutput.length > 500 - ? error.details.rawOutput.slice(0, 500) + "...(truncated)" - : error.details.rawOutput; - lines.push(` 📝 Details:\n${truncated}`); + if (context.details) { + lines.push("Details:", truncate(context.details)); } - return { - isError: true, - content: [{ type: "text", text: lines.join("\n") }] - }; + return { content: [{ type: "text", text: lines.join("\n") }], isError: true }; } -/** - * Creates a structured error object (for use with createStructuredErrorResponse). - */ -export function createStructuredError( - code: ErrorCode, - message: string, - options?: { - suggestion?: string; - file?: string; - line?: number; - column?: number; - rawOutput?: string; +function formatLocation({ file, line, column }: FailureContext): string | undefined { + if (!file) { + return undefined; } -): StructuredError { - return { - code, - message, - suggestion: options?.suggestion, - details: - options?.file || options?.line || options?.rawOutput - ? { - file: options?.file, - line: options?.line, - column: options?.column, - rawOutput: options?.rawOutput - } - : undefined - }; + // Column was previously dropped whenever it appeared without a line. + return [file, line, column].filter(part => part !== undefined).join(":"); +} + +function truncate(text: string): string { + return text.length > MAX_DETAIL_CHARS ? `${text.slice(0, MAX_DETAIL_CHARS)}...(truncated)` : text; } From 2f7a5ff0d5e4a518ec36797ef0e4c60cd5a43db4 Mon Sep 17 00:00:00 2001 From: Rahman Date: Wed, 29 Jul 2026 16:35:41 +0200 Subject: [PATCH 31/36] docs(mcp): document how the server is evaluated, add repo-local skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/evaluation.md explains, without assuming knowledge of the codebase, what the server does, how each claim about it is proved, and how fast it is. It is written to be readable by someone deciding whether to use this, not only by someone maintaining it. The timings quoted were measured by hand on 2026-07-29 against a real Mendix project; the document says so, and says which layers are designed but not yet built. The two skills move out of a private ~/.claude directory and into the package, so anyone contributing gets them without local setup: - mcp-server-test — the operating procedure for the three evaluation layers. Includes the rules for the open-ended layer, where a model uses the server knowing only what the server tells any client. Those rules are the substance of that layer: reading src/ first makes the result meaningless. - pluggable-widgets-mcp — the existing working notes on the server's design, with its cross-reference fixed to the repository-relative path. The private code-generation-test skill is superseded and should be removed. It drives generate-widget-code and detectWidgetPattern, both deleted, and grades with LLM judgment, so it cannot gate a change. --- .../.claude/skills/mcp-server-test/SKILL.md | 127 ++++++++++ .../skills/pluggable-widgets-mcp/SKILL.md | 230 ++++++++++++++++++ .../skills/pluggable-widgets-mcp/reference.md | 160 ++++++++++++ .../pluggable-widgets-mcp/docs/evaluation.md | 145 +++++++++++ 4 files changed, 662 insertions(+) create mode 100644 packages/pluggable-widgets-mcp/.claude/skills/mcp-server-test/SKILL.md create mode 100644 packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/SKILL.md create mode 100644 packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/reference.md create mode 100644 packages/pluggable-widgets-mcp/docs/evaluation.md diff --git a/packages/pluggable-widgets-mcp/.claude/skills/mcp-server-test/SKILL.md b/packages/pluggable-widgets-mcp/.claude/skills/mcp-server-test/SKILL.md new file mode 100644 index 0000000000..1759799d19 --- /dev/null +++ b/packages/pluggable-widgets-mcp/.claude/skills/mcp-server-test/SKILL.md @@ -0,0 +1,127 @@ +--- +name: mcp-server-test +description: Use after any change to pluggable-widgets-mcp to verify it still works end to end and to record how fast it is. Runs unit tests, real-process end-to-end tests, and optionally an open-ended run where a model uses the server unaided. Produces a pass/fail result plus a timing comparison against the previous run. +--- + +# Testing the pluggable-widgets-mcp server + +Three layers, fastest first. Run them in order and stop at the first failure — a broken unit test +makes the end-to-end result meaningless. + +The reader-facing explanation of what this measures and why lives in +`packages/pluggable-widgets-mcp/docs/evaluation.md`. This file is the operating procedure. + +## Arguments + +- no arguments — layers 1 and 2 (warm), plus the timing comparison +- `--cold` — adds the from-scratch run that produces the headline timing +- `--llm` — adds layer 3, the open-ended run +- `--only ` — `unit` | `e2e` | `llm` + +## Layer 1 — unit tests + +```bash +cd packages/pluggable-widgets-mcp +npm run test +``` + +Run from the package directory, not the repo root. Expect all tests green in a few seconds. + +**On failure:** stop. Report which specs failed and what they assert. Do not continue to layer 2. + +## Layer 2 — end-to-end tests + +```bash +cd packages/pluggable-widgets-mcp +npm run build # e2e drives dist/, not src/ +npm run test:e2e +``` + +`npm run build` first is not optional — the harness spawns `node dist/index.js`, so a stale `dist/` +tests the previous version of the server and will happily pass while the change under test is broken. + +For the from-scratch run: + +```bash +E2E_COLD=1 npm run test:e2e +``` + +Cold mode installs dependencies for real and needs network. If it fails on network, say so plainly — +do not report it as a server failure. + +**On a golden-file mismatch:** the diff is the finding. Read it before deciding anything. A changed +golden is either a regression or an intended change to XML generation; only the diff tells you +which. If it is intended, update the golden in the same commit as the generator change, never +separately. + +**On failure:** report the failing spec, the assertion, and the actual value. Do not re-run hoping +for a different result. + +## Layer 3 — open-ended run (`--llm`) + +This measures whether the server is _usable_, not whether it is correct. It only means something if +you approach it genuinely cold. + +**Rules — these are the whole point of the exercise:** + +- Do **not** read `src/`, the tests, or this repository's documentation first. +- Connect to the server and read only what it tells any client: `tools/list`, the server + instructions returned at initialize, and the MCP resources it advertises. +- Work only from those. If you find yourself guessing at an argument, that is a finding — record it + rather than looking up the answer. + +**Setup:** start the server against a scratch Mendix project. + +```bash +MENDIX_PROJECT_DIR= node dist/index.js stdio +``` + +**The brief.** Pick one and treat it as a user request, nothing more: + +- "Add a rating-stars widget to my app." +- "I need a badge that shows a status and changes colour." +- "Build me a collapsible panel I can put other widgets inside." + +**Record, as you go:** + +| | | +| -------------------------- | ------------------------------------------------------------------- | +| Calls made, in order | including ones that failed | +| Wrong turns | a tool called with bad arguments, a step done out of order, a retry | +| Where guidance was missing | anything you had to guess | +| Result | did a `.mpk` reach the project's `widgets/` folder? | +| Wall-clock | from first call to deployed widget | + +**Report as findings, not a score.** "The description of `set-widget-properties` does not say +properties replace the previous set, so the first call dropped two properties" is useful. "7/10" is +not. + +## Timing + +Every end-to-end run appends a row to `packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl` +tagged with the current commit. After layer 2, compare the latest row against the previous row of +the same mode and report the delta. + +Report it in plain terms: + +``` +scratch → deployed .mpk 37.4s (cold) +1.2s vs 18fe583 +rebuild after an edit 3.9s (warm) -0.1s vs 18fe583 +``` + +A few seconds of drift on the install or build steps is noise — those are npm and the Mendix +toolchain, not this server. Movement in the server's own steps (`set-widget-properties`, +`write-widget-file`, `deploy-widget`) is not noise: those are measured in milliseconds, so a jump to +hundreds of milliseconds means something real changed. + +## Reporting + +Give the outcome in this order: + +1. **Pass or fail**, and if failed, the single most important reason. +2. **Timings**, with the delta against the previous run. +3. **Findings** from layer 3, if it ran. +4. **What you did not run**, and why. + +Never report a layer as passing without having run it. If cold mode was skipped, say it was skipped +rather than quoting the previous run's number as if it were fresh. diff --git a/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/SKILL.md b/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/SKILL.md new file mode 100644 index 0000000000..39d42b2279 --- /dev/null +++ b/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/SKILL.md @@ -0,0 +1,230 @@ +--- +name: pluggable-widgets-mcp +description: Use when working on the pluggable-widgets-mcp package — adding tools, writing tests, debugging the server, or understanding the widget lifecycle pipeline. +--- + +## Project Location + +`packages/pluggable-widgets-mcp/` in the `web-widgets` monorepo. All paths below are relative to it. + +## Mental model — read this before changing anything + +> The server does what is mechanically derivable. The client LLM does what requires judgment. +> The resources tell it how. + +XML generation is the server's job: a deterministic transformation of a property model against a +fixed schema. **Component `.tsx` is not** — the model writes it via `write-widget-file`, guided by +`docs/widget-patterns.md`, which ships as an MCP resource. An in-process TSX generator used to exist +and was deleted; it emitted code that did not compile. Do not reintroduce one. + +The server runs as a **child process of Mendix Studio Pro over STDIO**. HTTP is for MCP Inspector +debugging only: stateless, bound to `127.0.0.1`. + +**The Mendix project directory is the single sandbox root.** Nothing derives from `process.cwd()` — +that used to move the security boundary depending on who spawned the process. + +## Pipeline + +``` +get-project-info → create-widget → set-widget-properties → write-widget-file → build-widget → deploy-widget + (Yeoman) (XML) (model writes TSX) (.mpk) (→ project/widgets/) +``` + +Widgets scaffold into `{MENDIX_PROJECT_DIR}/widget-sources//`. + +## Key entry points + +| File | Role | +| ---------------------- | ---------------------------------------------------------------------------------- | +| `src/index.ts` | Entry point — validates `argv[2]` (`stdio` \| `http`), `--help` | +| `src/server/server.ts` | `createMcpServer()` — registers tools + resources | +| `src/tools/index.ts` | `registerAllTools(server, state)`, ordered by pipeline stage | +| `src/config.ts` | `getConfiguredProjectDir()`, `widgetSourcesDir()`, timeouts, `SERVER_INSTRUCTIONS` | + +## Tool map (9 tools) + +| File | Tools | Takes `state`? | +| ---------------------------- | ------------------------------------------------------------ | -------------- | +| `project.tools.ts` | `get-project-info`, `set-project-directory`, `deploy-widget` | Yes | +| `scaffolding.tools.ts` | `create-widget` | Yes | +| `widget-properties.tools.ts` | `set-widget-properties` | No | +| `file-operations.tools.ts` | `list-widget-files`, `read-widget-file`, `write-widget-file` | No | +| `build.tools.ts` | `build-widget` | Yes | + +`SessionState` is `{ projectDir: string | undefined }` (`src/tools/session-state.ts`), seeded from +`MENDIX_PROJECT_DIR` and re-pointable by `set-project-directory`. Tools that resolve paths against +the sandbox or spawn processes take it; the rest are fenced by `validateFilePath` alone. + +`set-widget-properties` is **declarative** — callers send the complete property set, not a diff. It +replaced `generate-widget-code` + `update-widget-properties`, which shared a +`.widget-definition.json` snapshot on disk that could disagree with the XML. + +## Adding a new tool + +```ts +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import type { ToolResponse } from "@/tools/types"; +import { createLogger } from "@/tools/utils/logger"; +import { fail, ok } from "@/tools/utils/response"; +import type { SessionState } from "@/tools/session-state"; + +const log = createLogger("my-feature"); + +const schema = z.object({ widgetPath: z.string().min(1).describe("Absolute path to the widget") }); + +export function registerMyFeatureTools(server: McpServer, state: SessionState): void { + server.registerTool( + "my-tool", + { title: "My Tool", description: "What it does and what it returns.", inputSchema: schema }, + async (args): Promise => { + if (!state.projectDir) { + return fail("ERR_PROJECT_NOT_CONFIGURED", "No Mendix project is configured.", { + suggestion: "Call set-project-directory." + }); + } + return ok("Result text"); + } + ); +} +``` + +Then wire it into `registerAllTools` in `src/tools/index.ts`, at its pipeline position. + +**Tool descriptions say what the tool does and returns — nothing about what to call next.** The +workflow lives once in `SERVER_INSTRUCTIONS` (`src/config.ts`). Retry policy prose does not belong in +a description: the server cannot enforce it, and in MCP the client owns the loop. + +## Response contract + +Exactly two constructors in `src/tools/utils/response.ts`: + +```ts +ok(text) +fail(code, message, { suggestion?, file?, line?, column?, details? }) +``` + +`ToolResponse` (`src/tools/types.ts`) is a **type alias, not an interface** — and that is +load-bearing. The SDK's handler signature expects a type carrying an index signature; TypeScript +grants aliases an implicit one, so it stays assignable while excess-property checking still catches a +misspelled `isError`. Changing it to an `interface` breaks every registration site. + +Every failure path goes through `fail` with a code. Codes live in the `ErrorCode` union; add one only +when a tool actually emits it. `fail` renders `[ERR_CODE]` into the text, which is all the model sees. + +## Path security + +Two layers, both rooted at the project directory. + +**Sandbox** (`src/tools/utils/sandbox.ts`) — `allowedRoots(state)`, `isPathAllowed(path, state)`, +`describeAllowedRoots(state)`. Roots are `state.projectDir` plus optional `MCP_EXTRA_ALLOWED_PATHS` +(split on `path.delimiter`, not `":"` — that would tear `C:\widgets` in two). + +**Guardrails** (`src/security/guardrails.ts`) — `isPathWithinDirectory`, `isExtensionAllowed`, +`validateFilePath(widgetPath, filePath, checkExtension?)` (throws). Pass `checkExtension=true` for +writes. Containment is the whole traversal defence — `resolve()` collapses `../` before comparison, +so there is deliberately no substring test for `".."` (it rejected legitimate names like +`foo..bar.tsx`). Boundary comparisons use `path.sep`, never a hardcoded `"/"`. + +Extensions: `.tsx .ts .xml .scss .css .json .md`, plus extensionless `package`/`tsconfig`/`eslintrc` +by **exact filename** and the dot-files `.gitignore .prettierrc .eslintrc .editorconfig`. + +## The Yeoman generator — do not reintroduce CLI flags + +`@mendix/generator-widget` (registry `^11.11.0`) has **no non-interactive CLI**: it declares zero +`this.option()` calls, `yeoman-generator@8`'s `prompt()` never reads `this.options`, and `.yo-rc.json` +prefill only touches `store: true` prompts and still prompts. A local fork added `--default` to work +around this; the registry version does not have it. + +Instead `src/tools/utils/answer-adapter.ts` replaces the Yeoman environment's I/O layer — the +supported extension point. `AnswerAdapter` implements the full `QueuedAdapter` shape (`log`, `prompt`, +`queue`, `progress`, `close`, `abort`, `signal`) because `yeoman-environment` assigns it directly and +does **not** wrap a plain adapter. + +Three properties that matter: + +- Supplied answer wins → prompt default fills gaps → **missing-and-defaultless throws** + (`MissingAnswerError`), so an upstream prompt rename fails loudly instead of silently defaulting. +- Nothing writes to stdout. Under STDIO that channel is the JSON-RPC stream. +- Scaffolding **must** target a fresh empty directory. The generator's `end()` hook spawns builds with + `stdio: "inherit"` when it finds a populated `node_modules` — straight into the protocol channel. + +The 14 prompt names are pinned in `src/tools/utils/__tests__/generator.test.ts`. They are the +generator's contract, not ours: `hasUnitTests`/`hasE2eTests` (not `unitTests`/`e2eTests`), and +`copyright` is deliberately unanswered so its own current-year default applies. + +`runWidgetGenerator` (in-process, `skipInstall: true`, ~200 ms) and `runNpmInstall` (separate spawn, +own timeout) are separate steps with separately reported outcomes — a registry stall leaves a usable +scaffold. + +## Build + +`build-widget` success is **the child process's exit code**, never a substring of the output. +`parseBuildOutput` returns `Omit` so the compiler enforces that it does not +decide success. Failures return `file:line:column`; file contents are not embedded — `read-widget-file` +exists for that. + +## Logging + +`src/tools/utils/logger.ts` — `createLogger(tag)` → `.debug/.info/.warn/.error`. **Always stderr**; +Studio Pro captures it and that is the support log. Level via `MCP_LOG_LEVEL`. Distinct from +`src/tools/utils/notifications.ts`, which sends `notifications/message` to the _client_. + +## Testing + +**`npm run test` inside the package directory** — not `pnpm test` from the repo root. + +That is the fast layer only. To verify a change end to end — real process, real scaffold, real build, +real `.mpk` — plus timings, use the `mcp-server-test` skill. `docs/evaluation.md` explains what each +layer proves and why. + +```ts +import { createMcpTestContext, getResultText, isError } from "@/__test-utils__/mcp-test-harness"; +import { registerMyFeatureTools } from "@/tools/my-feature.tools"; + +const { client, state, cleanup } = await createMcpTestContext(registerMyFeatureTools); +state.projectDir = dir; // most tools refuse without one +const result = await client.callTool({ name: "my-tool", arguments: { ... } }); +expect(isError(result)).toBe(false); +await cleanup(); +``` + +The harness runs a real client↔server pair over `InMemoryTransport`, so calls go through JSON-RPC +serialization and Zod validation, not direct handler invocation. + +**Two traps:** + +- The suite runs with `restoreMocks: true`. A `vi.fn()` created inside a `vi.mock` factory is reset + after the first test, leaving later tests with a stub returning `undefined`. Use **plain functions** + in module-mock factories. +- Scaffolding is no longer slow — `skipInstall: true` runs in ~200 ms, so prefer a real scaffold over + mocking `runWidgetGenerator` in new tests. + +Fixtures: `createTempMendixProject()`, `createTempWidgetWithMpk()` (`src/__test-utils__/temp-dir.ts`). + +## Commands + +| Command | Effect | +| ---------------------------------------------- | ------------------------------------- | +| `npm run build` | `tsc` + `tsc-alias` + chmod | +| `npm run dev` | Watch via tsx | +| `npm run test` | `vitest run` | +| `npm run lint` | eslint | +| `node dist/index.js stdio` | Production transport | +| `MENDIX_PROJECT_DIR=… node dist/index.js http` | Inspector debugging, `127.0.0.1:3100` | + +## Environment + +| Variable | Purpose | +| ------------------------- | --------------------------------------------------------------------------------------- | +| `MENDIX_PROJECT_DIR` | The project. Also the sandbox root. Tools fail `ERR_PROJECT_NOT_CONFIGURED` without it. | +| `MCP_EXTRA_ALLOWED_PATHS` | Extra roots, platform-delimited. Dev only. | +| `MCP_LOG_LEVEL` | `debug` \| `info` \| `warn` \| `error` | +| `PORT` | HTTP port, default 3100 | + +`@/` → `src/` (tsconfig paths; `vite-tsconfig-paths` in tests, `tsc-alias` in build). + +## Deep reference + +`.claude/skills/pluggable-widgets-mcp/reference.md` — full module inventory, test inventory, +transport details, MPK structure. diff --git a/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/reference.md b/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/reference.md new file mode 100644 index 0000000000..14d2102af7 --- /dev/null +++ b/packages/pluggable-widgets-mcp/.claude/skills/pluggable-widgets-mcp/reference.md @@ -0,0 +1,160 @@ +# Pluggable Widgets MCP Server — Deep Reference + +Companion to `SKILL.md`. Read that first for the mental model. + +## Module inventory (30 source files) + +### Entry & config + +- `src/index.ts` — validates `argv[2]` against `["stdio","http"]`, `--help`/`-h`, exits 1 on an + unknown transport (an unchecked cast used to fall through to stdio, so `htpp` started a server + speaking the wrong protocol). +- `src/config.ts` — `SERVER_NAME`, `PORT`, `SERVER_ICON`, `SERVER_INSTRUCTIONS`, `PACKAGE_ROOT`, + `SERVER_VERSION` (degrades to `0.0.0` rather than killing module eval), `DOCS_DIR`, + `SCAFFOLD_TIMEOUT_MS` (60s) / `INSTALL_TIMEOUT_MS` (5m) / `BUILD_TIMEOUT_MS` (5m), + `getConfiguredProjectDir()`, `widgetSourcesDir(projectDir)`, `validateProjectDir(dir)`, + `ProjectValidation`. + - `getConfiguredProjectDir()` is a **function**, not a const — a module-load const let three + modules capture a stale copy. The live value is `state.projectDir`. + - `validateProjectDir` sorts `.mpr` matches and errors on more than one, rather than taking + whichever `readdir` returned first. + +### Server layer (`src/server/`) + +- `server.ts` — `createMcpServer()`. Capabilities: `logging`, `resources`, `tools`. **No `prompts`** — + it was advertised with none registered, which makes `prompts/list` look empty rather than + unsupported. +- `stdio.ts` — production transport. Shuts down on SIGINT, SIGTERM, **and stdin `end`/`close`** — the + last is how a stdio child learns its host died; without it the process outlives Studio Pro, and on + Windows the signals are not delivered as POSIX code expects. +- `http.ts` — binds `127.0.0.1` explicitly, handles `EADDRINUSE` with a clear message. No `cors` + dependency; the SDK's `createMcpExpressApp({ host })` installs DNS-rebinding protection for loopback. +- `routes.ts` — **stateless**. `POST /mcp` builds a transport + server per request + (`sessionIdGenerator: undefined`), disposes on `res.close`. `GET`/`DELETE` → 405. `OPTIONS` → 204. + `GET /health` → `{ status, server, version, projectConfigured }` — deliberately no project path + (unauthenticated endpoint) and no session count (there are none). +- `log-project-config.ts` — one helper, sinks injected. STDIO passes `(log.info, log.info)` because + stdout is the protocol channel; HTTP passes `(log.info, log.warn)`. Tagging is the sink's job. + +**Deleted:** `session.ts` / `SessionManager` (the map could never be emptied — DELETE carries no body, +so the handler threw before reaching the transport; a raw GET minted an `McpServer` that was never +registered or closed) and `protocol-logger.ts` (synchronous `appendFileSync` per request, HTTP-only, +half-wired). There is no `mcp-session-logs/` directory any more. + +### Tools (`src/tools/`) + +- `project.tools.ts` — `get-project-info`, `set-project-directory`, `deploy-widget`. +- `scaffolding.tools.ts` — `create-widget`. Verifies an existing directory's `package.json` + `widgetName` before reporting a skip as success. Categorises failures by **type** + (`ScaffoldTimeoutError`, `MissingAnswerError`, `InvalidAnswerError`, `err.code === "ENOENT"/"EACCES"`), + never by matching message text. +- `widget-properties.tools.ts` — `set-widget-properties`. Resolves the widget name from + `package.json`'s `widgetName`; the directory basename is a poor fallback (`my-widget` → `My-widget` + fails PascalCase and blames the user for a name they never chose). +- `file-operations.tools.ts` — `list/read/write-widget-file`. Validates **every** path before writing + **any**. A partial write returns `fail`, not `ok` — it used to return a success envelope with + "Partial success" in the text, so a client checking `isError` saw a clean write. +- `build.tools.ts` — `build-widget`. +- `property-schema.ts` — the single Zod property model: `propertyDefinitionSchema`, + `propertyGroupSchema`, `systemPropertySchema`, `enumValueSchema`, `PROPERTY_TYPES`, + `ATTRIBUTE_TYPES`, `SYSTEM_PROPERTIES`, `parseMaybeStringifiedArray`. Lives under `tools/` because + `generators/` is deliberately Zod-free. +- `types.ts` — `ToolResponse` (type alias — see SKILL.md), `ToolContext`, `widgetOptionsSchema`, + `DEFAULT_WIDGET_OPTIONS`, `WidgetOptions`. +- `session-state.ts` — `SessionState`, `createSessionState()`. + +**Deleted:** `code-generation.tools.ts` and `property-update.tools.ts`, merged into +`widget-properties.tools.ts`. With them went `generatePropertySuggestions` (keyword matching that +produced duplicate keys), `detectTemplateMismatch` (a linter that ran _after_ writing files) and +`cleanupScaffoldFiles` (undisclosed `unlink` of unrelated files in `src/`, errors uncaught mid-loop). + +### Utilities (`src/tools/utils/`) + +- `answer-adapter.ts` — `AnswerAdapter`, `MissingAnswerError`, `InvalidAnswerError`. See SKILL.md. +- `generator.ts` — `runWidgetGenerator`, `runNpmInstall`, `buildWidgetOptions`, + `buildGeneratorAnswers`, `ScaffoldTimeoutError`, `ScaffoldResult`, `InstallResult`, + `SCAFFOLD_PROGRESS`. `yeoman-environment` is imported lazily so the STDIO path does not pay for its + dependency graph unless a widget is scaffolded. +- `response.ts` — `ok`, `fail`, `ErrorCode`, `FailureContext`. 500-char detail truncation. +- `sandbox.ts` — `allowedRoots`, `isPathAllowed`, `describeAllowedRoots`. +- `logger.ts` — `createLogger(tag)`, `MCP_LOG_LEVEL`. +- `mpk.ts` — `findMpkFile(widgetPath)`: recursive under `dist/`, returns the **newest by mtime**. It + used to return whichever `readdirSync` yielded first, so with `dist/1.0.0/` and `dist/1.0.1/` both + present `deploy-widget` could copy the stale artifact — and succeed silently. +- `notifications.ts`, `progress-tracker.ts` — client-facing progress and log messages. + +**Deleted:** `mpk-analyzer.ts` (no production callers; `execSync("unzip …")` interpolated a +caller-supplied path and needed a binary absent on Windows). + +### Generators (`src/generators/`) + +- `xml-generator.ts` — `generateWidgetXml`, `validateWidgetDefinition`. Pure, fs-free, Zod-free. + Validation covers PascalCase name, camelCase keys, **duplicate keys**, `attributeTypes` on + `attribute`, `enumValues` on `enumeration`, and property groups referencing unknown keys. +- `types.ts` — `WidgetDefinition`, `PropertyDefinition`, `PropertyGroup`, `SystemProperty`, + `MendixPropertyType`, `AttributeType`. + +**Deleted:** `tsx-generator.ts`. It hand-assembled React by string concatenation and emitted +non-compiling code in at least four cases (container calling `useCallback` without importing it; +`isCollapsible` falling back to the _string_ `"false"` spliced into source; enum compared to a raw +string; unused destructured props tripping `noUnusedLocals`). `docs/widget-patterns.md` covers the +same ground properly and is served as a resource. + +### Resources (`src/resources/`) + +- `guidelines.ts` — `GUIDELINE_RESOURCES`, `loadGuidelineContent(filename)`, module-level cache. +- Serves `mendix://guidelines/property-types` (`docs/property-types.md`) and + `mendix://guidelines/widget-patterns` (`docs/widget-patterns.md`) from `DOCS_DIR`. +- `docs` is in `package.json` `files` — without it these throw ENOENT in an installed copy. + +## Test inventory (14 files, 119 tests) + +| File | Covers | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `src/__tests__/config.test.ts` | `validateProjectDir` | +| `src/__tests__/scenarios/widget-lifecycle.test.ts` | Multi-tool workflows through the harness | +| `src/generators/__tests__/xml-generator.test.ts` | XML output per property type + all validation rules | +| `src/resources/__tests__/guidelines.test.ts` | Both guidelines load from disk | +| `src/security/__tests__/guardrails.test.ts` | Traversal, prefix attacks (`/widgets/foobar` vs `/widgets/foo`), extension allowlist | +| `src/tools/__tests__/build.tools.test.ts` | Sandbox checks, failure/success formatting | +| `src/tools/__tests__/file-operations.tools.test.ts` | list/read/write, validate-all-before-writing-any | +| `src/tools/__tests__/project.tools.test.ts` | Project config + real `.mpk` deploy | +| `src/tools/__tests__/scaffolding.tools.test.ts` | Sandbox + `widget-sources` default | +| `src/tools/__tests__/widget-properties.tools.test.ts` | XML written, declarative replacement, invalid definitions write nothing | +| `src/tools/utils/__tests__/answer-adapter.test.ts` | Answer precedence, `when` guards, `validate`, never-stdout | +| `src/tools/utils/__tests__/generator.test.ts` | **Real in-process scaffold**; pins all 14 prompt names | +| `src/tools/utils/__tests__/mpk.test.ts` | `findMpkFile` | +| `src/tools/utils/__tests__/response.test.ts` | `ok`/`fail` envelope, code rendering, truncation | + +`generator.test.ts` is the guard against upstream generator drift — if `@mendix/generator-widget` +renames a prompt, it fails there instead of silently producing a working-but-wrong widget. + +## Transports + +- STDIO: `StdioServerTransport`. Production. stdout is JSON-RPC only. +- HTTP: `StreamableHTTPServerTransport` with `sessionIdGenerator: undefined` (stateless). No + `mcp-session-id` handling, no `isInitializeRequest` branching — every POST is self-contained. + +## Widget id ↔ bundle path + +`xml-generator.ts:120` — `widget.id ?? \`${organization}.${widgetNameLower}.${widget.name}\``, e.g. +`mendix.counter.Counter`. Studio Pro converts dots to slashes to locate the bundle: +`mendix/counter/Counter.js`, which must exist inside the `.mpk`. + +A mismatch surfaces in Studio Pro as an **"ES6 modules" error**. The historical failure was +`com.mendix.widget.custom.counter.Counter`, which sends Studio Pro looking for +`com/mendix/widget/custom/counter/Counter.js`. + +## MPK structure + +A `.mpk` is a ZIP containing `package.xml` (client module name, version, widgetFile path), the widget +`.xml` (properties definition), and the JS bundle (AMD `define(` or ESM `export`). + +## Conventions + +- `@/` → `src/`; imports use no `.js` suffix internally (`tsc-alias` resolves at build). +- Every failure goes through `fail(code, …)`. No raw response literals. +- Tool modules export `registerXxxTools(server, state?)`. +- Vitest, not Jest. `restoreMocks: true` — see the SKILL.md trap. +- Logging via `createLogger`, never bare `console.*` — stdout is the protocol channel. +- Categorise errors by type or `err.code`, never by matching message text. diff --git a/packages/pluggable-widgets-mcp/docs/evaluation.md b/packages/pluggable-widgets-mcp/docs/evaluation.md new file mode 100644 index 0000000000..61a6d5b0d2 --- /dev/null +++ b/packages/pluggable-widgets-mcp/docs/evaluation.md @@ -0,0 +1,145 @@ +# How we evaluate this server + +This server turns a description of a Mendix widget into a working `.mpk` sitting in a project's +`widgets/` folder. This document explains how we prove that it works, and how fast it does it. + +Two questions, answered separately: + +- **Does it work?** — a test suite that runs the whole thing for real and compares the result + against a known-correct answer. +- **How good is it?** — timings recorded on every run, and an open-ended run where a model uses the + server with no help and we watch what happens. + +--- + +## What the server does + +Six steps, start to finish. A client calls them in order. + +| Step | What it does | +| ----------------------- | ------------------------------------------------------------------ | +| `get-project-info` | Finds the Mendix project and reports what widgets it already has | +| `create-widget` | Scaffolds a new widget into the project's `widget-sources/` folder | +| `set-widget-properties` | Writes the widget's XML definition from a list of properties | +| `write-widget-file` | Writes the component source the model authored | +| `build-widget` | Compiles it into a `.mpk` | +| `deploy-widget` | Copies the `.mpk` into the project so Studio Pro picks it up | + +The division of labour matters and the tests are built around it: **the server does what is +mechanically derivable, the model does what needs judgment.** XML is derived from a property list, so +the server owns it and we can check it against a golden file. Component code needs taste, so the +model writes it, guided by [`docs/widget-patterns.md`](./widget-patterns.md) — which the server +serves to the model as a resource. + +--- + +## How we prove it works + +Three layers, fastest first. Each catches something the layer above cannot. + +### Layer 1 — unit tests · `src/__tests__/`, `src/**/__tests__/` + +119 tests, a few seconds. They call the server's tools through a real client/server pair held in +memory, so a call still goes through protocol serialisation and input validation. They cover error +codes, path security, XML generation and the response contract. + +What they cannot catch: anything involving a real process, a real install, or a real build. + +`npm run test` + +### Layer 2 — end-to-end tests · `src/__e2e__/` + +The server is launched **as a real child process** and driven over the real protocol, exactly as +Studio Pro drives it. A real widget is scaffolded, really built, and the resulting `.mpk` is unzipped +and inspected. + +| File | What it proves | +| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `pipeline.e2e.test.ts` | The whole happy path. The generated XML matches a golden file **and** validates against Mendix's own schema. The `.mpk` is opened and its contents checked — a file existing is not the same as a file Studio Pro can load. | +| `transport.e2e.test.ts` | The server never writes stray output onto the channel it talks to Studio Pro over. One stray character corrupts the connection. Also: the server exits when Studio Pro does, instead of orphaning. | +| `failure-modes.e2e.test.ts` | A broken component reports the exact file, line and column. Attempts to write outside the project are refused — including when the server is started from a different folder, which used to move the boundary. | +| `docs-templates.e2e.test.ts` | Every code template in `docs/widget-patterns.md` actually compiles. Since the model writes component code from those templates, a broken template is a broken product. | +| `cold.e2e.test.ts` | A genuine from-scratch run with a real dependency install. This is the one that produces the headline timing. Off by default. | + +`npm run test:e2e` · from scratch: `E2E_COLD=1 npm run test:e2e` + +### Layer 3 — open-ended run + +A model is pointed at the server knowing nothing about it — no source code, only what the server +tells clients about itself — and given a plain instruction like _"add a rating-stars widget to my +app."_ We record every call it made, every wrong turn, and whether it reached a working widget. + +Layers 1 and 2 check that the server is correct. This layer checks that it is **usable** — that the +tool descriptions and instructions are good enough for a model to succeed without a human +translating. + +Run via the `mcp-server-test` skill with `--llm`. + +--- + +## How fast it is + +Every run records how long each step took. Results are appended to +[`docs/benchmarks/timings.jsonl`](./benchmarks/) with the commit they came from, so a change that +makes things slower shows up immediately instead of being noticed months later. + +Measured on 2026-07-29, macOS, from nothing to a deployed widget: + +| Step | Time | +| ------------------------------- | ---------------- | +| Scaffold + install dependencies | 23.3 s | +| Write the XML definition | 0.003 s | +| Write the component files | 0.002 s | +| Build the `.mpk` | 14.1 s | +| Deploy into the project | 0.003 s | +| **Total** | **≈ 37 seconds** | + +Rebuilding an existing widget after an edit: **≈ 4 seconds.** + +Almost all of it is the two steps the server doesn't control — npm installing dependencies, and the +Mendix build toolchain compiling. The server's own work is measured in **milliseconds**. That is the +number worth quoting: the pipeline adds essentially nothing to the cost of building a widget by hand, +and removes all the steps in between. + +--- + +## Running the whole thing + +The `mcp-server-test` skill runs the layers in order and summarises the result, including how the +timings compare to the previous run. + +| Command | What it runs | +| ------------------------- | ------------------------------------------------------------- | +| `/mcp-server-test` | Unit tests, then end-to-end tests, then the timing comparison | +| `/mcp-server-test --cold` | Adds the from-scratch run and records the headline number | +| `/mcp-server-test --llm` | Adds the open-ended run | + +Use it after any change to the server. The first two layers are a pass/fail gate; the third is a +judgment call you read. + +--- + +## Where things live + +| | | +| ------------------------------------ | --------------------------------------------------- | +| The server's tools | `src/tools/` | +| XML generation | `src/generators/xml-generator.ts` | +| Guidance served to the model | `docs/widget-patterns.md`, `docs/property-types.md` | +| Unit tests | `src/__tests__/`, `src/**/__tests__/` | +| End-to-end tests | `src/__e2e__/` | +| Known-correct XML to compare against | `src/__e2e__/goldens/` | +| Timing history | `docs/benchmarks/timings.jsonl` | +| The test workflow itself | `.claude/skills/mcp-server-test/` | +| Working notes on the server's design | `.claude/skills/pluggable-widgets-mcp/` | + +Both skills live in the repository, so anyone contributing gets them automatically — no local setup. + +--- + +## Status + +Layer 1 is in place and passing — 119 tests. Layers 2 and 3 and the timing history are designed and +not yet built; the numbers quoted above were measured by hand on 2026-07-29 while validating the +pipeline against a real Mendix project, and will be reproduced automatically once `src/__e2e__/` +lands. From a22c4ddeda8bf9ea595309659c60600f8b15459e Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 31 Jul 2026 16:16:56 +0200 Subject: [PATCH 32/36] fix(generators): point widget XML at the schema that actually exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every XML the server wrote declared xsi:schemaLocation="... ../../../../node_modules/mendix/custom_widget.xsd" The file is written to /src/, so four levels up is the Mendix project root, which has no node_modules. @mendix/generator-widget scaffolds "../node_modules/..." for the same file, which is correct: the widget's own install is one level up. Only XML editors read schemaLocation, so this never broke a build — it just meant nobody editing a generated .xml by hand got schema completion or validation, and the reference silently pointed at nothing. Found while blessing the first golden file for the end-to-end suite, which is what pins it from now on. --- .../pluggable-widgets-mcp/src/generators/xml-generator.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts index 5a3c3976d3..ddfa97c073 100644 --- a/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts +++ b/packages/pluggable-widgets-mcp/src/generators/xml-generator.ts @@ -130,7 +130,11 @@ export function generateWidgetXml(widget: WidgetDefinition): GeneratorResult { `offlineCapable="${offlineCapable}"`, `xmlns="http://www.mendix.com/widget/1.0/"`, `xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`, - `xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../../../../node_modules/mendix/custom_widget.xsd"` + // The XML is written to /src/, so the widget's own node_modules is one level up. + // This matched what @mendix/generator-widget scaffolds; four levels up resolved to the + // Mendix project root, which has no node_modules, leaving every generated file pointing + // at a schema that does not exist. Only XML editors read this, not the build. + `xsi:schemaLocation="http://www.mendix.com/widget/1.0/ ../node_modules/mendix/custom_widget.xsd"` ] .filter(Boolean) .join(" "); From a4c514afd057d7fc60d44ce2cc771e5868368f6e Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 31 Jul 2026 16:17:17 +0200 Subject: [PATCH 33/36] test(mcp): add an end-to-end suite that drives a real server process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing 119 tests run client and server in one process over InMemoryTransport. That is right for a fast gate, but it cannot see spawning, packaging, argv handling, stdout hygiene, or anything involving a real install or a real build — which is most of what breaks the Studio Pro integration. `npm run test:e2e` spawns dist/index.js as a child process and drives it over the real protocol. 24 specs, about 20 seconds warm. - pipeline: the happy path. The XML is compared byte for byte against a golden file and validated against Mendix's own custom_widget.xsd, and the .mpk is opened and its entries checked. A build reporting success while producing an archive Studio Pro cannot load is the failure this exists to catch. - transport: asserts every byte the server writes to stdout is a JSON-RPC frame. Under stdio that channel carries the protocol, so one stray console.log corrupts the connection. Also covers exit-on-stdin-close, so the process does not orphan when Studio Pro is killed, and argv validation. - failure-modes: a broken component reports file:line:column and does not inline file contents; duplicate keys are refused with nothing written; sandbox escapes are refused, checked twice — once from the package directory and once from an unrelated cwd, which is the regression test for the boundary that used to move with process.cwd(). - docs-templates: extracts every template from docs/widget-patterns.md verbatim, generates real XML through the real tool, generates real typings, and compiles them. That document is served to the client model and is where component code comes from, so a template that does not compile is a broken product. - cold: skipped unless E2E_COLD=1. A genuine scaffold with a real npm install, and the source of the headline timing. Timings are recorded per phase to docs/benchmarks/timings.jsonl, tagged with the commit, and the runner prints the delta against the previous run. Three specs were verified by breaking what they guard rather than trusting a green result: a console.log added to dist/ was caught by the stdout assertion; restoring the unpublished @mendix/widget-plugin-platform import was caught with the offending line in widget-patterns.md; and a missing golden refuses to self-bless, writing one and failing until it is reviewed. Two things the harness had to work around, both recorded in comments because they cost time to find. The warm cache scaffolds directly into its final location rather than being copied there: npm writes absolute symlinks into node_modules/.bin, so a copied cache points tsc at a deleted temp directory. And the typings generator resolves package.json from process.cwd() at import time, so it runs as a child process with cwd set to the widget. Measured from scratch: 25.0s total, of which the server's own steps account for 6 milliseconds. Recorded in docs/evaluation.md. --- packages/pluggable-widgets-mcp/.gitignore | 1 + .../docs/benchmarks/timings.jsonl | 5 + .../pluggable-widgets-mcp/docs/evaluation.md | 49 ++-- .../pluggable-widgets-mcp/eslint.config.mjs | 11 +- packages/pluggable-widgets-mcp/package.json | 2 + .../src/__e2e__/cold.e2e.test.ts | 102 +++++++++ .../src/__e2e__/docs-templates.e2e.test.ts | 198 ++++++++++++++++ .../src/__e2e__/failure-modes.e2e.test.ts | 216 ++++++++++++++++++ .../__e2e__/fixtures/badge.component.tsx.txt | 34 +++ .../__e2e__/fixtures/badge.properties.json | 42 ++++ .../src/__e2e__/fixtures/doc-patterns.json | 87 +++++++ .../src/__e2e__/goldens/ProbeBadge.xml | 49 ++++ .../src/__e2e__/pipeline.e2e.test.ts | 168 ++++++++++++++ .../src/__e2e__/support/fixture-widget.ts | 71 ++++++ .../src/__e2e__/support/generate-typings.mjs | 29 +++ .../src/__e2e__/support/harness.ts | 159 +++++++++++++ .../src/__e2e__/support/timeline.ts | 129 +++++++++++ .../src/__e2e__/support/warm-cache.ts | 134 +++++++++++ .../src/__e2e__/support/zip.ts | 48 ++++ .../src/__e2e__/transport.e2e.test.ts | 134 +++++++++++ .../vitest.e2e.config.ts | 25 ++ 21 files changed, 1677 insertions(+), 16 deletions(-) create mode 100644 packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/cold.e2e.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/docs-templates.e2e.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/failure-modes.e2e.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.component.tsx.txt create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.properties.json create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/fixtures/doc-patterns.json create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/pipeline.e2e.test.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/fixture-widget.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/generate-typings.mjs create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/harness.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/timeline.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/warm-cache.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/support/zip.ts create mode 100644 packages/pluggable-widgets-mcp/src/__e2e__/transport.e2e.test.ts create mode 100644 packages/pluggable-widgets-mcp/vitest.e2e.config.ts diff --git a/packages/pluggable-widgets-mcp/.gitignore b/packages/pluggable-widgets-mcp/.gitignore index 94ba000c8b..bc3054072d 100644 --- a/packages/pluggable-widgets-mcp/.gitignore +++ b/packages/pluggable-widgets-mcp/.gitignore @@ -1,3 +1,4 @@ dist/ generations/ node_modules/ +.e2e-cache/ diff --git a/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl new file mode 100644 index 0000000000..b8f7a0d5a7 --- /dev/null +++ b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl @@ -0,0 +1,5 @@ +{"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:05:17.918Z","phases":{"get-project-info":2,"set-widget-properties":2,"write-widget-file":2,"read-widget-file":5,"build-widget":16450,"deploy-widget":2},"totalToWorkingWidget":16463} +{"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:06:23.885Z","phases":{"get-project-info":2,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":1,"build-widget":9207,"deploy-widget":3},"totalToWorkingWidget":9218} +{"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:06:38.565Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":4,"read-widget-file":3,"build-widget":3633,"deploy-widget":2},"totalToWorkingWidget":3645} +{"sha":"4844bb59b","mode":"cold","ts":"2026-07-31T14:14:10.593Z","phases":{"create-widget":11584,"set-widget-properties":2,"write-widget-file":2,"build-widget":13419,"deploy-widget":2},"totalToWorkingWidget":25009} +{"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:15:46.773Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":7,"build-widget":6520,"deploy-widget":3},"totalToWorkingWidget":6536} diff --git a/packages/pluggable-widgets-mcp/docs/evaluation.md b/packages/pluggable-widgets-mcp/docs/evaluation.md index 61a6d5b0d2..a9a2879f1b 100644 --- a/packages/pluggable-widgets-mcp/docs/evaluation.md +++ b/packages/pluggable-widgets-mcp/docs/evaluation.md @@ -83,23 +83,32 @@ Every run records how long each step took. Results are appended to [`docs/benchmarks/timings.jsonl`](./benchmarks/) with the commit they came from, so a change that makes things slower shows up immediately instead of being noticed months later. -Measured on 2026-07-29, macOS, from nothing to a deployed widget: +Recorded by `cold.e2e.test.ts` on 2026-07-31, macOS — nothing on disk to a widget deployed into a +Mendix project: | Step | Time | | ------------------------------- | ---------------- | -| Scaffold + install dependencies | 23.3 s | -| Write the XML definition | 0.003 s | +| Scaffold + install dependencies | 11.6 s | +| Write the XML definition | 0.002 s | | Write the component files | 0.002 s | -| Build the `.mpk` | 14.1 s | -| Deploy into the project | 0.003 s | -| **Total** | **≈ 37 seconds** | +| Build the `.mpk` | 13.4 s | +| Deploy into the project | 0.002 s | +| **Total** | **25.0 seconds** | -Rebuilding an existing widget after an edit: **≈ 4 seconds.** +Rebuilding after an edit: **3.6 seconds.** -Almost all of it is the two steps the server doesn't control — npm installing dependencies, and the -Mendix build toolchain compiling. The server's own work is measured in **milliseconds**. That is the -number worth quoting: the pipeline adds essentially nothing to the cost of building a widget by hand, -and removes all the steps in between. +The shape of that table is the point. Two steps take all the time, and the server owns neither of +them: `npm install` and the Mendix build toolchain. Everything the server itself does — deriving the +XML, writing the files, deploying the artifact — adds up to **six milliseconds**. + +So the honest claim is not that this server is fast. It is that **the pipeline costs nothing**. A +developer doing this by hand pays the same 25 seconds of npm and compilation, plus the scaffolding +decisions, the XML by hand, and the copy into `widgets/`. The server removes those and adds +approximately zero. + +Two caveats worth stating before quoting the number. It assumes npm's package cache is warm; on a +machine downloading everything for the first time, expect closer to 40 seconds. And it is one +machine — the value of `timings.jsonl` is the trend across commits, not any single row. --- @@ -139,7 +148,17 @@ Both skills live in the repository, so anyone contributing gets them automatical ## Status -Layer 1 is in place and passing — 119 tests. Layers 2 and 3 and the timing history are designed and -not yet built; the numbers quoted above were measured by hand on 2026-07-29 while validating the -pipeline against a real Mendix project, and will be reproduced automatically once `src/__e2e__/` -lands. +Layers 1 and 2 are built and passing: **119 unit tests in 1.3 s**, **24 end-to-end tests in 20 s** +warm, **25 s** for the from-scratch run. Timings are recorded automatically. + +Layer 3 is a procedure rather than code — it runs through the `mcp-server-test` skill and produces +findings, not a pass/fail — so there is nothing to build, but it has not been exercised yet. + +Three of the end-to-end specs were verified to fail when the thing they guard is broken, rather than +being assumed correct because they were green: + +| Spec | Broken deliberately | Caught | +| ---------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------ | +| `transport.e2e.test.ts` | added a `console.log` to the built server | the stray line on stdout | +| `docs-templates.e2e.test.ts` | restored the unpublished `@mendix/widget-plugin-platform` import | `TS2307`, naming the template's line in `widget-patterns.md` | +| `pipeline.e2e.test.ts` | no golden file present | refused to self-bless; wrote one and failed pending review | diff --git a/packages/pluggable-widgets-mcp/eslint.config.mjs b/packages/pluggable-widgets-mcp/eslint.config.mjs index 466c5f6440..5f3ef86bca 100644 --- a/packages/pluggable-widgets-mcp/eslint.config.mjs +++ b/packages/pluggable-widgets-mcp/eslint.config.mjs @@ -15,7 +15,7 @@ import config from "@mendix/eslint-config-web-widgets/widget-ts.mjs"; */ export default [ { - ignores: ["dist/**", "generations/**"] + ignores: ["dist/**", "generations/**", ".e2e-cache/**"] }, ...config, { @@ -27,5 +27,14 @@ export default [ tsconfigRootDir: import.meta.dirname } } + }, + { + // The e2e suite has one plain ESM helper that has to run as its own process, so it is not + // TypeScript and the shared config has no Node globals for it. + name: "pluggable-widgets-mcp: node scripts", + files: ["**/*.mjs"], + languageOptions: { + globals: { process: "readonly", console: "readonly" } + } } ]; diff --git a/packages/pluggable-widgets-mcp/package.json b/packages/pluggable-widgets-mcp/package.json index dff627d466..08278aaebc 100644 --- a/packages/pluggable-widgets-mcp/package.json +++ b/packages/pluggable-widgets-mcp/package.json @@ -23,6 +23,8 @@ "start:http": "pnpm run build && node dist/index.js http", "start:stdio": "pnpm run build && node dist/index.js stdio", "test": "vitest run", + "test:e2e": "vitest run -c vitest.e2e.config.ts", + "test:e2e:cold": "E2E_COLD=1 vitest run -c vitest.e2e.config.ts", "test:watch": "vitest" }, "dependencies": { diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/cold.e2e.test.ts b/packages/pluggable-widgets-mcp/src/__e2e__/cold.e2e.test.ts new file mode 100644 index 0000000000..5755c3b6f9 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/cold.e2e.test.ts @@ -0,0 +1,102 @@ +/** + * From nothing to a deployed widget, with no cache and a real dependency install. + * + * This is the run the headline number comes from — "a working Mendix widget in N seconds" — so it + * deliberately shares nothing with the warm specs. It needs the network, takes the better part of a + * minute, and is skipped unless E2E_COLD is set. + * + * Everything the warm suite proves about behaviour is proved there. What this adds is the two things + * a cache necessarily hides: that scaffolding actually works against the published generator, and + * what the whole thing really costs. + */ + +import { afterAll, describe, expect, it } from "vitest"; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { startStdioServer } from "./support/harness"; +import { readBadgeComponent, readBadgeFixture, supportingFiles } from "./support/fixture-widget"; +import { appendTimingRow, formatSummary, Timeline } from "./support/timeline"; +import { readZipEntryNames } from "./support/zip"; + +const COLD = process.env.E2E_COLD === "1"; +const WIDGET_NAME = "ProbeBadge"; +const WIDGET_FOLDER = "probeBadge"; + +describe.skipIf(!COLD)("from scratch, with a real install", () => { + const timeline = new Timeline(); + let projectDir: string | undefined; + + afterAll(() => { + if (projectDir) rmSync(projectDir, { recursive: true, force: true }); + + const row = timeline.toRow("cold"); + appendTimingRow(row); + process.stderr.write(`${formatSummary(row)}\n`); + }); + + it("scaffolds, defines, writes, builds and deploys a widget", async () => { + projectDir = mkdtempSync(join(tmpdir(), "mcp-e2e-cold-")); + mkdirSync(join(projectDir, "widgets"), { recursive: true }); + writeFileSync(join(projectDir, "ColdApp.mpr"), ""); + + const server = await startStdioServer({ projectDir, timeline }); + + try { + const created = await server.call("create-widget", { + name: WIDGET_NAME, + description: "A badge that displays a text value and can trigger an action on click", + organization: "mendix", + programmingLanguage: "typescript", + template: "empty", + unitTests: false, + e2eTests: false + }); + + expect(created.isError, created.text).toBe(false); + expect(created.text).toContain("Dependencies installed."); + + // Scaffolding belongs to the project, not to wherever the server happens to be running. + const widgetPath = join(projectDir, "widget-sources", WIDGET_FOLDER); + expect(existsSync(join(widgetPath, "package.json"))).toBe(true); + expect( + (JSON.parse(readFileSync(join(widgetPath, "package.json"), "utf-8")) as { widgetName: string }) + .widgetName + ).toBe(WIDGET_NAME); + + const fixture = readBadgeFixture(); + const properties = await server.call("set-widget-properties", { widgetPath, ...fixture }); + expect(properties.isError, properties.text).toBe(false); + + const written = await server.call("write-widget-file", { + widgetPath, + files: [{ relativePath: `src/${WIDGET_NAME}.tsx`, content: readBadgeComponent() }, ...supportingFiles()] + }); + expect(written.isError, written.text).toBe(false); + + const built = await server.call("build-widget", { widgetPath }); + expect(built.isError, built.text).toBe(false); + + const mpk = /(\S+\.mpk)/.exec(built.text)?.[1]; + expect(mpk).toBeDefined(); + expect(readZipEntryNames(readFileSync(mpk!))).toContain(`${WIDGET_NAME}.xml`); + + const deployed = await server.call("deploy-widget", { widgetPath }); + expect(deployed.isError, deployed.text).toBe(false); + expect(existsSync(join(projectDir, "widgets", `mendix.${WIDGET_NAME}.mpk`))).toBe(true); + } finally { + await server.close(); + } + }); + + it("spends its time where we claim it does", () => { + // The pitch rests on this: the server's own steps are milliseconds, and the wall-clock is + // npm and the Mendix toolchain. If that ever stops being true, the claim needs rewriting. + for (const phase of ["set-widget-properties", "write-widget-file", "deploy-widget"]) { + expect(timeline.get(phase) ?? 0, `${phase} took longer than a moment`).toBeLessThan(1000); + } + + expect(timeline.get("create-widget") ?? 0).toBeGreaterThan(0); + expect(timeline.get("build-widget") ?? 0).toBeGreaterThan(0); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/docs-templates.e2e.test.ts b/packages/pluggable-widgets-mcp/src/__e2e__/docs-templates.e2e.test.ts new file mode 100644 index 0000000000..477249997d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/docs-templates.e2e.test.ts @@ -0,0 +1,198 @@ +/** + * Compiles every code template in docs/widget-patterns.md against real Mendix typings. + * + * That document is not documentation in the decorative sense. It is served to the client model as + * an MCP resource and is where the component code comes from, so a template that does not compile + * is a broken product — the model writes it, the build fails, and the failure looks like the user's + * fault. + * + * The templates are extracted verbatim. Nothing is patched on the way through: if a spec has to + * massage a template to make it compile, the template is what needs fixing. + * + * This spec is the one that would have caught templates importing + * @mendix/widget-plugin-platform — a package that resolves inside the web-widgets monorepo through + * workspace linking and returns 404 from npm, which no amount of type-checking inside that monorepo + * would ever reveal. + */ + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { execFileSync } from "node:child_process"; +import { copyFileSync, mkdirSync, readdirSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import { DOCS_DIR, PACKAGE_ROOT } from "@/config"; +import { type E2EServer, startStdioServer } from "./support/harness"; +import { ensureWarmCache, makeProjectDir, materializeWarmWidget, typescriptCompiler } from "./support/warm-cache"; + +const PATTERNS_DOC = join(DOCS_DIR, "widget-patterns.md"); +const FIXTURES = join(PACKAGE_ROOT, "src", "__e2e__", "fixtures"); + +interface Template { + name: string; + line: number; + code: string; +} + +interface PatternModel { + description: string; + properties: unknown[]; + systemProperties?: string[]; +} + +/** + * Pulls every fenced block out of the document and keeps the tsx ones that declare an exported + * component. Illustrative snippets — a handler, an import list — are not components and are not + * independently compilable, so they are skipped by construction rather than by a hand-kept list. + */ +function extractTemplates(markdown: string): Template[] { + const lines = markdown.split("\n"); + const templates: Template[] = []; + + for (let i = 0; i < lines.length; i++) { + const fence = /^```(\w+)?\s*$/.exec(lines[i]); + if (!fence) continue; + + let end = i + 1; + while (end < lines.length && !/^```\s*$/.test(lines[end])) end++; + + if (fence[1] === "tsx") { + const code = lines.slice(i + 1, end).join("\n"); + const declared = /export\s+(?:default\s+)?function\s+(\w+)\s*\(/.exec(code); + if (declared) { + templates.push({ name: declared[1], line: i + 2, code }); + } + } + i = end; + } + + return templates; +} + +describe("widget-patterns.md templates compile", () => { + const models = ( + JSON.parse(readFileSync(join(FIXTURES, "doc-patterns.json"), "utf-8")) as { + patterns: Record; + } + ).patterns; + + const templates = extractTemplates(readFileSync(PATTERNS_DOC, "utf-8")); + + let projectDir: string; + let widgetPath: string; + let server: E2EServer; + + beforeAll(async () => { + const cache = await ensureWarmCache(); + projectDir = makeProjectDir(); + widgetPath = materializeWarmWidget(cache, projectDir); + server = await startStdioServer({ projectDir }); + }); + + afterAll(async () => { + await server?.close(); + if (projectDir) rmSync(projectDir, { recursive: true, force: true }); + }); + + it("finds templates in the document", () => { + expect( + templates.length, + "no component templates found — has the document's structure changed?" + ).toBeGreaterThan(0); + }); + + it("has a property model for every template", () => { + const missing = templates.map(t => t.name).filter(name => !(name in models)); + + expect( + missing, + `Templates with no entry in fixtures/doc-patterns.json would be silently skipped. Add a model for: ${missing.join(", ")}` + ).toEqual([]); + }); + + it("compiles every template against typings generated from real XML", async () => { + const src = join(widgetPath, "src"); + + // Start from a clean src: the fixture widget's own component would otherwise be compiled + // alongside the templates and could mask, or invent, an error. + for (const entry of readdirSync(src)) { + if (entry.endsWith(".tsx") || entry.endsWith(".ts") || entry.endsWith(".xml")) { + if (entry !== "package.xml") unlinkSync(join(src, entry)); + } + } + + // Each template needs its own widget XML, and set-widget-properties derives the widget name + // from package.json — so each gets a throwaway directory declaring that name. Going through + // the real tool means the XML under test is the XML the server actually produces. + for (const template of templates) { + const model = models[template.name]; + const defDir = join(projectDir, "widget-sources", `.doc-${template.name}`); + mkdirSync(join(defDir, "src"), { recursive: true }); + writeFileSync( + join(defDir, "package.json"), + JSON.stringify({ name: template.name.toLowerCase(), widgetName: template.name }, null, 2) + ); + + const result = await server.call("set-widget-properties", { + widgetPath: defDir, + description: model.description, + properties: model.properties, + ...(model.systemProperties ? { systemProperties: model.systemProperties } : {}) + }); + expect(result.isError, `could not build XML for ${template.name}:\n${result.text}`).toBe(false); + + copyFileSync(join(defDir, "src", `${template.name}.xml`), join(src, `${template.name}.xml`)); + writeFileSync(join(src, `${template.name}.tsx`), `${template.code}\n`); + + // The templates import a stylesheet next to themselves; create it so resolution is real. + mkdirSync(join(src, "ui"), { recursive: true }); + writeFileSync(join(src, "ui", `${template.name}.scss`), `.widget-${template.name.toLowerCase()} {\n}\n`); + } + + // The typings generator reads package.xml, so every widget has to be listed there. + writeFileSync( + join(src, "package.xml"), + [ + ``, + ``, + ` `, + ` `, + ...templates.map(t => ` `), + ` `, + ` `, + ` `, + ` `, + ` `, + ``, + `` + ].join("\n") + ); + + // Runs in a child process with cwd set to the widget — see generate-typings.mjs. + execFileSync( + process.execPath, + [join(PACKAGE_ROOT, "src", "__e2e__", "support", "generate-typings.mjs"), widgetPath], + { + cwd: widgetPath, + stdio: "pipe" + } + ); + + let output = ""; + let failed = false; + try { + // Addressed directly rather than through npx: the widget's node_modules/.bin entries are + // symlinks written by npm, and resolving them depends on where the install happened. + execFileSync(process.execPath, [typescriptCompiler(widgetPath), "--noEmit"], { + cwd: widgetPath, + encoding: "utf-8", + stdio: "pipe" + }); + } catch (error) { + failed = true; + const e = error as { stdout?: string; stderr?: string }; + output = `${e.stdout ?? ""}${e.stderr ?? ""}`; + } + + const located = templates.map(t => `${t.name} (widget-patterns.md line ${t.line})`).join(", "); + expect(failed, `Templates do not compile. Checked: ${located}\n\n${output}`).toBe(false); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/failure-modes.e2e.test.ts b/packages/pluggable-widgets-mcp/src/__e2e__/failure-modes.e2e.test.ts new file mode 100644 index 0000000000..abac0d7965 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/failure-modes.e2e.test.ts @@ -0,0 +1,216 @@ +/** + * The paths that matter when something is wrong. + * + * A pipeline that works when everything is correct is easy. What decides whether a model can use + * this server is whether a failure tells it enough to recover: which file, which line, which + * argument. And whether a refusal is actually a refusal. + */ + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { type E2EServer, startStdioServer } from "./support/harness"; +import { applyBadgeFixture } from "./support/fixture-widget"; +import { ensureWarmCache, makeProjectDir, materializeWarmWidget, WARM_WIDGET_NAME } from "./support/warm-cache"; + +describe("build failures", () => { + let projectDir: string; + let widgetPath: string; + let server: E2EServer; + + beforeAll(async () => { + const cache = await ensureWarmCache(); + projectDir = makeProjectDir(); + widgetPath = materializeWarmWidget(cache, projectDir); + server = await startStdioServer({ projectDir }); + + // Start from a widget that builds, so the only error the build reports is the one injected + // below. Otherwise the first failure comes from the scaffold's own preview files. + await applyBadgeFixture(server, widgetPath); + }); + + afterAll(async () => { + await server?.close(); + if (projectDir) rmSync(projectDir, { recursive: true, force: true }); + }); + + it("reports the exact file, line and column for a type error", async () => { + await server.call("write-widget-file", { + widgetPath, + filePath: `src/${WARM_WIDGET_NAME}.tsx`, + content: [ + `import { ReactElement } from "react";`, + `import { ${WARM_WIDGET_NAME}ContainerProps } from "../typings/${WARM_WIDGET_NAME}Props";`, + ``, + `export function ${WARM_WIDGET_NAME}(props: ${WARM_WIDGET_NAME}ContainerProps): ReactElement {`, + ` const broken: number = props.badgeType;`, + ` return
{broken}
;`, + `}`, + `` + ].join("\n") + }); + + const result = await server.call("build-widget", { widgetPath }); + + expect(result.isError).toBe(true); + expect(result.text).toContain("ERR_BUILD_FAILED"); + // A location, not just "the build failed" — this is what lets a model fix it unaided. + expect(result.text).toMatch(new RegExp(`src/${WARM_WIDGET_NAME}\\.tsx:\\d+:\\d+`)); + }); + + it("does not embed file contents in the failure, only locations", async () => { + const result = await server.call("build-widget", { widgetPath }); + + expect(result.isError).toBe(true); + // Inlining sources was unbounded; read-widget-file exists for that. + expect(result.text).not.toContain("export function"); + expect(result.text.length).toBeLessThan(4000); + }); +}); + +describe("refusals", () => { + let projectDir: string; + let widgetPath: string; + let server: E2EServer; + + beforeAll(async () => { + const cache = await ensureWarmCache(); + projectDir = makeProjectDir(); + widgetPath = materializeWarmWidget(cache, projectDir); + server = await startStdioServer({ projectDir }); + }); + + afterAll(async () => { + await server?.close(); + if (projectDir) rmSync(projectDir, { recursive: true, force: true }); + }); + + it("refuses to write outside the widget directory", async () => { + const result = await server.call("write-widget-file", { + widgetPath, + filePath: "../../../../../../tmp/mcp-escape-probe.tsx", + content: "export const escaped = true;\n" + }); + + expect(result.isError).toBe(true); + expect(existsSync("/tmp/mcp-escape-probe.tsx")).toBe(false); + }); + + it("refuses a disallowed file extension", async () => { + const result = await server.call("write-widget-file", { + widgetPath, + filePath: "src/payload.sh", + content: "#!/bin/sh\necho hi\n" + }); + + expect(result.isError).toBe(true); + expect(existsSync(join(widgetPath, "src", "payload.sh"))).toBe(false); + }); + + it("rejects duplicate property keys and writes nothing", async () => { + const before = readFileSync(join(widgetPath, "src", `${WARM_WIDGET_NAME}.xml`), "utf-8"); + + const result = await server.call("set-widget-properties", { + widgetPath, + description: "duplicate keys", + properties: [ + { key: "value", type: "string", caption: "First" }, + { key: "value", type: "string", caption: "Second" } + ] + }); + + expect(result.isError).toBe(true); + expect(result.text).toContain("ERR_INVALID_DEFINITION"); + expect(readFileSync(join(widgetPath, "src", `${WARM_WIDGET_NAME}.xml`), "utf-8")).toBe(before); + }); + + it("refuses to deploy a widget that has not been built", async () => { + rmSync(join(widgetPath, "dist"), { recursive: true, force: true }); + + const result = await server.call("deploy-widget", { widgetPath }); + + expect(result.isError).toBe(true); + }); +}); + +describe("the sandbox boundary does not move with the working directory", () => { + let projectDir: string; + let widgetPath: string; + let elsewhere: string; + + beforeAll(async () => { + const cache = await ensureWarmCache(); + projectDir = makeProjectDir(); + widgetPath = materializeWarmWidget(cache, projectDir); + elsewhere = mkdtempSync(join(tmpdir(), "mcp-e2e-elsewhere-")); + }); + + afterAll(() => { + rmSync(projectDir, { recursive: true, force: true }); + rmSync(elsewhere, { recursive: true, force: true }); + }); + + /** + * The regression test for the bug that motivated the refactor: the sandbox root used to derive + * from process.cwd(), so the fence moved depending on who spawned the server. Studio Pro spawns + * it from its own install directory. + */ + it.each([ + ["the package directory", undefined], + ["an unrelated directory", () => elsewhere] + ])("refuses an escape when started from %s", async (_label, cwdFn) => { + const server = await startStdioServer({ projectDir, cwd: cwdFn?.() }); + try { + const probe = join(elsewhere, "escaped.tsx"); + const result = await server.call("write-widget-file", { + widgetPath, + filePath: `../../../../../..${probe}`, + content: "export const escaped = true;\n" + }); + + expect(result.isError).toBe(true); + expect(existsSync(probe)).toBe(false); + + // And the project itself is still writable from either cwd — a fence that refuses + // everything is not a fence, it is a broken server. + const legitimate = await server.call("write-widget-file", { + widgetPath, + filePath: "src/ui/boundary-probe.scss", + content: ".probe { color: red; }\n" + }); + expect(legitimate.isError).toBe(false); + } finally { + await server.close(); + } + }); +}); + +describe("project configuration", () => { + it("fails every tool with a recoverable error when no project is configured", async () => { + const server = await startStdioServer({ projectDir: "" }); + try { + const result = await server.call("get-project-info"); + + expect(result.isError).toBe(true); + expect(result.text).toContain("ERR_PROJECT_NOT_CONFIGURED"); + expect(result.text).toContain("set-project-directory"); + } finally { + await server.close(); + } + }); + + it("refuses a directory that is not a Mendix project", async () => { + const notAProject = mkdtempSync(join(tmpdir(), "mcp-e2e-not-a-project-")); + writeFileSync(join(notAProject, "README.md"), "no .mpr here\n"); + + const server = await startStdioServer({ projectDir: "" }); + try { + const result = await server.call("set-project-directory", { projectDir: notAProject }); + expect(result.isError).toBe(true); + } finally { + await server.close(); + rmSync(notAProject, { recursive: true, force: true }); + } + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.component.tsx.txt b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.component.tsx.txt new file mode 100644 index 0000000000..c976c3cb9d --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.component.tsx.txt @@ -0,0 +1,34 @@ +import { ReactElement, useCallback } from "react"; +import { ActionValue } from "mendix"; +import { ProbeBadgeContainerProps } from "../typings/ProbeBadgeProps"; +import "./ui/ProbeBadge.scss"; + +function executeAction(action?: ActionValue): void { + if (action && action.canExecute && !action.isExecuting) { + action.execute(); + } +} + +export function ProbeBadge(props: ProbeBadgeContainerProps): ReactElement { + const { value, badgeType, count, showCount, onClick, tabIndex, class: className, style } = props; + + const handleClick = useCallback(() => { + executeAction(onClick); + }, [onClick]); + + const isClickable = onClick?.canExecute ?? false; + const countLabel = showCount ? (count?.value?.toNumber() ?? 0) : undefined; + + return ( +
+ {value?.value ?? ""} + {countLabel !== undefined && {countLabel}} +
+ ); +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.properties.json b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.properties.json new file mode 100644 index 0000000000..ac98f1f625 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/badge.properties.json @@ -0,0 +1,42 @@ +{ + "description": "A badge that displays a text value and can trigger an action on click", + "properties": [ + { + "key": "value", + "type": "textTemplate", + "caption": "Value", + "description": "Text shown inside the badge" + }, + { + "key": "badgeType", + "type": "enumeration", + "caption": "Badge type", + "defaultValue": "primary", + "enumValues": [ + { "key": "primary", "caption": "Primary" }, + { "key": "success", "caption": "Success" }, + { "key": "danger", "caption": "Danger" } + ] + }, + { + "key": "count", + "type": "attribute", + "caption": "Count", + "attributeTypes": ["Integer", "Long"], + "required": false + }, + { + "key": "showCount", + "type": "boolean", + "caption": "Show count", + "defaultValue": false + }, + { + "key": "onClick", + "type": "action", + "caption": "On click", + "required": false + } + ], + "systemProperties": ["Name", "TabIndex", "Visibility"] +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/doc-patterns.json b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/doc-patterns.json new file mode 100644 index 0000000000..33832053f7 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/fixtures/doc-patterns.json @@ -0,0 +1,87 @@ +{ + "comment": "Property models for the templates in docs/widget-patterns.md, keyed by the component name each template exports. The doc's own 'Typical Properties' blocks are illustrative and contain placeholders, so the concrete models live here. Adding a pattern to the doc means adding its model here, or the compile check silently skips it — the spec asserts every template is covered.", + "patterns": { + "MyWidget": { + "description": "Display pattern from widget-patterns.md", + "properties": [ + { "key": "value", "type": "textTemplate", "caption": "Value" }, + { + "key": "type", + "type": "enumeration", + "caption": "Style", + "defaultValue": "primary", + "enumValues": [ + { "key": "primary", "caption": "Primary" }, + { "key": "secondary", "caption": "Secondary" } + ] + }, + { "key": "onClick", "type": "action", "caption": "On click", "required": false } + ] + }, + "MyButton": { + "description": "Button pattern from widget-patterns.md", + "properties": [ + { "key": "caption", "type": "textTemplate", "caption": "Caption" }, + { "key": "icon", "type": "icon", "caption": "Icon", "required": false }, + { + "key": "buttonStyle", + "type": "enumeration", + "caption": "Style", + "defaultValue": "primary", + "enumValues": [ + { "key": "primary", "caption": "Primary" }, + { "key": "secondary", "caption": "Secondary" }, + { "key": "danger", "caption": "Danger" } + ] + }, + { "key": "onClick", "type": "action", "caption": "On click" } + ] + }, + "MyInput": { + "description": "Input pattern from widget-patterns.md", + "properties": [ + { + "key": "value", + "type": "attribute", + "caption": "Value", + "attributeTypes": ["String"], + "required": true + }, + { "key": "placeholder", "type": "textTemplate", "caption": "Placeholder", "required": false }, + { "key": "readOnly", "type": "boolean", "caption": "Read-only", "defaultValue": false }, + { "key": "onChange", "type": "action", "caption": "On change", "required": false }, + { "key": "onEnter", "type": "action", "caption": "On enter", "required": false } + ] + }, + "MyContainer": { + "description": "Container pattern from widget-patterns.md", + "properties": [ + { "key": "content", "type": "widgets", "caption": "Content" }, + { "key": "header", "type": "textTemplate", "caption": "Header", "required": false }, + { "key": "collapsible", "type": "boolean", "caption": "Collapsible", "defaultValue": false } + ] + }, + "MyList": { + "description": "Data list pattern from widget-patterns.md", + "systemProperties": ["Name", "Visibility"], + "properties": [ + { "key": "dataSource", "type": "datasource", "caption": "Data source", "isList": true }, + { "key": "content", "type": "widgets", "caption": "Content", "dataSource": "dataSource" }, + { "key": "emptyMessage", "type": "textTemplate", "caption": "Empty message", "required": false }, + { "key": "onItemClick", "type": "action", "caption": "On item click", "required": false } + ] + }, + "Counter": { + "description": "Counter pattern from the numeric-attributes section of widget-patterns.md", + "properties": [ + { + "key": "counterValue", + "type": "attribute", + "caption": "Counter value", + "attributeTypes": ["Integer", "Long"], + "required": true + } + ] + } + } +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml b/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml new file mode 100644 index 0000000000..1b4d51a019 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml @@ -0,0 +1,49 @@ + + + ProbeBadge + A badge that displays a text value and can trigger an action on click + Display + Display + + + + Value + Text shown inside the badge + + + Badge type + + + Primary + Success + Danger + + + + Count + + + + + + + + Show count + + + + + + On click + + + + + + + + + + + + diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/pipeline.e2e.test.ts b/packages/pluggable-widgets-mcp/src/__e2e__/pipeline.e2e.test.ts new file mode 100644 index 0000000000..8247ba0ee0 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/pipeline.e2e.test.ts @@ -0,0 +1,168 @@ +/** + * The happy path, start to finish, against a real server process. + * + * The assertions deliberately go past "the tool returned success": the XML is compared to a golden + * file and validated against Mendix's own schema, and the .mpk is opened and its contents checked. + * A build reporting success while producing an archive Studio Pro cannot load is exactly the class + * of failure this suite exists to catch. + */ + +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { execFileSync } from "node:child_process"; +import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { PACKAGE_ROOT } from "@/config"; +import { type E2EServer, startStdioServer } from "./support/harness"; +import { appendTimingRow, formatSummary, Timeline } from "./support/timeline"; +import { ensureWarmCache, makeProjectDir, materializeWarmWidget, WARM_WIDGET_NAME } from "./support/warm-cache"; +import { readZipEntryNames } from "./support/zip"; + +const FIXTURES = join(PACKAGE_ROOT, "src", "__e2e__", "fixtures"); +const GOLDENS = join(PACKAGE_ROOT, "src", "__e2e__", "goldens"); + +interface BadgeFixture { + description: string; + properties: unknown[]; + systemProperties: string[]; +} + +const badge = JSON.parse(readFileSync(join(FIXTURES, "badge.properties.json"), "utf-8")) as BadgeFixture; +const component = readFileSync(join(FIXTURES, "badge.component.tsx.txt"), "utf-8"); + +/** xmllint ships with macOS and most Linux images, but is not guaranteed. */ +function hasXmllint(): boolean { + try { + execFileSync("xmllint", ["--version"], { stdio: "ignore" }); + return true; + } catch { + return false; + } +} + +describe("full pipeline", () => { + const timeline = new Timeline(); + let projectDir: string; + let widgetPath: string; + let server: E2EServer; + + beforeAll(async () => { + const cache = await ensureWarmCache(); + projectDir = makeProjectDir(); + widgetPath = materializeWarmWidget(cache, projectDir); + server = await startStdioServer({ projectDir, timeline }); + }); + + afterAll(async () => { + await server?.close(); + if (projectDir) rmSync(projectDir, { recursive: true, force: true }); + + const row = timeline.toRow("warm"); + appendTimingRow(row); + process.stderr.write(`${formatSummary(row)}\n`); + }); + + it("get-project-info finds the project and reports its widgets", async () => { + const result = await server.call("get-project-info"); + + expect(result.isError).toBe(false); + expect(result.text).toContain("E2eApp"); + }); + + it("set-widget-properties writes XML matching the golden file", async () => { + const result = await server.call("set-widget-properties", { + widgetPath, + description: badge.description, + properties: badge.properties, + systemProperties: badge.systemProperties + }); + + expect(result.isError).toBe(false); + + const written = readFileSync(join(widgetPath, "src", `${WARM_WIDGET_NAME}.xml`), "utf-8"); + const goldenPath = join(GOLDENS, `${WARM_WIDGET_NAME}.xml`); + + // Regenerating the golden is a deliberate act, not a side effect of running the suite: a + // self-healing golden asserts nothing. + if (!existsSync(goldenPath)) { + mkdirSync(dirname(goldenPath), { recursive: true }); + writeFileSync(goldenPath, written, "utf-8"); + throw new Error( + `No golden file existed, so one was written to ${goldenPath}. Review it and commit it, then re-run.` + ); + } + + expect(written).toBe(readFileSync(goldenPath, "utf-8")); + }); + + it.skipIf(!hasXmllint())("the generated XML validates against the Mendix widget schema", () => { + const xsd = join(widgetPath, "node_modules", "mendix", "custom_widget.xsd"); + expect(existsSync(xsd)).toBe(true); + + // Throws on a validation failure; the message carries the offending line. + execFileSync("xmllint", ["--noout", "--schema", xsd, join(widgetPath, "src", `${WARM_WIDGET_NAME}.xml`)], { + stdio: "pipe" + }); + }); + + it("write-widget-file writes the component, and read-widget-file returns it unchanged", async () => { + const write = await server.call("write-widget-file", { + widgetPath, + files: [ + { relativePath: `src/${WARM_WIDGET_NAME}.tsx`, content: component }, + { + relativePath: `src/ui/${WARM_WIDGET_NAME}.scss`, + content: ".widget-probebadge {\n display: inline-flex;\n gap: 4px;\n}\n" + }, + { + relativePath: `src/${WARM_WIDGET_NAME}.editorPreview.tsx`, + content: `import { ReactElement } from "react";\nimport { ${WARM_WIDGET_NAME}PreviewProps } from "../typings/${WARM_WIDGET_NAME}Props";\n\nexport function preview({ value }: ${WARM_WIDGET_NAME}PreviewProps): ReactElement {\n return
{value}
;\n}\n` + }, + { + relativePath: `src/${WARM_WIDGET_NAME}.editorConfig.ts`, + content: `import { ${WARM_WIDGET_NAME}PreviewProps } from "../typings/${WARM_WIDGET_NAME}Props";\n\nexport function getProperties(_values: ${WARM_WIDGET_NAME}PreviewProps, defaultProperties: unknown): unknown {\n return defaultProperties;\n}\n` + } + ] + }); + + expect(write.isError).toBe(false); + + const read = await server.call("read-widget-file", { + widgetPath, + filePath: `src/${WARM_WIDGET_NAME}.tsx` + }); + expect(read.isError).toBe(false); + expect(read.text).toContain("executeAction"); + expect(read.text).toContain(`export function ${WARM_WIDGET_NAME}`); + }); + + it("build-widget produces an .mpk Studio Pro can load", async () => { + const result = await server.call("build-widget", { widgetPath }); + + expect(result.isError).toBe(false); + + const match = /(\S+\.mpk)/.exec(result.text); + expect(match, `no .mpk path in the build output:\n${result.text}`).not.toBeNull(); + + const mpkPath = match![1]; + expect(existsSync(mpkPath)).toBe(true); + + // The archive layout is what determines whether the widget loads, so check inside it. + const entries = readZipEntryNames(readFileSync(mpkPath)); + expect(entries).toContain(`${WARM_WIDGET_NAME}.xml`); + expect(entries).toContain("package.xml"); + expect(entries.some(e => e.endsWith(`${WARM_WIDGET_NAME}.js`))).toBe(true); + }); + + it("deploy-widget copies the .mpk into the project, and reports a replacement on redeploy", async () => { + const first = await server.call("deploy-widget", { widgetPath }); + expect(first.isError).toBe(false); + expect(first.text).toContain("Deployed"); + + const deployed = join(projectDir, "widgets", `mendix.${WARM_WIDGET_NAME}.mpk`); + expect(existsSync(deployed)).toBe(true); + + const second = await server.call("deploy-widget", { widgetPath }); + expect(second.isError).toBe(false); + expect(second.text).toContain("Replaced"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/fixture-widget.ts b/packages/pluggable-widgets-mcp/src/__e2e__/support/fixture-widget.ts new file mode 100644 index 0000000000..f0bcc9d2ea --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/fixture-widget.ts @@ -0,0 +1,71 @@ +/** + * Brings the warm fixture widget to a known-good, buildable state. + * + * Worth knowing: `set-widget-properties` replaces the widget's XML, which regenerates its typings. + * The scaffold's own `editorPreview.tsx` and `editorConfig.ts` reference the template's `sampleText` + * property, so they stop compiling the moment the XML changes. A real client hits this too — which + * is why the Studio Pro preview files are part of what a caller is expected to write, not an + * afterthought. + */ + +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { PACKAGE_ROOT } from "@/config"; +import type { E2EServer } from "./harness"; +import { WARM_WIDGET_NAME } from "./warm-cache"; + +const FIXTURES = join(PACKAGE_ROOT, "src", "__e2e__", "fixtures"); + +export interface BadgeFixture { + description: string; + properties: unknown[]; + systemProperties: string[]; +} + +export function readBadgeFixture(): BadgeFixture { + return JSON.parse(readFileSync(join(FIXTURES, "badge.properties.json"), "utf-8")) as BadgeFixture; +} + +export function readBadgeComponent(): string { + return readFileSync(join(FIXTURES, "badge.component.tsx.txt"), "utf-8"); +} + +/** The files a caller must supply alongside the component for the build to succeed. */ +export function supportingFiles(): Array<{ relativePath: string; content: string }> { + const name = WARM_WIDGET_NAME; + return [ + { + relativePath: `src/ui/${name}.scss`, + content: ".widget-probebadge {\n display: inline-flex;\n gap: 4px;\n}\n" + }, + { + relativePath: `src/${name}.editorPreview.tsx`, + content: + `import { ReactElement } from "react";\n` + + `import { ${name}PreviewProps } from "../typings/${name}Props";\n\n` + + `export function preview({ value }: ${name}PreviewProps): ReactElement {\n` + + ` return
{value}
;\n}\n` + }, + { + relativePath: `src/${name}.editorConfig.ts`, + content: + `import { ${name}PreviewProps } from "../typings/${name}Props";\n\n` + + `export function getProperties(_values: ${name}PreviewProps, defaultProperties: unknown): unknown {\n` + + ` return defaultProperties;\n}\n` + } + ]; +} + +/** Applies properties and every source file, leaving the widget in a state that builds cleanly. */ +export async function applyBadgeFixture(server: E2EServer, widgetPath: string): Promise { + const fixture = readBadgeFixture(); + + const properties = await server.call("set-widget-properties", { widgetPath, ...fixture }); + if (properties.isError) throw new Error(`fixture setup failed:\n${properties.text}`); + + const write = await server.call("write-widget-file", { + widgetPath, + files: [{ relativePath: `src/${WARM_WIDGET_NAME}.tsx`, content: readBadgeComponent() }, ...supportingFiles()] + }); + if (write.isError) throw new Error(`fixture setup failed:\n${write.text}`); +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/generate-typings.mjs b/packages/pluggable-widgets-mcp/src/__e2e__/support/generate-typings.mjs new file mode 100644 index 0000000000..8348c1dbe0 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/generate-typings.mjs @@ -0,0 +1,29 @@ +/** + * Generates a widget's typings/*Props.d.ts from its XML, the same way `pluggable-widgets-tools` + * does during a build. + * + * Run as a child process with cwd set to the widget directory. That is not incidental: the tool + * reads the widget's package.json from process.cwd() at import time, so importing it from inside the + * test process picks up pluggable-widgets-mcp's own package.json and fails with "Widget does not + * define widgetName". + * + * Usage: node generate-typings.mjs + */ + +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; + +const widgetDir = process.argv[2]; +if (!widgetDir) { + console.error("usage: node generate-typings.mjs "); + process.exit(2); +} + +const entry = pathToFileURL( + join(widgetDir, "node_modules", "@mendix", "pluggable-widgets-tools", "dist", "typings-generator", "index.js") +).href; + +const { transformPackage } = await import(entry); +const src = join(widgetDir, "src"); +await transformPackage(readFileSync(join(src, "package.xml"), "utf-8"), src); diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/harness.ts b/packages/pluggable-widgets-mcp/src/__e2e__/support/harness.ts new file mode 100644 index 0000000000..0ce97b7601 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/harness.ts @@ -0,0 +1,159 @@ +/** + * Drives the built server the way Studio Pro does: as a real child process over a real transport. + * + * This is the whole reason the e2e suite exists. `src/__test-utils__/mcp-test-harness.ts` runs a + * client and server in one process over InMemoryTransport, which is right for the fast suite but + * cannot see anything about spawning, packaging, argv handling, stdout hygiene or process + * lifecycle — the failure modes that actually break the Studio Pro integration. + * + * Specs run against `dist/`, not `src/`. Build before running, or you are testing the previous + * version of the server. + */ + +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; +import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process"; +import { existsSync } from "node:fs"; +import { join } from "node:path"; +import { PACKAGE_ROOT } from "@/config"; +import type { Timeline } from "./timeline"; + +export const SERVER_ENTRY = join(PACKAGE_ROOT, "dist", "index.js"); + +/** Fails with an actionable message rather than a confusing ENOENT from deep inside the SDK. */ +export function assertBuilt(): void { + if (!existsSync(SERVER_ENTRY)) { + throw new Error(`${SERVER_ENTRY} does not exist. Run "npm run build" before the e2e suite.`); + } +} + +export interface ToolCallResult { + text: string; + isError: boolean; + ms: number; +} + +export interface E2EServer { + client: Client; + /** Calls a tool and returns its flattened text plus how long it took. */ + call(name: string, args?: Record): Promise; + listToolNames(): Promise; + listResourceUris(): Promise; + close(): Promise; +} + +export interface StartServerOptions { + projectDir: string; + /** Where to spawn the process from. The sandbox must not depend on this — see failure-modes. */ + cwd?: string; + /** When given, every call is filed under a phase name matching the tool. */ + timeline?: Timeline; + env?: Record; +} + +/** + * Starts the server over stdio and returns a connected client. + * + * `env` is passed explicitly rather than inherited wholesale: MENDIX_PROJECT_DIR leaking in from the + * developer's shell would silently point specs at a real project. + */ +export async function startStdioServer(options: StartServerOptions): Promise { + assertBuilt(); + + const transport = new StdioClientTransport({ + command: process.execPath, + args: [SERVER_ENTRY, "stdio"], + cwd: options.cwd ?? PACKAGE_ROOT, + env: { + PATH: process.env.PATH ?? "", + HOME: process.env.HOME ?? "", + MENDIX_PROJECT_DIR: options.projectDir, + MCP_LOG_LEVEL: process.env.MCP_LOG_LEVEL ?? "warn", + ...options.env + }, + stderr: "pipe" + }); + + const client = new Client({ name: "e2e-harness", version: "1.0.0" }); + await client.connect(transport); + + const call = async (name: string, args: Record = {}): Promise => { + const started = Date.now(); + const invoke = async (): Promise => { + const result = await client.callTool({ name, arguments: args }, undefined, { timeout: 600_000 }); + const content = (result.content ?? []) as Array<{ type: string; text?: string }>; + return { + text: content.map(c => c.text ?? `[${c.type}]`).join("\n"), + isError: result.isError === true, + ms: Date.now() - started + }; + }; + + return options.timeline ? options.timeline.record(name, invoke) : invoke(); + }; + + return { + client, + call, + listToolNames: async () => (await client.listTools()).tools.map(t => t.name).sort(), + listResourceUris: async () => (await client.listResources()).resources.map(r => r.uri).sort(), + close: () => client.close() + }; +} + +export interface RawStdioSession { + child: ChildProcessWithoutNullStreams; + /** Everything the server wrote to stdout. */ + stdout(): string; + stderr(): string; + send(message: unknown): void; + /** Resolves with the exit code once the process ends. */ + exited: Promise; +} + +/** + * Spawns the server without the SDK client in the way, so a spec can inspect raw stdout. + * + * The SDK's transport consumes stdout to parse frames, which makes it impossible to assert that + * *nothing else* was written there. Under stdio that channel carries JSON-RPC, so a stray banner or + * console.log corrupts the connection to Studio Pro — an invariant worth testing directly. + */ +export function startRawStdioSession(projectDir: string): RawStdioSession { + assertBuilt(); + + const child = spawn(process.execPath, [SERVER_ENTRY, "stdio"], { + cwd: PACKAGE_ROOT, + env: { + PATH: process.env.PATH ?? "", + HOME: process.env.HOME ?? "", + MENDIX_PROJECT_DIR: projectDir, + MCP_LOG_LEVEL: "debug" // maximise the chance of catching a logger writing to the wrong stream + }, + stdio: ["pipe", "pipe", "pipe"] + }) as ChildProcessWithoutNullStreams; + + let out = ""; + let err = ""; + child.stdout.on("data", (chunk: Buffer) => (out += chunk.toString())); + child.stderr.on("data", (chunk: Buffer) => (err += chunk.toString())); + + const exited = new Promise(resolve => child.on("close", code => resolve(code))); + + return { + child, + stdout: () => out, + stderr: () => err, + send: (message: unknown) => child.stdin.write(`${JSON.stringify(message)}\n`), + exited + }; +} + +/** Polls until `predicate` holds or the budget runs out. Avoids arbitrary sleeps in specs. */ +export async function waitFor(predicate: () => boolean, budgetMs = 10_000, stepMs = 50): Promise { + const deadline = Date.now() + budgetMs; + while (Date.now() < deadline) { + if (predicate()) return true; + await new Promise(resolve => setTimeout(resolve, stepMs)); + } + return predicate(); +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/timeline.ts b/packages/pluggable-widgets-mcp/src/__e2e__/support/timeline.ts new file mode 100644 index 0000000000..3f51fef6e3 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/timeline.ts @@ -0,0 +1,129 @@ +/** + * Wall-clock instrumentation for the end-to-end suite. + * + * Every tool call the harness makes is timed, so specs get timings without bookkeeping. Runs append + * a row to docs/benchmarks/timings.jsonl tagged with the commit that produced it: the point is not + * the number on any single run but the trend, so a change that doubles scaffold time is visible. + * + * Only the phases the server actually owns are worth watching closely. `scaffold` and `build` are + * npm and the Mendix toolchain; they drift with the network and the machine. `setProperties`, + * `writeFiles` and `deploy` are this server's own work and run in single-digit milliseconds, so + * movement there means something real changed. + */ + +import { execFileSync } from "node:child_process"; +import { appendFileSync, mkdirSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { PACKAGE_ROOT } from "@/config"; + +export const TIMINGS_FILE = join(PACKAGE_ROOT, "docs", "benchmarks", "timings.jsonl"); + +export type RunMode = "warm" | "cold"; + +export interface TimingRow { + sha: string; + mode: RunMode; + ts: string; + phases: Record; + totalToWorkingWidget: number; +} + +/** Short commit SHA, or "unknown" outside a git checkout — never throw for a benchmark. */ +function currentSha(): string { + try { + return execFileSync("git", ["rev-parse", "--short", "HEAD"], { + cwd: PACKAGE_ROOT, + encoding: "utf-8" + }).trim(); + } catch { + return "unknown"; + } +} + +export class Timeline { + private readonly phases = new Map(); + + /** Times `fn`, filing the result under `phase`. Repeated phases accumulate. */ + async record(phase: string, fn: () => Promise): Promise { + const started = Date.now(); + try { + return await fn(); + } finally { + this.phases.set(phase, (this.phases.get(phase) ?? 0) + (Date.now() - started)); + } + } + + get(phase: string): number | undefined { + return this.phases.get(phase); + } + + get total(): number { + let sum = 0; + for (const ms of this.phases.values()) sum += ms; + return sum; + } + + toRow(mode: RunMode): TimingRow { + return { + sha: currentSha(), + mode, + ts: new Date().toISOString(), + phases: Object.fromEntries(this.phases), + totalToWorkingWidget: this.total + }; + } +} + +/** + * Appends a row. Benchmark bookkeeping must never fail a run that otherwise passed, so write errors + * are swallowed after a warning — a read-only checkout should still be able to run the suite. + */ +export function appendTimingRow(row: TimingRow): void { + try { + mkdirSync(dirname(TIMINGS_FILE), { recursive: true }); + appendFileSync(TIMINGS_FILE, `${JSON.stringify(row)}\n`, "utf-8"); + } catch (error) { + process.stderr.write(`[e2e] could not record timings: ${String(error)}\n`); + } +} + +/** The most recent recorded row for `mode`, excluding `exceptSha`. Used to report a delta. */ +export function previousRow(mode: RunMode, exceptSha: string): TimingRow | undefined { + let content: string; + try { + content = readFileSync(TIMINGS_FILE, "utf-8"); + } catch { + return undefined; + } + + const rows = content + .split("\n") + .filter(line => line.trim() !== "") + .flatMap(line => { + try { + return [JSON.parse(line) as TimingRow]; + } catch { + return []; // a truncated write should not blind the whole history + } + }) + .filter(r => r.mode === mode && r.sha !== exceptSha); + + return rows.at(-1); +} + +/** Human-readable summary printed at the end of a run. */ +export function formatSummary(row: TimingRow): string { + const previous = previousRow(row.mode, row.sha); + const seconds = (ms: number): string => `${(ms / 1000).toFixed(1)}s`; + + const lines = [`[e2e] ${row.mode} run — total ${seconds(row.totalToWorkingWidget)}`]; + for (const [phase, ms] of Object.entries(row.phases)) { + lines.push(` ${phase.padEnd(18)} ${seconds(ms)}`); + } + if (previous) { + const delta = row.totalToWorkingWidget - previous.totalToWorkingWidget; + const sign = delta >= 0 ? "+" : "-"; + lines.push(` vs ${previous.sha}: ${sign}${seconds(Math.abs(delta))}`); + } + return lines.join("\n"); +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/warm-cache.ts b/packages/pluggable-widgets-mcp/src/__e2e__/support/warm-cache.ts new file mode 100644 index 0000000000..9ed194f75c --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/warm-cache.ts @@ -0,0 +1,134 @@ +/** + * Scaffold once, reuse many times. + * + * A real `create-widget` costs 20-40 seconds, almost all of it `npm install`. Paying that per spec + * would make the suite something people skip. Instead one real scaffold is cached and each spec gets + * a fresh copy of it. + * + * The copy is real in every way that matters: the Mendix build toolchain runs against it for real + * and produces a real .mpk. Only the install is shared. `node_modules` is symlinked rather than + * copied — duplicating tens of thousands of files would cost more than the install it saves. + * + * The cache key includes the generator version, so bumping @mendix/generator-widget invalidates it + * automatically and the next run re-scaffolds against the new template. + */ + +import { createRequire } from "node:module"; +import { + cpSync, + existsSync, + mkdirSync, + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + symlinkSync, + writeFileSync +} from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { PACKAGE_ROOT } from "@/config"; +import { startStdioServer } from "./harness"; +import type { Timeline } from "./timeline"; + +export const CACHE_ROOT = join(PACKAGE_ROOT, ".e2e-cache"); + +/** Fixed across the suite so goldens and typings paths stay stable. */ +export const WARM_WIDGET_NAME = "ProbeBadge"; +const WARM_WIDGET_FOLDER = "probeBadge"; + +function generatorVersion(): string { + try { + const require = createRequire(join(PACKAGE_ROOT, "package.json")); + const pkgPath = require.resolve("@mendix/generator-widget/package.json"); + return (JSON.parse(readFileSync(pkgPath, "utf-8")) as { version: string }).version; + } catch { + return "unknown"; + } +} + +function cacheKey(): string { + return `${generatorVersion()}-typescript-empty`; +} + +/** A minimal directory the server will accept as a Mendix project. */ +export function makeProjectDir(): string { + const dir = mkdtempSync(join(tmpdir(), "mcp-e2e-project-")); + mkdirSync(join(dir, "widgets"), { recursive: true }); + mkdirSync(join(dir, "widget-sources"), { recursive: true }); + writeFileSync(join(dir, "E2eApp.mpr"), ""); + return dir; +} + +/** + * Ensures the cached scaffold exists, creating it with a real `create-widget` call if not. + * + * Population goes through the tool rather than calling the generator directly, so even the cache + * warm-up exercises the real path. Returns the cached widget directory. + */ +export async function ensureWarmCache(timeline?: Timeline): Promise { + // The cache directory doubles as a Mendix project, so the widget is scaffolded straight into its + // final home. Scaffolding elsewhere and copying looked simpler but is not: npm writes absolute + // symlinks into node_modules/.bin, so a copied cache points its tsc and pluggable-widgets-tools + // at a temp directory that no longer exists, and fails in a way that reads like a broken build. + const projectRoot = join(CACHE_ROOT, cacheKey()); + const cachedWidget = join(projectRoot, "widget-sources", WARM_WIDGET_FOLDER); + + if (existsSync(join(cachedWidget, "package.json")) && existsSync(join(cachedWidget, "node_modules"))) { + return cachedWidget; + } + + // A half-populated cache is worse than none: a failed install leaves a widget that cannot build, + // and every later spec blames the change under test. + rmSync(projectRoot, { recursive: true, force: true }); + mkdirSync(join(projectRoot, "widgets"), { recursive: true }); + mkdirSync(join(projectRoot, "widget-sources"), { recursive: true }); + writeFileSync(join(projectRoot, "E2eApp.mpr"), ""); + + const server = await startStdioServer({ projectDir: projectRoot, timeline }); + try { + const result = await server.call("create-widget", { + name: WARM_WIDGET_NAME, + description: "Fixture widget for the pluggable-widgets-mcp end-to-end suite", + organization: "mendix", + programmingLanguage: "typescript", + template: "empty", + unitTests: false, + e2eTests: false + }); + + if (result.isError) { + rmSync(projectRoot, { recursive: true, force: true }); + throw new Error(`could not populate the e2e warm cache:\n${result.text}`); + } + } finally { + await server.close(); + } + + return cachedWidget; +} + +/** The TypeScript compiler inside the cached install, addressed directly. */ +export function typescriptCompiler(widgetPath: string): string { + return join(widgetPath, "node_modules", "typescript", "bin", "tsc"); +} + +/** + * Copies the cached scaffold into `projectDir/widget-sources/` and returns the widget path. + * + * Everything except node_modules is copied, so a spec can freely rewrite sources; node_modules is + * symlinked, so the build resolves pluggable-widgets-tools without a multi-second copy. + */ +export function materializeWarmWidget(cacheDir: string, projectDir: string): string { + const target = join(projectDir, "widget-sources", WARM_WIDGET_FOLDER); + rmSync(target, { recursive: true, force: true }); + mkdirSync(target, { recursive: true }); + + for (const entry of readdirSync(cacheDir)) { + if (entry === "node_modules" || entry === "dist") continue; + cpSync(join(cacheDir, entry), join(target, entry), { recursive: true }); + } + + symlinkSync(join(cacheDir, "node_modules"), join(target, "node_modules"), "dir"); + return target; +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/support/zip.ts b/packages/pluggable-widgets-mcp/src/__e2e__/support/zip.ts new file mode 100644 index 0000000000..d1791c6923 --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/support/zip.ts @@ -0,0 +1,48 @@ +/** + * Reads the file names out of a zip archive by walking its central directory. + * + * An .mpk is a zip. Checking that one exists proves nothing — the question is whether Studio Pro can + * load it, which comes down to whether the expected entries are inside at the expected paths. + * + * Implemented here rather than shelling out to `unzip`, which is absent on Windows, and rather than + * adding a dependency for thirty lines of header parsing. Only the names are needed; nothing is + * decompressed. + */ + +const END_OF_CENTRAL_DIRECTORY = 0x06054b50; +const CENTRAL_FILE_HEADER = 0x02014b50; + +/** Locates the end-of-central-directory record, which sits at the tail, after any comment. */ +function findEndOfCentralDirectory(buffer: Buffer): number { + // The comment is at most 0xffff bytes, and the record itself is 22. + const earliest = Math.max(0, buffer.length - 0xffff - 22); + for (let i = buffer.length - 22; i >= earliest; i--) { + if (buffer.readUInt32LE(i) === END_OF_CENTRAL_DIRECTORY) return i; + } + return -1; +} + +export function readZipEntryNames(buffer: Buffer): string[] { + const eocd = findEndOfCentralDirectory(buffer); + if (eocd < 0) { + throw new Error("not a zip archive: no end-of-central-directory record found"); + } + + const entryCount = buffer.readUInt16LE(eocd + 10); + let offset = buffer.readUInt32LE(eocd + 16); + + const names: string[] = []; + for (let i = 0; i < entryCount; i++) { + if (buffer.readUInt32LE(offset) !== CENTRAL_FILE_HEADER) { + throw new Error(`corrupt zip: expected a central directory header at byte ${offset}`); + } + const nameLength = buffer.readUInt16LE(offset + 28); + const extraLength = buffer.readUInt16LE(offset + 30); + const commentLength = buffer.readUInt16LE(offset + 32); + + names.push(buffer.toString("utf-8", offset + 46, offset + 46 + nameLength)); + offset += 46 + nameLength + extraLength + commentLength; + } + + return names.sort(); +} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/transport.e2e.test.ts b/packages/pluggable-widgets-mcp/src/__e2e__/transport.e2e.test.ts new file mode 100644 index 0000000000..a60e30313e --- /dev/null +++ b/packages/pluggable-widgets-mcp/src/__e2e__/transport.e2e.test.ts @@ -0,0 +1,134 @@ +/** + * Transport-level invariants that only a real process can demonstrate. + * + * Under stdio, stdout carries JSON-RPC. A single stray byte on that stream — a generator banner, a + * forgotten console.log — corrupts the connection to Studio Pro. The unit suite cannot see this at + * all: it runs client and server in one process over InMemoryTransport, where stdout is irrelevant. + */ + +import { afterEach, describe, expect, it } from "vitest"; +import { spawn } from "node:child_process"; +import { rmSync } from "node:fs"; +import { PACKAGE_ROOT } from "@/config"; +import { SERVER_ENTRY, startRawStdioSession, startStdioServer, waitFor } from "./support/harness"; +import { makeProjectDir } from "./support/warm-cache"; + +const EXPECTED_TOOLS = [ + "build-widget", + "create-widget", + "deploy-widget", + "get-project-info", + "list-widget-files", + "read-widget-file", + "set-project-directory", + "set-widget-properties", + "write-widget-file" +]; + +describe("stdio transport", () => { + const cleanups: string[] = []; + + afterEach(() => { + for (const dir of cleanups) rmSync(dir, { recursive: true, force: true }); + cleanups.length = 0; + }); + + it("advertises exactly the nine pipeline tools and both guideline resources", async () => { + const projectDir = makeProjectDir(); + cleanups.push(projectDir); + + const server = await startStdioServer({ projectDir }); + try { + expect(await server.listToolNames()).toEqual(EXPECTED_TOOLS); + expect(await server.listResourceUris()).toEqual([ + "mendix://guidelines/property-types", + "mendix://guidelines/widget-patterns" + ]); + } finally { + await server.close(); + } + }); + + it("writes nothing but JSON-RPC to stdout", async () => { + const projectDir = makeProjectDir(); + cleanups.push(projectDir); + + const session = startRawStdioSession(projectDir); + session.send({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "purity-probe", version: "1.0.0" } + } + }); + + await waitFor(() => session.stdout().includes("\n")); + session.child.kill(); + await session.exited; + + const lines = session.stdout().split("\n").filter(Boolean); + expect(lines.length, "server produced no stdout at all").toBeGreaterThan(0); + + for (const line of lines) { + // Anything that is not a JSON-RPC frame would desynchronise the client's parser. + const parsed = JSON.parse(line) as { jsonrpc?: string }; + expect(parsed.jsonrpc, `non-JSON-RPC line on stdout: ${line}`).toBe("2.0"); + } + + // Logging still has to go somewhere, and stderr is where Studio Pro collects it. + expect(session.stderr().length).toBeGreaterThan(0); + }); + + it("exits when its parent closes stdin, instead of orphaning", async () => { + const projectDir = makeProjectDir(); + cleanups.push(projectDir); + + const session = startRawStdioSession(projectDir); + await waitFor(() => session.stderr().includes("Connected") || session.stderr().length > 0); + + session.child.stdin.end(); + + const code = await Promise.race([ + session.exited, + new Promise<"timeout">(resolve => setTimeout(() => resolve("timeout"), 15_000)) + ]); + + expect(code, "server did not exit after stdin closed — it would orphan when Studio Pro dies").not.toBe( + "timeout" + ); + }); +}); + +describe("command line", () => { + /** Runs the entry point and resolves with its exit code and combined output. */ + function run(args: string[]): Promise<{ code: number | null; output: string }> { + return new Promise(resolve => { + const child = spawn(process.execPath, [SERVER_ENTRY, ...args], { + cwd: PACKAGE_ROOT, + env: { PATH: process.env.PATH ?? "", HOME: process.env.HOME ?? "" } + }); + let output = ""; + child.stdout.on("data", (c: Buffer) => (output += c.toString())); + child.stderr.on("data", (c: Buffer) => (output += c.toString())); + child.on("close", code => resolve({ code, output })); + }); + } + + it("rejects an unknown transport instead of silently defaulting to stdio", async () => { + const { code, output } = await run(["htpp"]); + + expect(code).not.toBe(0); + expect(output).toContain("htpp"); + }); + + it("prints usage for --help", async () => { + const { code, output } = await run(["--help"]); + + expect(code).toBe(0); + expect(output).toContain("stdio"); + expect(output).toContain("http"); + }); +}); diff --git a/packages/pluggable-widgets-mcp/vitest.e2e.config.ts b/packages/pluggable-widgets-mcp/vitest.e2e.config.ts new file mode 100644 index 0000000000..078b7fc50c --- /dev/null +++ b/packages/pluggable-widgets-mcp/vitest.e2e.config.ts @@ -0,0 +1,25 @@ +import tsconfigPaths from "vite-tsconfig-paths"; +import { defineConfig } from "vitest/config"; + +/** + * End-to-end suite. Separate from vitest.config.ts on purpose. + * + * These specs spawn the built server as a real child process, run a real Yeoman scaffold, and shell + * out to the Mendix build toolchain. They are minutes where the unit suite is seconds, so they must + * never be swept into `npm run test` — a slow gate is a gate people stop running. + * + * Files run one at a time: they share the warm-cache directory and each spawns processes, so + * parallel execution would have them fighting over the same fixture. + */ +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + globals: false, + include: ["src/__e2e__/**/*.e2e.test.ts"], + testTimeout: 300_000, + hookTimeout: 300_000, + // vitest 0.34: `threads: false` is how you get one file at a time. + threads: false, + restoreMocks: true + } +}); From 5f4d8d9bdf57f4449147fdc7e74273e4a9c3e839 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 31 Jul 2026 16:18:22 +0200 Subject: [PATCH 34/36] fix(mcp): keep e2e golden files out of prettier The pre-commit formatter rewrote the golden XML on the way in, so the committed golden no longer matched what the generator emits and the pipeline spec failed on the very next run. A golden has to be byte-identical to real output or the comparison asserts prettier's opinion about XML rather than the server's behaviour. Caught by re-running the suite after committing, which is the only reason it did not land as a broken test. --- .prettierignore | 3 +++ packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl | 3 +++ .../pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 269a46be8d..3054be55cf 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ +# Golden files must stay byte-identical to what the generator emits. Formatting them makes the +# comparison assert prettier's opinion instead of the server's output. +packages/pluggable-widgets-mcp/src/__e2e__/goldens packages/tools/generator-widget/generators/app/templates packages/tools/pluggable-widgets-tools/tests/projects packages/pluggableWidgets/*/typings diff --git a/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl index b8f7a0d5a7..552ef39a0c 100644 --- a/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl +++ b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl @@ -3,3 +3,6 @@ {"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:06:38.565Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":4,"read-widget-file":3,"build-widget":3633,"deploy-widget":2},"totalToWorkingWidget":3645} {"sha":"4844bb59b","mode":"cold","ts":"2026-07-31T14:14:10.593Z","phases":{"create-widget":11584,"set-widget-properties":2,"write-widget-file":2,"build-widget":13419,"deploy-widget":2},"totalToWorkingWidget":25009} {"sha":"4844bb59b","mode":"warm","ts":"2026-07-31T14:15:46.773Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":7,"build-widget":6520,"deploy-widget":3},"totalToWorkingWidget":6536} +{"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:17:37.660Z","phases":{"get-project-info":1,"set-widget-properties":1,"write-widget-file":4,"read-widget-file":2,"build-widget":3525,"deploy-widget":6},"totalToWorkingWidget":3539} +{"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:18:08.017Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":5,"read-widget-file":4,"build-widget":3828,"deploy-widget":3},"totalToWorkingWidget":3843} +{"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:18:13.453Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":3,"build-widget":3739,"deploy-widget":10},"totalToWorkingWidget":3758} diff --git a/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml b/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml index 1b4d51a019..e78f93ab43 100644 --- a/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml +++ b/packages/pluggable-widgets-mcp/src/__e2e__/goldens/ProbeBadge.xml @@ -46,4 +46,4 @@ -
+
\ No newline at end of file From 8537876e51df52d75bd31d96a8260551c63ed0a3 Mon Sep 17 00:00:00 2001 From: Rahman Date: Fri, 31 Jul 2026 16:20:13 +0200 Subject: [PATCH 35/36] docs(mcp): report the measured spread rather than the best run A second cold run came in at 35.1s against the first at 25.0s. The whole difference is npm install (22.4s vs 11.6s); the build moved by less than a second and the server's own steps were six milliseconds both times. Quoting only the faster run would have been a number nobody else could reproduce. Showing both makes the actual claim clearer: the wall-clock belongs to npm and the Mendix toolchain, and this server adds nothing measurable to it. --- .../docs/benchmarks/timings.jsonl | 2 ++ .../pluggable-widgets-mcp/docs/evaluation.md | 33 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl index 552ef39a0c..b98bd0d6ae 100644 --- a/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl +++ b/packages/pluggable-widgets-mcp/docs/benchmarks/timings.jsonl @@ -6,3 +6,5 @@ {"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:17:37.660Z","phases":{"get-project-info":1,"set-widget-properties":1,"write-widget-file":4,"read-widget-file":2,"build-widget":3525,"deploy-widget":6},"totalToWorkingWidget":3539} {"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:18:08.017Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":5,"read-widget-file":4,"build-widget":3828,"deploy-widget":3},"totalToWorkingWidget":3843} {"sha":"dc05a1d44","mode":"warm","ts":"2026-07-31T14:18:13.453Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":3,"build-widget":3739,"deploy-widget":10},"totalToWorkingWidget":3758} +{"sha":"58c40b5f0","mode":"warm","ts":"2026-07-31T14:18:52.601Z","phases":{"get-project-info":1,"set-widget-properties":2,"write-widget-file":3,"read-widget-file":2,"build-widget":3663,"deploy-widget":3},"totalToWorkingWidget":3674} +{"sha":"58c40b5f0","mode":"cold","ts":"2026-07-31T14:19:38.399Z","phases":{"create-widget":22420,"set-widget-properties":3,"write-widget-file":1,"build-widget":12715,"deploy-widget":2},"totalToWorkingWidget":35141} diff --git a/packages/pluggable-widgets-mcp/docs/evaluation.md b/packages/pluggable-widgets-mcp/docs/evaluation.md index a9a2879f1b..e144796243 100644 --- a/packages/pluggable-widgets-mcp/docs/evaluation.md +++ b/packages/pluggable-widgets-mcp/docs/evaluation.md @@ -84,31 +84,32 @@ Every run records how long each step took. Results are appended to makes things slower shows up immediately instead of being noticed months later. Recorded by `cold.e2e.test.ts` on 2026-07-31, macOS — nothing on disk to a widget deployed into a -Mendix project: +Mendix project. Two runs, to show what is stable and what is not: -| Step | Time | -| ------------------------------- | ---------------- | -| Scaffold + install dependencies | 11.6 s | -| Write the XML definition | 0.002 s | -| Write the component files | 0.002 s | -| Build the `.mpk` | 13.4 s | -| Deploy into the project | 0.002 s | -| **Total** | **25.0 seconds** | +| Step | Run 1 | Run 2 | +| ------------------------------- | ---------- | ---------- | +| Scaffold + install dependencies | 11.6 s | 22.4 s | +| Write the XML definition | 0.002 s | 0.002 s | +| Write the component files | 0.002 s | 0.002 s | +| Build the `.mpk` | 13.4 s | 12.7 s | +| Deploy into the project | 0.002 s | 0.002 s | +| **Total** | **25.0 s** | **35.1 s** | Rebuilding after an edit: **3.6 seconds.** -The shape of that table is the point. Two steps take all the time, and the server owns neither of -them: `npm install` and the Mendix build toolchain. Everything the server itself does — deriving the -XML, writing the files, deploying the artifact — adds up to **six milliseconds**. +The shape of that table is the point, and the variance makes it clearer rather than muddier. The two +runs differ by ten seconds, all of it in `npm install`. The build barely moved. The server's own +steps — deriving the XML, writing the files, deploying the artifact — came to **six milliseconds** +both times. So the honest claim is not that this server is fast. It is that **the pipeline costs nothing**. A -developer doing this by hand pays the same 25 seconds of npm and compilation, plus the scaffolding +developer doing this by hand pays the same npm install and the same compilation, plus the scaffolding decisions, the XML by hand, and the copy into `widgets/`. The server removes those and adds approximately zero. -Two caveats worth stating before quoting the number. It assumes npm's package cache is warm; on a -machine downloading everything for the first time, expect closer to 40 seconds. And it is one -machine — the value of `timings.jsonl` is the trend across commits, not any single row. +If you need one number, say **under a minute from nothing to a widget in the project**, and expect +the spread to come from npm rather than from anything here. The value of `timings.jsonl` is the trend +across commits, not any single row. --- From 5889dc903bb8a7e17241ea66c45a2f2874365cd4 Mon Sep 17 00:00:00 2001 From: Rahman Date: Mon, 10 Aug 2026 10:16:44 +0200 Subject: [PATCH 36/36] fix(mcp): restore workspace glob for pluggable-widgets-mcp package --- pnpm-lock.yaml | 3465 ++++++++++++++++++++++++++++++++++++++++++- pnpm-workspace.yaml | 1 + 2 files changed, 3391 insertions(+), 75 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c55e1477f..9a1acf8d6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -363,6 +363,49 @@ importers: specifier: ^17.3.0 version: 17.9.0 + packages/pluggable-widgets-mcp: + dependencies: + '@mendix/generator-widget': + specifier: ^11.11.0 + version: 11.13.0(@types/node@24.12.4)(@types/vinyl@2.0.12)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12))) + '@modelcontextprotocol/sdk': + specifier: ^1.24.2 + version: 1.30.0(zod@4.4.3) + express: + specifier: ^5.1.0 + version: 5.2.1 + yeoman-environment: + specifier: ^6.1.0 + version: 6.1.0(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + zod: + specifier: ^4.1.13 + version: 4.4.3 + devDependencies: + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/node': + specifier: ~24.12.0 + version: 24.12.4 + tsc-alias: + specifier: ^1.8.16 + version: 1.9.1 + tsx: + specifier: ^4.21.0 + version: 4.23.12 + typescript: + specifier: '>5.8.0 <6.0.0' + version: 5.9.3 + vite: + specifier: ^4.5.14 + version: 4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0) + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.9.3)(vite@4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0)) + vitest: + specifier: ^0.34.6 + version: 0.34.6(happy-dom@19.0.2)(jsdom@26.1.0(canvas@3.2.0))(playwright@1.62.1)(sass@1.102.0)(terser@5.44.0) + packages/pluggableWidgets/accessibility-helper-web: dependencies: '@mendix/widget-plugin-component-kit': @@ -4356,6 +4399,294 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4454,6 +4785,10 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@gar/promise-retry@1.0.3': + resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} + engines: {node: ^20.17.0 || >=22.9.0} + '@googlemaps/jest-mocks@2.22.6': resolution: {integrity: sha512-t3n0l03OdGPEUCfWVC1a4xGgcE21+58tGdNsIjGWsTbsaMZBOfCxwTHvzmAx/H0dyPeKZ4uWmtahsyUQIcGInA==} @@ -4494,66 +4829,207 @@ packages: peerDependencies: react: '>=18.0.0 <19.0.0' - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@30.3.0': - resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@jest/core@30.3.0': - resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + '@types/node': ~24.12.0 peerDependenciesMeta: - node-notifier: + '@types/node': optional: true - '@jest/create-cache-key-function@30.2.0': - resolution: {integrity: sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/diff-sequences@30.3.0': - resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/diff-sequences@30.4.0': - resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true - '@jest/environment-jsdom-abstract@30.3.0': - resolution: {integrity: sha512-0hNFs5N6We3DMCwobzI0ydhkY10sT1tZSC0AAiy+0g2Dt/qEWgrcV5BrMxPczhe41cxW4qm6X+jqZaUdpZIajA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: - canvas: ^3.0.0 - jsdom: '*' + '@types/node': ~24.12.0 peerDependenciesMeta: - canvas: + '@types/node': optional: true - '@jest/environment-jsdom-abstract@30.4.1': - resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: - canvas: ^3.0.0 - jsdom: '*' + '@types/node': ~24.12.0 peerDependenciesMeta: - canvas: + '@types/node': optional: true - '@jest/environment@30.3.0': - resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@isaacs/string-locale-compare@1.1.0': + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@30.3.0': + resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@30.3.0': + resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/create-cache-key-function@30.2.0': + resolution: {integrity: sha512-44F4l4Enf+MirJN8X/NhdGkl71k5rBYiwdVlo4HxOwbu0sHV8QKrGEedb1VUU4K3W7fBKE0HGfbn7eZm0Ti3zg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/diff-sequences@30.3.0': + resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/diff-sequences@30.4.0': + resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment-jsdom-abstract@30.3.0': + resolution: {integrity: sha512-0hNFs5N6We3DMCwobzI0ydhkY10sT1tZSC0AAiy+0g2Dt/qEWgrcV5BrMxPczhe41cxW4qm6X+jqZaUdpZIajA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + jsdom: '*' + peerDependenciesMeta: + canvas: + optional: true + + '@jest/environment-jsdom-abstract@30.4.1': + resolution: {integrity: sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + jsdom: '*' + peerDependenciesMeta: + canvas: + optional: true + + '@jest/environment@30.3.0': + resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment@30.4.1': resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} @@ -4604,6 +5080,10 @@ packages: node-notifier: optional: true + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@30.0.5': resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4670,6 +5150,12 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + + '@kwsites/promise-deferred@1.1.1': + resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} + '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -4736,6 +5222,11 @@ packages: '@melloware/coloris@0.25.0': resolution: {integrity: sha512-RBWVFLjWbup7GRkOXb9g3+ZtR9AevFtJinrRz2cYPLjZ3TCkNRGMWuNbmQWbZ5cF3VU7aQDZwUsYgIY/bGrh2g==} + '@mendix/generator-widget@11.13.0': + resolution: {integrity: sha512-MR+O/NV5JuIjHUNNo7i3nLDsYuTJ+6WKsHEwq7d2mKiIXs408nGy8B+jq0fAhOsFUg59lfMj+ZNeF6GePb5aLA==} + engines: {node: ^22.18.0} + hasBin: true + '@mendix/pluggable-widgets-tools@11.11.0': resolution: {integrity: sha512-HegJy+xzMEmrdV/PZ+n1TnmsTUu2497BFa60ZSPDmBDdsxAIebVv5NHEmlbfTObugOb+GtF64RM1SxPUZguKHg==} engines: {node: '>=20'} @@ -4780,6 +5271,64 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@npmcli/agent@4.0.2': + resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/arborist@9.9.1': + resolution: {integrity: sha512-K0mr16xJ/yiTApeGIFbpgZSvJFOvxO2VJnCBhP543t9NTlHzgL+ewpG0kaWv9xkjeESAlRWHG9Q4lbT/LqHbWw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/git@7.0.2': + resolution: {integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/installed-package-contents@4.0.0': + resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + '@npmcli/map-workspaces@5.0.3': + resolution: {integrity: sha512-o2grssXo1e774E5OtEwwrgoszYRh0lqkJH+Pb9r78UcqdGJRDRfhpM8DvZPjzNLLNYeD/rNbjOKM3Ss5UABROw==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/metavuln-calculator@9.0.3': + resolution: {integrity: sha512-94GLSYhLXF2t2LAC7pDwLaM4uCARzxShyAQKsirmlNcpidH89VA4/+K1LbJmRMgz5gy65E/QBBWQdUvGLe2Frg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/name-from-folder@4.0.0': + resolution: {integrity: sha512-qfrhVlOSqmKM8i6rkNdZzABj8MKEITGFAY+4teqBziksCQAOLutiAxM1wY2BKEd8KjUSpWmWCYxvXr0y4VTlPg==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/node-gyp@5.0.0': + resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/package-json@7.0.5': + resolution: {integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/promise-spawn@9.0.1': + resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/query@5.0.0': + resolution: {integrity: sha512-8TZWfTQOsODpLqo9SVhVjHovmKXNpevHU0gO9e+y4V4fRIOneiXy0u0sMP9LmS71XivrEWfZWg50ReH4WRT4aQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/redact@4.0.0': + resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/run-script@10.0.4': + resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} + engines: {node: ^20.17.0 || >=22.9.0} + '@one-ini/wasm@0.2.1': resolution: {integrity: sha512-TUqERXGNTifZ9y2g3wPxQrw3HpHv/02DsW3D90T9x0hhonrL1ZqpSmNrU2XkoIq0fP1N6gZfVQzy2Fw1ZvGBNg==} @@ -4907,6 +5456,18 @@ packages: '@plotly/regl@2.1.2': resolution: {integrity: sha512-Mdk+vUACbQvjd0m/1JJjOOafmkp/EpmHjISsopEz5Av44CBq7rPC05HHNbYGKVyNUF2zmEoBS/TT0pd0SPFFyw==} + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@3.0.3': + resolution: {integrity: sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA==} + engines: {node: '>=12'} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -5429,6 +5990,39 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@sigstore/bundle@4.0.0': + resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/core@3.2.1': + resolution: {integrity: sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/protobuf-specs@0.5.1': + resolution: {integrity: sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/sign@4.1.1': + resolution: {integrity: sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/tuf@4.0.2': + resolution: {integrity: sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@sigstore/verify@3.1.1': + resolution: {integrity: sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@simple-git/args-pathspec@1.0.3': + resolution: {integrity: sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==} + + '@simple-git/argv-parser@1.1.1': + resolution: {integrity: sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==} + '@simple-libs/child-process-utils@2.0.0': resolution: {integrity: sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==} engines: {node: '>=22'} @@ -5437,9 +6031,16 @@ packages: resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==} engines: {node: '>=22'} + '@sinclair/typebox@0.27.12': + resolution: {integrity: sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==} + '@sinclair/typebox@0.34.41': resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -5576,6 +6177,14 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@tufjs/canonical-json@2.0.0': + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} + + '@tufjs/models@4.1.0': + resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} + engines: {node: ^20.17.0 || >=22.9.0} + '@turf/area@7.2.0': resolution: {integrity: sha512-zuTTdQ4eoTI9nSSjerIy4QwgvxqwJVciQJ8tOPuMHbXJ9N/dNjI7bU8tasjhxas/Cx3NE9NxVHtNpYHL0FSzoA==} @@ -5612,15 +6221,35 @@ packages: '@types/big.js@6.2.2': resolution: {integrity: sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/chai-subset@1.3.6': + resolution: {integrity: sha512-m8lERkkQj+uek18hXOZuec3W/fCRTrU4hrnXjH3qhHy96ytuPaPiWGgu7sJb7tZxZonO75vYAjCvpe/e4VUwRw==} + peerDependencies: + '@types/chai': <5.2.0 + + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cross-zip@4.0.2': resolution: {integrity: sha512-yvTQ6/tWlGdykh6qkVigwmq42gi51qHPdi7e60KRmPCxeYj5QcX8RX0T6jCDIWcHNWLMVw1IuoMehGcwDuzrYw==} '@types/date-arithmetic@4.1.4': resolution: {integrity: sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + '@types/deep-equal@1.0.4': resolution: {integrity: sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==} + '@types/ejs@3.1.5': + resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} + '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} @@ -5630,6 +6259,15 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/expect@1.20.4': + resolution: {integrity: sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==} + + '@types/express-serve-static-core@5.1.3': + resolution: {integrity: sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==} + + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + '@types/fs-extra@8.1.5': resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} @@ -5645,6 +6283,9 @@ packages: '@types/google.maps@3.58.1': resolution: {integrity: sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==} + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -5678,6 +6319,12 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.25': + resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==} + '@types/mapbox__point-geometry@0.1.4': resolution: {integrity: sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==} @@ -5696,15 +6343,24 @@ packages: '@types/minimatch@3.0.5': resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} '@types/node@24.12.4': resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==} + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/pbf@3.0.5': resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} + '@types/picomatch@4.0.3': + resolution: {integrity: sha512-iG0T6+nYJ9FAPmx9SsUlnwcq1ZVRuCXcVEvWnntoPlrOpwtSTKNDC9uVAxTsC3PUvJ+99n4RpAcNgBbHX3JSnQ==} + '@types/plotly.js-dist-min@2.3.4': resolution: {integrity: sha512-ISwLFV6Zs/v3DkaRFLyk2rvYAfVdnYP2VVVy7h+fBDWw52sn7sMUzytkWiN4M75uxr1uz1uiBioePTDpAfoFIg==} @@ -5714,6 +6370,12 @@ packages: '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/rc-slider@8.6.6': resolution: {integrity: sha512-2Q3vwKrSm3PbgiMNwzxMkOaMtcAGi0xQ8WPeVKoabk1vNYHiVR44DMC3mr9jC2lhbxCBgGBJWF9sBhmnSDQ8Bg==} @@ -5756,6 +6418,12 @@ packages: '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -5771,6 +6439,9 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/vinyl@2.0.12': + resolution: {integrity: sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==} + '@types/warning@3.0.3': resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} @@ -6040,9 +6711,59 @@ packages: react: '>=18.0.0 <19.0.0' react-dom: '>=18.0.0 <19.0.0' + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@xml-tools/parser@1.0.11': resolution: {integrity: sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==} + '@yeoman/adapter@4.0.2': + resolution: {integrity: sha512-4uttbNuZ/guMBRhf7R6TCfnLT6XY1HGpxsq6vpHRM3AVr5G6Qz7xXqxG2kdMzBGtIuH5CkXz0k/Byl27GsvIkg==} + engines: {node: 20 || >=22} + + '@yeoman/conflicter@4.1.0': + resolution: {integrity: sha512-Py62rJdWHf1zMpf801Ql7kg+sbUeVmvfmZf++mgA1SOk/uByLIfcXUMDzQPSHkW4Y7I2yVWLKZATRGjoczXVFQ==} + engines: {node: 20 || >=22} + peerDependencies: + '@types/node': ~24.12.0 + '@yeoman/types': ^1.0.0 + mem-fs: ^4.0.0 + + '@yeoman/namespace@2.1.0': + resolution: {integrity: sha512-/BxsZlALPRp34juAzzh9QUr2hR9w9o+dBSw8sF/iv7NfUU/A/QI0xOyVtQJz2uCPuvtY6nkGDxYxZoKI/lnFQg==} + engines: {node: ^16.13.0 || >=18.12.0} + + '@yeoman/transform@2.1.2': + resolution: {integrity: sha512-LdPNm5Moc+Z8l2YeQ0IzsybXoV531o7RmGJB8lpFr8SjtqqGkT2bZYlS1LftPpCcPJ//yE0MbI/fPGh1WWlabg==} + engines: {node: '>=18.19.0'} + peerDependencies: + '@types/node': ~24.12.0 + + '@yeoman/types@1.11.1': + resolution: {integrity: sha512-27CI5hHQAHfq8ohYILmLNzClbdzBJzu+ny9AzUVV6naJO0l4/+t+67QDKlwQvt+TW3oE5j74I/Mh4Kn14rsVXA==} + engines: {node: ^16.13.0 || >=18.12.0} + peerDependencies: + '@types/node': ~24.12.0 + '@yeoman/adapter': ^1.6.0 || ^2.0.0-beta.0 || ^3.0.0 || ^4.0.0 + mem-fs: ^3.0.0 || ^4.0.0-beta.1 + peerDependenciesMeta: + '@yeoman/adapter': + optional: true + mem-fs: + optional: true + '@zxing/library@0.21.3': resolution: {integrity: sha512-hZHqFe2JyH/ZxviJZosZjV+2s6EDSY0O24R+FQmlWZBZXP9IqMo7S3nb3+2LBWxodJQkSurdQGnqE7KXqrYgow==} engines: {node: '>= 10.4.0'} @@ -6050,6 +6771,10 @@ packages: '@zxing/text-encoding@0.9.0': resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + abbrev@4.0.0: + resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} + engines: {node: ^20.17.0 || >=22.9.0} + abbrev@5.0.0: resolution: {integrity: sha512-/XrFJgzQQQHpti1raDJC6m4ws6aNktmjBlhk8Fdlk7LwCEuDoieEJJY9OFHjfiFJFFRM2tK+Ky/IsfbbmlMu1w==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -6080,6 +6805,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -6161,6 +6891,10 @@ packages: resolution: {integrity: sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==} engines: {node: '>=0.10.0'} + array-differ@4.0.0: + resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + array-find-index@1.0.2: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} @@ -6186,6 +6920,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-union@3.0.1: + resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==} + engines: {node: '>=12'} + array-uniq@1.0.3: resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} engines: {node: '>=0.10.0'} @@ -6218,9 +6956,16 @@ packages: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} + arrify@3.0.0: + resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} + engines: {node: '>=12'} + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + ast-types@0.16.1: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} @@ -6244,6 +6989,14 @@ packages: resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} engines: {node: '>=4'} + b4a@1.8.1: + resolution: {integrity: sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + babel-jest@30.4.1: resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6312,6 +7065,14 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + bare-events@2.9.1: + resolution: {integrity: sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + base64-arraybuffer@1.0.2: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} @@ -6331,13 +7092,25 @@ packages: big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} + bin-links@6.0.2: + resolution: {integrity: sha512-frE1t78WOwJ45PKV2cF2tNPjTcs9L1J9s6VkrV59wanRP4GlaomuxYPVma7BwthMg8WnfSory4w5PTE6FZZ81w==} + engines: {node: ^20.17.0 || >=22.9.0} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + binary-extensions@3.1.0: + resolution: {integrity: sha512-Jvvd9hy1w+xUad8+ckQsWA/V1AoyubOvqn0aygjMOVM4BfIaRav1NFS3LsTSDaV4n4FtcCtQXvzep1E6MboqwQ==} + engines: {node: '>=18.20'} + binary-search-bounds@2.0.5: resolution: {integrity: sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==} + binaryextensions@6.11.0: + resolution: {integrity: sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==} + engines: {node: '>=4'} + bit-twiddle@1.0.2: resolution: {integrity: sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA==} @@ -6407,6 +7180,14 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@20.0.4: + resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} + engines: {node: ^20.17.0 || >=22.9.0} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -6447,6 +7228,10 @@ packages: resolution: {integrity: sha512-jk0GxrLtUEmW/TmFsk2WghvgHe8B0pxGilqCL21y8lHkPUGa6FTsnCNtHPOzT8O3y+N+m3espawV80bbBlgfTA==} engines: {node: ^18.12.0 || >= 20.9.0} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -6465,6 +7250,12 @@ packages: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + chevrotain@7.1.1: resolution: {integrity: sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==} @@ -6479,6 +7270,10 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@4.3.1: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} @@ -6496,10 +7291,26 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + + cli-table@0.3.11: + resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} + engines: {node: '>= 0.2.0'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -6519,10 +7330,18 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmd-shim@8.0.0: + resolution: {integrity: sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==} + engines: {node: ^20.17.0 || >=22.9.0} + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -6570,6 +7389,10 @@ packages: colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + colors@1.0.3: + resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} + engines: {node: '>=0.1.90'} + colors@1.4.0: resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} engines: {node: '>=0.1.90'} @@ -6593,9 +7416,17 @@ packages: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + commenting@1.1.0: resolution: {integrity: sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==} + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} + commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} @@ -6620,6 +7451,9 @@ packages: engines: {node: '>=10.0.0'} hasBin: true + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -6892,6 +7726,10 @@ packages: date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + dateformat@5.0.3: + resolution: {integrity: sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==} + engines: {node: '>=12.20'} + dayjs@1.11.18: resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} @@ -6935,6 +7773,10 @@ packages: babel-plugin-macros: optional: true + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + deep-equal@2.2.3: resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} engines: {node: '>= 0.4'} @@ -7000,10 +7842,18 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -7084,6 +7934,10 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + editions@6.22.0: + resolution: {integrity: sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==} + engines: {ecmascript: '>= es5', node: '>=4'} + editorconfig@3.0.2: resolution: {integrity: sha512-T0ix8GhtxyKVfUFEcvdNDt3YGqlwkFHbD4/5bgFUDgFmxhI/cSRAeJ87/Sz//Cq8Eam6JX/e23RkoFO71P7aAA==} engines: {node: '>=20'} @@ -7092,6 +7946,11 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + ejs@6.0.1: + resolution: {integrity: sha512-UaaM14yby8U3k02ihS1Bmj5Kz2d7CCQM1scxpgs4Mhkq8F1wR2gl3+Ts4h5Ne4Mnt7M9m4Dw7jsuMr3+xO4vZA==} + engines: {node: '>=0.12.18'} + hasBin: true + electron-to-chromium@1.5.237: resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} @@ -7146,6 +8005,10 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -7205,6 +8068,16 @@ packages: es6-weak-map@2.0.3: resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -7431,6 +8304,12 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -7451,6 +8330,10 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + exit-x@0.2.2: resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} engines: {node: '>= 0.8.0'} @@ -7467,6 +8350,9 @@ packages: resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + exponential-backoff@3.1.3: + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} + express-rate-limit@8.6.2: resolution: {integrity: sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==} engines: {node: '>= 16'} @@ -7490,6 +8376,9 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -7503,9 +8392,18 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.4: resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fast-xml-parser@4.5.3: resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} hasBin: true @@ -7529,6 +8427,10 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -7552,6 +8454,10 @@ packages: find-free-port@2.0.0: resolution: {integrity: sha512-J1j8gfEVf5FN4PR5w5wrZZ7NYs2IvqsHcd03cAeQx3Ec/mo+lKceaVNhpsRKoZpZKbId88o8qh+dwUwzBV6WCg==} + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -7564,6 +8470,14 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + first-chunk-stream@5.0.0: + resolution: {integrity: sha512-WdHo4ejd2cG2Dl+sLkW79SctU7mUQDfr4s1i26ffOZRs5mgv+BRttIM9gwcq0rDbemo0KlpVPaa3LBVLqPXzcQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -7582,6 +8496,10 @@ packages: resolution: {integrity: sha512-H/Wqt2SDkQ8GH8wpyAj434zN40zomipZWBbKYlAwQYyNdMIQMKqpXTx82FMnITt2iDQ5zrV80NvSPAgIAafwGA==} engines: {node: '>=0.4.0'} + fly-import@1.0.0: + resolution: {integrity: sha512-JZEaXZw9QR+DRMClMVJYeY5SNn8zzHBuc+KTreFGDBghRXzCiGR9aDgYGP7O/EeoxwHBZ2Brl+2ixlH/Jmt/qg==} + engines: {node: ^20.17.0 || >=22.9.0} + font-atlas@2.1.0: resolution: {integrity: sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==} @@ -7626,6 +8544,10 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -7677,6 +8599,9 @@ packages: resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -7697,10 +8622,17 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + get-tsconfig@4.14.1: + resolution: {integrity: sha512-Dz/6HxkrxgNehhxLVeyv8sad9UzF2xBVeaKBQNDfJ5XiSXmp2gTR0eO0RWiT2NCKS5aGP9jjkOMggTN90qU50A==} + git-hooks-list@4.1.1: resolution: {integrity: sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==} @@ -7782,6 +8714,17 @@ packages: resolution: {integrity: sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==} engines: {node: '>=8'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@16.2.3: + resolution: {integrity: sha512-VZX7TV7jmd/pn71vdnLKtgwy1IWqc3KjI9x1/UtPkwoKk5fKrNLY30ltDe3cAM5xruIN7YuuaulFt133jRrKZg==} + engines: {node: '>=20'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + glsl-inject-defines@1.0.3: resolution: {integrity: sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A==} @@ -7832,12 +8775,19 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} grid-index@1.1.0: resolution: {integrity: sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==} + grouped-queue@2.1.0: + resolution: {integrity: sha512-c5NDCWO0XiXuJAhOegMiNotkDmgORN+VNo3+YHMhWpoWG/u2+8im8byqsOe3/myI9YcC//plRdqGa2AE3Qsdjw==} + engines: {node: '>=8.0.0'} + handlebars@4.7.9: resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} @@ -7915,6 +8865,9 @@ packages: htmlparser2@4.1.0: resolution: {integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -7931,6 +8884,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + husky@8.0.3: resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} engines: {node: '>=14'} @@ -7964,6 +8921,10 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore-walk@8.0.0: + resolution: {integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==} + engines: {node: ^20.17.0 || >=22.9.0} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -8003,6 +8964,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + index-to-position@1.2.0: + resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} + engines: {node: '>=18'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -8017,10 +8982,23 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@5.0.0: + resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} + engines: {node: ^18.17.0 || >=20.5.0} + ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} + inquirer@13.4.3: + resolution: {integrity: sha512-EPd3IqieHSavSOXh+LZhrIkdQcOELWeRblLT6kslQr+cF9XTh/HxZdSt1YkHH1iq4dvqBnV42uwg2YlorgOy6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': ~24.12.0 + peerDependenciesMeta: + '@types/node': + optional: true + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -8133,6 +9111,10 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -8162,6 +9144,10 @@ packages: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + is-plain-obj@1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} @@ -8207,6 +9193,10 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string-blank@1.0.1: resolution: {integrity: sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==} @@ -8229,6 +9219,13 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-utf8@0.2.1: + resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -8250,6 +9247,10 @@ packages: isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isbinaryfile@5.0.7: + resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==} + engines: {node: '>= 18.0.0'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -8257,6 +9258,10 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} + isexe@4.0.0: + resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} + engines: {node: '>=20'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -8547,6 +9552,10 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@5.0.0: + resolution: {integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==} + engines: {node: ^20.17.0 || >=22.9.0} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -8559,6 +9568,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-nice@1.1.4: + resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} + json-stringify-pretty-compact@4.0.0: resolution: {integrity: sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==} @@ -8584,6 +9596,10 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -8595,6 +9611,12 @@ packages: resolution: {integrity: sha512-3KF80UaaSSxo8jVnRYtMKNGFOoVPBdkkVPsw+Ad0y4oxKXPduS6G6iHkrf69yJVff/VAaYXkV42rtZ7daJxU3w==} engines: {node: '>=0.10.0'} + just-diff-apply@5.5.0: + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} + + just-diff@6.0.2: + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} + katex@0.16.25: resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} hasBin: true @@ -8612,6 +9634,14 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + ky@1.14.3: + resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} + engines: {node: '>=18'} + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + leaflet@1.9.4: resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==} @@ -8651,6 +9681,10 @@ packages: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -8663,9 +9697,20 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locate-path@8.0.0: + resolution: {integrity: sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==} + engines: {node: '>=20'} + lodash-es@4.17.23: resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -8695,10 +9740,17 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -8752,6 +9804,10 @@ packages: make-event-props@1.6.2: resolution: {integrity: sha512-iDwf7mA03WPiR8QxvcVHmVWEPfMY1RZXerDVNCRYW7dUr2ppH3J58Rwb39/WG39yTZdRSxr3x+2v22tvI0VEvA==} + make-fetch-happen@15.0.6: + resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==} + engines: {node: ^20.17.0 || >=22.9.0} + makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -8798,6 +9854,28 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + mem-fs-editor@12.0.6: + resolution: {integrity: sha512-I8SCe5xl6kVaoiJTJg33aW/s5wsvjG61kXy3TzH17tr8ZfgkBZFDTBQqwcZMSKx7VBAYgQFJEj3f38qXIWE44Q==} + engines: {node: 20 || >=22} + peerDependencies: + '@types/node': ~24.12.0 + mem-fs: ^4.0.0 + peerDependenciesMeta: + '@types/node': + optional: true + + mem-fs@4.1.5: + resolution: {integrity: sha512-6aWRo1jLo82ngo44tPQXjzINN7gvSNWhMU96I+O5nsJWdPi5vbUJWMTDGZDb/AVpCJRpaLGcFgrIBIpyk0DuUA==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@types/node': ~24.12.0 + '@types/vinyl': ^2.0.12 + peerDependenciesMeta: + '@types/node': + optional: true + '@types/vinyl': + optional: true + memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} @@ -8865,6 +9943,10 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -8898,6 +9980,30 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@5.0.2: + resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + minipass-flush@1.0.7: + resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@2.0.0: + resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@4.2.8: resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} engines: {node: '>=8'} @@ -8906,6 +10012,10 @@ packages: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} @@ -8921,6 +10031,9 @@ packages: engines: {node: '>=10'} hasBin: true + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + mobx-react-lite@4.0.7: resolution: {integrity: sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==} peerDependencies: @@ -8968,9 +10081,21 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + multimatch@8.0.0: + resolution: {integrity: sha512-0D10M2/MnEyvoog7tmozlpSqL3HEU1evxUFa3v1dsKYmBDFSP1dLSX4CH2rNjpQ+4Fps8GKmUkCwiKryaKqd9A==} + engines: {node: '>=20'} + murmurhash-js@1.0.0: resolution: {integrity: sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + mylas@2.1.14: + resolution: {integrity: sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==} + engines: {node: '>=16.0.0'} + nanoevents@9.1.0: resolution: {integrity: sha512-Jd0fILWG44a9luj8v5kED4WI+zfkkgwKyRQKItTtlPfEsh7Lznfi1kr8/iZ+XAIss4Qq5GqRB0qtWbaz9ceO/A==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9046,6 +10171,11 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-gyp@12.4.0: + resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -9061,6 +10191,15 @@ packages: engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} hasBin: true + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + normalize-package-data@8.0.0: + resolution: {integrity: sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==} + engines: {node: ^20.17.0 || >=22.9.0} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -9075,10 +10214,34 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} + npm-bundled@5.0.0: + resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-install-checks@8.0.0: + resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-normalize-package-bin@5.0.0: + resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} + engines: {node: ^20.17.0 || >=22.9.0} + npm-package-arg@13.0.2: resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} engines: {node: ^20.17.0 || >=22.9.0} + npm-packlist@10.0.4: + resolution: {integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-pick-manifest@11.0.3: + resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + npm-registry-fetch@19.1.1: + resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} + engines: {node: ^20.17.0 || >=22.9.0} + npm-run-path@2.0.2: resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} @@ -9087,6 +10250,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -9150,6 +10317,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -9161,6 +10332,10 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + ora@9.4.1: + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} + engines: {node: '>=20'} + own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} @@ -9177,6 +10352,10 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} engines: {node: '>=6'} @@ -9189,14 +10368,42 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@7.0.6: + resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==} + engines: {node: '>=18'} + p-queue@6.6.2: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} + p-queue@8.1.1: + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} + + p-queue@9.3.3: + resolution: {integrity: sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==} + engines: {node: '>=20'} + p-timeout@3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + + p-transform@5.0.1: + resolution: {integrity: sha512-tb3/zIwbU6Z9RMDxZM3/UsyL5LpIUQj7Drq7iXWG9ilPpzyGG28EEFRRrGTsxHf3sOSOiQEiwevQH/VWtHbZfg==} + engines: {node: '>=18.19.0'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -9208,10 +10415,19 @@ packages: resolution: {integrity: sha512-pd5HlhSA4oymER74A8ODqcOHz3sQAQjmTysx1oiJrIMRtIWl9pqxX5L3aUqRb5CBFpXroA8j6fhJmaFKhVUuxQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + package-name-regex@2.0.6: resolution: {integrity: sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA==} engines: {node: '>=12'} + pacote@21.5.1: + resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -9225,10 +10441,22 @@ packages: parenthesis@3.1.8: resolution: {integrity: sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw==} + parse-conflict-json@5.0.1: + resolution: {integrity: sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==} + engines: {node: ^20.17.0 || >=22.9.0} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-json@8.3.0: + resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} + engines: {node: '>=18'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse-rect@1.2.0: resolution: {integrity: sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==} @@ -9253,6 +10481,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -9265,6 +10497,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -9287,6 +10523,15 @@ packages: resolution: {integrity: sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ==} engines: {node: '>=6'} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pbf@3.3.0: resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==} hasBin: true @@ -9349,6 +10594,9 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + playwright-core@1.62.1: resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} engines: {node: '>=20'} @@ -9362,6 +10610,10 @@ packages: engines: {node: '>=20'} hasBin: true + plimit-lit@1.6.1: + resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} + engines: {node: '>=12'} + plotly.js-dist-min@3.1.1: resolution: {integrity: sha512-eyuiESylUXW4kaF+v9J2gy9eZ+YT2uSVLILM4w1Afxnuv9u4UX9OnZnHR1OdF9ybq4x7+9chAzWUUbQ6HvBb3g==} @@ -9651,10 +10903,18 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-bytes@7.1.1: + resolution: {integrity: sha512-X+vn9z8nOFZQlxOLmfJ0iKDdMD7jYTsTW12OAlCpdoE3Igik6L37pugIZi+N3usuyp5McfgKPWi12q3zvHLeGQ==} + engines: {node: '>=20'} + pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@30.3.0: resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -9663,6 +10923,10 @@ packages: resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + pretty-quick@4.2.2: resolution: {integrity: sha512-uAh96tBW1SsD34VhhDmWuEmqbpfYc/B3j++5MC/6b3Cb8Ow7NJsvKFhg0eoGu2xXX+o9RkahkTK6sUdd8E7g5w==} engines: {node: '>=14'} @@ -9680,6 +10944,16 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + proggy@4.0.0: + resolution: {integrity: sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + promise-all-reject-late@1.0.1: + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} + + promise-call-limit@3.0.2: + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==} + promise.series@0.2.0: resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} engines: {node: '>=0.12'} @@ -9729,6 +11003,10 @@ packages: resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} + queue-lit@1.5.2: + resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} + engines: {node: '>=12'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9879,6 +11157,18 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + read-cmd-shim@6.0.0: + resolution: {integrity: sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==} + engines: {node: ^20.17.0 || >=22.9.0} + + read-package-up@12.0.0: + resolution: {integrity: sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==} + engines: {node: '>=20'} + + read-pkg@10.1.0: + resolution: {integrity: sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==} + engines: {node: '>=20'} + readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} @@ -9934,6 +11224,18 @@ packages: resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} engines: {node: '>=4'} + registry-auth-token@5.1.1: + resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + registry-url@7.2.0: + resolution: {integrity: sha512-I5UEBQ+09LWKInA1fPswOMZps0cs2Z+IQXb5Z5EkTJiUmIN52Vm/FD3ji5X82c5jIXL3nWEWOrYK0RkON6Oqyg==} + engines: {node: '>=18'} + regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} @@ -9959,6 +11261,13 @@ packages: remove-accents@0.5.0: resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + replace-ext@2.0.0: + resolution: {integrity: sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==} + engines: {node: '>= 10'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -9979,6 +11288,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-protobuf-schema@2.1.0: resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==} @@ -10008,6 +11320,10 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -10068,6 +11384,11 @@ packages: peerDependencies: rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 + rollup@3.30.0: + resolution: {integrity: sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -10085,6 +11406,10 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-async@4.0.6: + resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -10095,6 +11420,9 @@ packages: resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} engines: {npm: '>=2.0.0'} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -10249,6 +11577,9 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -10262,12 +11593,19 @@ packages: signum@1.0.0: resolution: {integrity: sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw==} + sigstore@4.1.1: + resolution: {integrity: sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==} + engines: {node: ^20.17.0 || >=22.9.0} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-git@3.36.0: + resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==} + slash@1.0.0: resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==} engines: {node: '>=0.10.0'} @@ -10276,9 +11614,29 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sort-keys@6.0.1: + resolution: {integrity: sha512-w7xWRu8U9MneKNna8ptG194jp9PLtbd/Rl6gwrmbK4yUeKbE66a64rHgl0iKTBBDr/hpanx7zMGP1Qo8MRkc/w==} + engines: {node: '>=20'} + sort-object-keys@1.1.3: resolution: {integrity: sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==} @@ -10319,6 +11677,9 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + spdx-expression-validate@2.0.0: resolution: {integrity: sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==} @@ -10334,6 +11695,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssri@13.0.1: + resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} + engines: {node: ^20.17.0 || >=22.9.0} + stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -10345,6 +11710,9 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + static-eval@2.1.1: resolution: {integrity: sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==} @@ -10352,6 +11720,13 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + engines: {node: '>=18'} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} @@ -10362,6 +11737,9 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamx@2.28.0: + resolution: {integrity: sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==} + string-hash@1.1.3: resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} @@ -10384,6 +11762,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -10420,6 +11802,14 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} + strip-bom-buf@3.0.1: + resolution: {integrity: sha512-iJaWw2WroigLHzQysdc5WWeUc99p7ea7AEgB6JkY8CMyiO1yTVAA1gIlJJgORElUIR+lcZJkNl1OGChMhvc2Cw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + strip-bom-stream@5.0.0: + resolution: {integrity: sha512-Yo472mU+3smhzqeKlIxClre4s4pwtYZEvDNQvY/sJpnChdaxmKuwU28UVx/v1ORKNMxkmj1GBuvxJQyBk6wYMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -10436,6 +11826,10 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -10448,6 +11842,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} @@ -10519,6 +11916,10 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tar-fs@2.1.4: resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} @@ -10526,6 +11927,13 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} + engines: {node: '>=18'} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + terser@5.44.0: resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} @@ -10535,6 +11943,16 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + textextensions@6.11.0: + resolution: {integrity: sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==} + engines: {node: '>=4'} + through2@0.6.5: resolution: {integrity: sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==} @@ -10544,6 +11962,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} @@ -10561,12 +11982,20 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + tinyqueue@2.0.3: resolution: {integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==} tinyqueue@3.0.0: resolution: {integrity: sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==} + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} @@ -10614,6 +12043,10 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + treeverse@3.0.0: + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -10671,6 +12104,22 @@ packages: '@swc/wasm': optional: true + tsc-alias@1.9.1: + resolution: {integrity: sha512-sFZdVFthH8uvdplPJrOYGOHcxu6UPtcAcY678JPwEQiMzgLZYFO7Qc/rzELp7ingTc+OxtzH6n+8Pn2eVQep6w==} + engines: {node: '>=16.20.2'} + hasBin: true + + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + deprecated: unmaintained + hasBin: true + peerDependencies: + typescript: '>5.8.0 <6.0.0' + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -10680,6 +12129,15 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.23.12: + resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + engines: {node: '>=18.0.0'} + hasBin: true + + tuf-js@4.1.0: + resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} + engines: {node: ^20.17.0 || >=22.9.0} + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -10728,6 +12186,10 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -10736,6 +12198,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.8.0: + resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} + engines: {node: '>=20'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -10780,6 +12246,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -10797,6 +12266,10 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici@6.28.0: + resolution: {integrity: sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==} + engines: {node: '>=18.17'} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -10813,6 +12286,18 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unicorn-magic@0.4.0: + resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} + engines: {node: '>=20'} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -10831,6 +12316,10 @@ packages: unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} + untildify@6.0.0: + resolution: {integrity: sha512-sA2YTBvW2F463GvSbiZtso+dpuQV+B7xX9saX30SGrR5Fyx4AUcvA/zN+ShAkABKUKVyDaHECsJrHv5ToTuHsQ==} + engines: {node: '>=20'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -10879,6 +12368,90 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + version-range@4.15.0: + resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} + engines: {node: '>=4'} + + vinyl-file@5.0.0: + resolution: {integrity: sha512-MvkPF/yA1EX7c6p+juVIvp9+Lxp70YUfNKzEWeHMKpUNVSnTZh2coaOqLxI0pmOe2V9nB+OkgFaMDkodaJUyGw==} + engines: {node: '>=14.16'} + + vinyl@3.0.1: + resolution: {integrity: sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==} + engines: {node: '>=10.13.0'} + + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@4.5.14: + resolution: {integrity: sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': ~24.12.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + vlq@0.2.3: resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==} @@ -10892,6 +12465,10 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -10950,6 +12527,10 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} + which-package-manager@1.0.1: + resolution: {integrity: sha512-Nse2rVsL302dkEhCyyS1U3iEQ9FRYPPkWJNk188xUVkKIGXjMmDPlA3L1VettE+T2z7SGLsJiDaZw//8CHUQwQ==} + engines: {node: '>=18'} + which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} @@ -10968,6 +12549,16 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true + which@6.0.1: + resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -10997,6 +12588,10 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + write-file-atomic@7.0.1: + resolution: {integrity: sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==} + engines: {node: ^20.17.0 || >=22.9.0} + ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -11063,6 +12658,13 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -11091,6 +12693,26 @@ packages: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yeoman-environment@6.1.0: + resolution: {integrity: sha512-QSKMfrSx3js9fxbMDBa+sZG0ctF0NDxcO6VA5BnrWdo6wqhdalT2IUU/ZrNakQLZQ76E3B6b0lXHKEA7ivsWxA==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + peerDependencies: + '@yeoman/adapter': ^4.0.2 + '@yeoman/types': ^1.10.3 + mem-fs: ^4.1.4 + + yeoman-generator@8.2.2: + resolution: {integrity: sha512-GIvRULf09VrTyJ1nMIxCRFTI8gzW9zsAxVXTHOmsWVKZ7QYPdByRQvFtnp0XOObM6dvDSoAwBhuYR6i5inp/ig==} + engines: {node: ^20.17.0 || >=22.9.0} + peerDependencies: + '@types/node': ~24.12.0 + '@yeoman/types': ^1.1.1 + mem-fs: ^4.0.0 + peerDependenciesMeta: + '@types/node': + optional: true + yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -11099,6 +12721,14 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + yoctocolors@2.2.0: + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + engines: {node: '>=18'} + zip-a-folder@6.1.4: resolution: {integrity: sha512-6zRF/xi0zRxKOCTBVWLwWyyp+zuAKyBhM3Q2ddgEg8uvjDFSTsKulc476jUAaNXnw3lI8pGRuffRNPbxNUld+g==} hasBin: true @@ -11117,6 +12747,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + snapshots: '@adobe/css-tools@4.4.4': {} @@ -12638,14 +14271,158 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.18.20': optional: true - '@emnapi/wasi-threads@1.2.1': - dependencies: - tslib: 2.8.1 + '@esbuild/win32-x64@0.28.2': optional: true '@eslint-community/eslint-utils@4.10.1(eslint@9.39.3(jiti@2.6.1))': @@ -12755,6 +14532,8 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@gar/promise-retry@1.0.3': {} + '@googlemaps/jest-mocks@2.22.6': {} '@happy-dom/jest-environment@19.0.2(@jest/environment@30.4.1)(@jest/fake-timers@30.4.1)(@jest/types@30.4.1)(jest-mock@30.4.1)(jest-util@30.4.1)': @@ -12785,6 +14564,125 @@ snapshots: dependencies: react: 18.3.1 + '@inquirer/ansi@2.0.7': {} + + '@inquirer/checkbox@5.2.1(@types/node@24.12.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/confirm@6.1.1(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/core@11.2.1(@types/node@24.12.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@24.12.4) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/editor@5.2.2(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/external-editor': 3.0.3(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/expand@5.1.1(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/external-editor@3.0.3(@types/node@24.12.4)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/figures@2.0.7': {} + + '@inquirer/input@5.1.2(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/number@4.1.1(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/password@5.1.1(@types/node@24.12.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/prompts@8.5.2(@types/node@24.12.4)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@24.12.4) + '@inquirer/confirm': 6.1.1(@types/node@24.12.4) + '@inquirer/editor': 5.2.2(@types/node@24.12.4) + '@inquirer/expand': 5.1.1(@types/node@24.12.4) + '@inquirer/input': 5.1.2(@types/node@24.12.4) + '@inquirer/number': 4.1.1(@types/node@24.12.4) + '@inquirer/password': 5.1.1(@types/node@24.12.4) + '@inquirer/rawlist': 5.3.1(@types/node@24.12.4) + '@inquirer/search': 4.2.1(@types/node@24.12.4) + '@inquirer/select': 5.2.1(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/rawlist@5.3.1(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/search@4.2.1(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/select@5.2.1(@types/node@24.12.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@24.12.4) + optionalDependencies: + '@types/node': 24.12.4 + + '@inquirer/type@4.0.7(@types/node@24.12.4)': + optionalDependencies: + '@types/node': 24.12.4 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -12794,6 +14692,12 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@isaacs/string-locale-compare@1.1.0': {} + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -13014,6 +14918,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.12 + '@jest/schemas@30.0.5': dependencies: '@sinclair/typebox': 0.34.41 @@ -13146,6 +15054,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@kwsites/file-exists@1.1.1': + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@kwsites/promise-deferred@1.1.1': {} + '@lezer/common@1.2.3': {} '@lezer/css@1.3.0': @@ -13217,6 +15133,22 @@ snapshots: '@melloware/coloris@0.25.0': {} + '@mendix/generator-widget@11.13.0(@types/node@24.12.4)(@types/vinyl@2.0.12)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))': + dependencies: + chalk: 5.6.2 + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) + semver: 7.8.5 + yeoman-environment: 6.1.0(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + yeoman-generator: 8.2.2(@types/node@24.12.4)(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + transitivePeerDependencies: + - '@types/node' + - '@types/vinyl' + - '@yeoman/adapter' + - '@yeoman/types' + - bare-abort-controller + - react-native-b4a + - supports-color + '@mendix/pluggable-widgets-tools@11.11.0(patch_hash=036a1e3d1a57e7418725babb71e5eef5220ae90fc481ad7e04fa7e8901b25801)(@jest/transform@30.4.1)(@jest/types@30.4.1)(@swc/core@1.13.5)(@types/babel__core@7.20.5)(@types/node@24.12.4)(canvas@3.2.0)(eslint@9.39.3(jiti@2.6.1))(jest-util@30.4.1)(picomatch@4.0.5)(prettier@3.9.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': dependencies: '@babel/core': 7.29.7 @@ -13332,6 +15264,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)': + dependencies: + '@hono/node-server': 2.1.0(hono@4.13.1) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.6.2(express@5.2.1) + hono: 4.13.1 + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.4.3 + zod-to-json-schema: 3.25.1(zod@4.4.3) + transitivePeerDependencies: + - supports-color + '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true @@ -13358,6 +15312,124 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@npmcli/agent@4.0.2': + dependencies: + agent-base: 7.1.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 11.2.2 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/arborist@9.9.1': + dependencies: + '@gar/promise-retry': 1.0.3 + '@isaacs/string-locale-compare': 1.1.0 + '@npmcli/fs': 5.0.0 + '@npmcli/installed-package-contents': 4.0.0 + '@npmcli/map-workspaces': 5.0.3 + '@npmcli/metavuln-calculator': 9.0.3 + '@npmcli/name-from-folder': 4.0.0 + '@npmcli/node-gyp': 5.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/query': 5.0.0 + '@npmcli/redact': 4.0.0 + '@npmcli/run-script': 10.0.4 + bin-links: 6.0.2 + cacache: 20.0.4 + common-ancestor-path: 2.0.0 + hosted-git-info: 9.0.3 + json-stringify-nice: 1.1.4 + lru-cache: 11.2.2 + minimatch: 10.2.6 + nopt: 9.0.0 + npm-install-checks: 8.0.0 + npm-package-arg: 13.0.2 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1 + pacote: 21.5.1 + parse-conflict-json: 5.0.1 + proc-log: 6.1.0 + proggy: 4.0.0 + promise-all-reject-late: 1.0.1 + promise-call-limit: 3.0.2 + semver: 7.8.5 + ssri: 13.0.1 + treeverse: 3.0.0 + walk-up-path: 4.0.0 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@5.0.0': + dependencies: + semver: 7.8.5 + + '@npmcli/git@7.0.2': + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/promise-spawn': 9.0.1 + ini: 6.0.0 + lru-cache: 11.2.2 + npm-pick-manifest: 11.0.3 + proc-log: 6.1.0 + semver: 7.8.5 + which: 6.0.1 + + '@npmcli/installed-package-contents@4.0.0': + dependencies: + npm-bundled: 5.0.0 + npm-normalize-package-bin: 5.0.0 + + '@npmcli/map-workspaces@5.0.3': + dependencies: + '@npmcli/name-from-folder': 4.0.0 + '@npmcli/package-json': 7.0.5 + glob: 13.0.6 + minimatch: 10.2.6 + + '@npmcli/metavuln-calculator@9.0.3': + dependencies: + cacache: 20.0.4 + json-parse-even-better-errors: 5.0.0 + pacote: 21.5.1 + proc-log: 6.1.0 + semver: 7.8.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/name-from-folder@4.0.0': {} + + '@npmcli/node-gyp@5.0.0': {} + + '@npmcli/package-json@7.0.5': + dependencies: + '@npmcli/git': 7.0.2 + glob: 13.0.6 + hosted-git-info: 9.0.3 + json-parse-even-better-errors: 5.0.0 + proc-log: 6.1.0 + semver: 7.8.5 + spdx-expression-parse: 4.0.0 + + '@npmcli/promise-spawn@9.0.1': + dependencies: + which: 6.0.1 + + '@npmcli/query@5.0.0': + dependencies: + postcss-selector-parser: 7.1.0 + + '@npmcli/redact@4.0.0': {} + + '@npmcli/run-script@10.0.4': + dependencies: + '@npmcli/node-gyp': 5.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/promise-spawn': 9.0.1 + node-gyp: 12.4.0 + proc-log: 6.1.0 + '@one-ini/wasm@0.2.1': {} '@parcel/watcher-android-arm64@2.5.1': @@ -13489,6 +15561,18 @@ snapshots: '@plotly/regl@2.1.2': {} + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@3.0.3': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + '@popperjs/core@2.11.8': {} '@prettier/plugin-xml@3.4.2(prettier@3.9.6)': @@ -13959,14 +16043,58 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@sec-ant/readable-stream@0.4.1': {} + + '@sigstore/bundle@4.0.0': + dependencies: + '@sigstore/protobuf-specs': 0.5.1 + + '@sigstore/core@3.2.1': {} + + '@sigstore/protobuf-specs@0.5.1': {} + + '@sigstore/sign@4.1.1': + dependencies: + '@gar/promise-retry': 1.0.3 + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.1 + make-fetch-happen: 15.0.6 + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color + + '@sigstore/tuf@4.0.2': + dependencies: + '@sigstore/protobuf-specs': 0.5.1 + tuf-js: 4.1.0 + transitivePeerDependencies: + - supports-color + + '@sigstore/verify@3.1.1': + dependencies: + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.1 + + '@simple-git/args-pathspec@1.0.3': {} + + '@simple-git/argv-parser@1.1.1': + dependencies: + '@simple-git/args-pathspec': 1.0.3 + '@simple-libs/child-process-utils@2.0.0': dependencies: '@simple-libs/stream-utils': 2.0.0 '@simple-libs/stream-utils@2.0.0': {} + '@sinclair/typebox@0.27.12': {} + '@sinclair/typebox@0.34.41': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -14078,6 +16206,13 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@tufjs/canonical-json@2.0.0': {} + + '@tufjs/models@4.1.0': + dependencies: + '@tufjs/canonical-json': 2.0.0 + minimatch: 10.2.6 + '@turf/area@7.2.0': dependencies: '@turf/helpers': 7.2.0 @@ -14139,12 +16274,33 @@ snapshots: '@types/big.js@6.2.2': {} + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.12.4 + + '@types/chai-subset@1.3.6(@types/chai@4.3.20)': + dependencies: + '@types/chai': 4.3.20 + + '@types/chai@4.3.20': {} + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.12.4 + '@types/cross-zip@4.0.2': {} '@types/date-arithmetic@4.1.4': {} + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + '@types/deep-equal@1.0.4': {} + '@types/ejs@3.1.5': {} + '@types/eslint@9.6.1': dependencies: '@types/estree': 1.0.9 @@ -14155,6 +16311,21 @@ snapshots: '@types/estree@1.0.9': {} + '@types/expect@1.20.4': {} + + '@types/express-serve-static-core@5.1.3': + dependencies: + '@types/node': 24.12.4 + '@types/qs': 6.15.1 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.6': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.3 + '@types/serve-static': 2.2.0 + '@types/fs-extra@8.1.5': dependencies: '@types/node': 24.12.4 @@ -14172,6 +16343,8 @@ snapshots: '@types/google.maps@3.58.1': {} + '@types/http-errors@2.0.5': {} + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -14207,6 +16380,12 @@ snapshots: '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.25 + + '@types/lodash@4.17.25': {} + '@types/mapbox__point-geometry@0.1.4': {} '@types/mapbox__vector-tile@1.3.4': @@ -14226,6 +16405,8 @@ snapshots: '@types/minimatch@3.0.5': {} + '@types/ms@2.1.0': {} + '@types/node-fetch@2.6.12': dependencies: '@types/node': 24.12.4 @@ -14235,8 +16416,12 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/normalize-package-data@2.4.4': {} + '@types/pbf@3.0.5': {} + '@types/picomatch@4.0.3': {} + '@types/plotly.js-dist-min@2.3.4': dependencies: '@types/plotly.js': 3.0.7 @@ -14245,6 +16430,10 @@ snapshots: '@types/prop-types@15.7.15': {} + '@types/qs@6.15.1': {} + + '@types/range-parser@1.2.7': {} + '@types/rc-slider@8.6.6': dependencies: '@types/rc-tooltip': 3.7.14 @@ -14299,6 +16488,15 @@ snapshots: '@types/semver@7.7.1': {} + '@types/send@1.2.1': + dependencies: + '@types/node': 24.12.4 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.12.4 + '@types/stack-utils@2.0.3': {} '@types/supercluster@7.1.3': @@ -14314,6 +16512,11 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/vinyl@2.0.12': + dependencies: + '@types/expect': 1.20.4 + '@types/node': 24.12.4 + '@types/warning@3.0.3': {} '@types/whatwg-mimetype@3.0.2': {} @@ -14577,19 +16780,96 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': optional: true - '@unrs/resolver-binding-win32-x64-msvc@1.12.2': - optional: true + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@vis.gl/react-google-maps@0.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@types/google.maps': 3.58.1 + fast-deep-equal: 3.1.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@vitest/expect@0.34.6': + dependencies: + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.5.0 + + '@vitest/runner@0.34.6': + dependencies: + '@vitest/utils': 0.34.6 + p-limit: 4.0.0 + pathe: 1.1.2 + + '@vitest/snapshot@0.34.6': + dependencies: + magic-string: 0.30.19 + pathe: 1.1.2 + pretty-format: 29.7.0 + + '@vitest/spy@0.34.6': + dependencies: + tinyspy: 2.2.1 + + '@vitest/utils@0.34.6': + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + + '@xml-tools/parser@1.0.11': + dependencies: + chevrotain: 7.1.1 + + '@yeoman/adapter@4.0.2(@types/node@24.12.4)': + dependencies: + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/prompts': 8.5.2(@types/node@24.12.4) + chalk: 5.6.2 + inquirer: 13.4.3(@types/node@24.12.4) + log-symbols: 7.0.1 + ora: 9.4.1 + p-queue: 9.3.3 + text-table: 0.2.0 + transitivePeerDependencies: + - '@types/node' + + '@yeoman/conflicter@4.1.0(@types/node@24.12.4)(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12))': + dependencies: + '@types/node': 24.12.4 + '@yeoman/transform': 2.1.2(@types/node@24.12.4) + '@yeoman/types': 1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + binary-extensions: 3.1.0 + cli-table: 0.3.11 + dateformat: 5.0.3 + diff: 9.0.0 + isbinaryfile: 5.0.7 + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) + mem-fs-editor: 12.0.6(@types/node@24.12.4)(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + minimatch: 10.2.6 + p-transform: 5.0.1 + pretty-bytes: 7.1.1 + slash: 5.1.0 + textextensions: 6.11.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + - supports-color + + '@yeoman/namespace@2.1.0': {} - '@vis.gl/react-google-maps@0.8.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@yeoman/transform@2.1.2(@types/node@24.12.4)': dependencies: - '@types/google.maps': 3.58.1 - fast-deep-equal: 3.1.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) + '@types/node': 24.12.4 + minimatch: 9.0.9 - '@xml-tools/parser@1.0.11': + '@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12))': dependencies: - chevrotain: 7.1.1 + '@types/node': 24.12.4 + optionalDependencies: + '@yeoman/adapter': 4.0.2(@types/node@24.12.4) + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) '@zxing/library@0.21.3': dependencies: @@ -14600,6 +16880,8 @@ snapshots: '@zxing/text-encoding@0.9.0': optional: true + abbrev@4.0.0: {} + abbrev@5.0.0: {} abs-svg-path@0.1.1: {} @@ -14621,6 +16903,8 @@ snapshots: acorn@8.15.0: {} + acorn@8.18.0: {} + agent-base@7.1.4: {} ajv-formats@3.0.1(ajv@8.17.1): @@ -14689,6 +16973,8 @@ snapshots: array-differ@1.0.0: {} + array-differ@4.0.0: {} + array-find-index@1.0.2: {} array-includes@3.1.9: @@ -14716,6 +17002,8 @@ snapshots: array-union@2.1.0: {} + array-union@3.0.1: {} + array-uniq@1.0.3: {} array.prototype.findlast@1.2.5: @@ -14771,8 +17059,12 @@ snapshots: arrify@1.0.1: {} + arrify@3.0.0: {} + asap@2.0.6: {} + assertion-error@1.1.0: {} + ast-types@0.16.1: dependencies: tslib: 2.8.1 @@ -14789,6 +17081,8 @@ snapshots: axe-core@4.12.1: {} + b4a@1.8.1: {} + babel-jest@30.4.1(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -14903,6 +17197,8 @@ snapshots: balanced-match@4.0.4: {} + bare-events@2.9.1: {} + base64-arraybuffer@1.0.2: {} base64-js@1.5.1: {} @@ -14913,10 +17209,24 @@ snapshots: big.js@6.2.2: {} + bin-links@6.0.2: + dependencies: + cmd-shim: 8.0.0 + npm-normalize-package-bin: 5.0.0 + proc-log: 6.1.0 + read-cmd-shim: 6.0.0 + write-file-atomic: 7.0.1 + binary-extensions@2.3.0: {} + binary-extensions@3.1.0: {} + binary-search-bounds@2.0.5: {} + binaryextensions@6.11.0: + dependencies: + editions: 6.22.0 + bit-twiddle@1.0.2: {} bitmap-sdf@1.0.4: {} @@ -15005,6 +17315,21 @@ snapshots: bytes@3.1.2: {} + cac@6.7.14: {} + + cacache@20.0.4: + dependencies: + '@npmcli/fs': 5.0.0 + fs-minipass: 3.0.3 + glob: 13.0.6 + lru-cache: 11.2.2 + minipass: 7.1.3 + minipass-collect: 2.0.1 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + p-map: 7.0.6 + ssri: 13.0.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -15049,6 +17374,16 @@ snapshots: prebuild-install: 7.1.3 optional: true + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -15062,6 +17397,12 @@ snapshots: char-regex@1.0.2: {} + chardet@2.2.0: {} + + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + chevrotain@7.1.1: dependencies: regexp-to-ast: 0.5.0 @@ -15085,6 +17426,8 @@ snapshots: chownr@1.1.4: optional: true + chownr@3.0.0: {} + ci-info@4.3.1: {} cjs-module-lexer@2.2.0: {} @@ -15097,8 +17440,20 @@ snapshots: dependencies: restore-cursor: 3.1.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + cli-spinners@2.9.2: {} + cli-spinners@3.4.0: {} + + cli-table@0.3.11: + dependencies: + colors: 1.0.3 + + cli-width@4.1.0: {} + cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -15125,8 +17480,12 @@ snapshots: clone@1.0.4: {} + clone@2.1.2: {} + clsx@2.1.1: {} + cmd-shim@8.0.0: {} + co@4.6.0: {} codemirror@6.0.2: @@ -15185,6 +17544,8 @@ snapshots: colorette@1.4.0: {} + colors@1.0.3: {} + colors@1.4.0: {} combined-stream@1.0.8: @@ -15199,8 +17560,12 @@ snapshots: commander@8.3.0: {} + commander@9.5.0: {} + commenting@1.1.0: {} + common-ancestor-path@2.0.0: {} + commondir@1.0.1: {} compute-scroll-into-view@2.0.4: {} @@ -15231,6 +17596,8 @@ snapshots: tree-kill: 1.2.2 yargs: 16.2.0 + confbox@0.1.8: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -15503,7 +17870,7 @@ snapshots: commander: 2.20.3 d3-array: 1.2.4 d3-geo: 1.12.1 - resolve: 1.22.10 + resolve: 1.22.12 d3-geo@1.12.1: dependencies: @@ -15571,6 +17938,8 @@ snapshots: date-fns@4.1.0: {} + dateformat@5.0.3: {} + dayjs@1.11.18: {} debug@2.6.9: @@ -15594,6 +17963,10 @@ snapshots: dedent@1.7.0: {} + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.2 @@ -15615,8 +17988,7 @@ snapshots: which-collection: 1.0.2 which-typed-array: 1.1.19 - deep-extend@0.6.0: - optional: true + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -15660,8 +18032,12 @@ snapshots: detect-newline@4.0.1: {} + diff-sequences@29.6.3: {} + diff@4.0.2: {} + diff@9.0.0: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -15757,6 +18133,10 @@ snapshots: eastasianwidth@0.2.0: {} + editions@6.22.0: + dependencies: + version-range: 4.15.0 + editorconfig@3.0.2: dependencies: '@one-ini/wasm': 0.2.1 @@ -15766,6 +18146,8 @@ snapshots: ee-first@1.1.1: {} + ejs@6.0.1: {} + electron-to-chromium@1.5.237: {} electron-to-chromium@1.5.403: {} @@ -15808,6 +18190,8 @@ snapshots: env-paths@2.2.1: {} + env-paths@3.0.0: {} + errno@0.1.8: dependencies: prr: 1.0.1 @@ -15956,6 +18340,60 @@ snapshots: es6-iterator: 2.0.3 es6-symbol: 3.1.4 + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -16224,6 +18662,14 @@ snapshots: eventemitter3@5.0.1: {} + eventemitter3@5.0.4: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.9.1 + transitivePeerDependencies: + - bare-abort-controller + events@3.3.0: {} eventsource-parser@3.0.6: {} @@ -16254,6 +18700,21 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.2.0 + exit-x@0.2.2: {} expand-template@2.0.3: @@ -16277,6 +18738,8 @@ snapshots: jest-mock: 30.4.1 jest-util: 30.4.1 + exponential-backoff@3.1.3: {} + express-rate-limit@8.6.2(express@5.2.1): dependencies: debug: 4.4.3 @@ -16331,6 +18794,8 @@ snapshots: fast-diff@1.3.0: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -16347,8 +18812,18 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.4: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fast-xml-parser@4.5.3: dependencies: strnum: 1.1.2 @@ -16374,6 +18849,10 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -16405,6 +18884,8 @@ snapshots: find-free-port@2.0.0: {} + find-up-simple@1.0.1: {} + find-up@3.0.0: dependencies: locate-path: 3.0.0 @@ -16419,6 +18900,14 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + first-chunk-stream@5.0.0: {} + flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -16436,6 +18925,15 @@ snapshots: dependencies: flow-estree: 0.326.0 + fly-import@1.0.0: + dependencies: + '@npmcli/arborist': 9.9.1 + env-paths: 3.0.0 + registry-auth-token: 5.1.1 + registry-url: 7.2.0 + transitivePeerDependencies: + - supports-color + font-atlas@2.1.0: dependencies: css-font: 1.2.0 @@ -16489,6 +18987,10 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.3 + fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -16528,6 +19030,8 @@ snapshots: get-east-asian-width@1.6.0: {} + get-func-name@2.0.2: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -16554,12 +19058,21 @@ snapshots: get-stream@6.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 + get-tsconfig@4.14.1: + dependencies: + resolve-pkg-maps: 1.0.0 + git-hooks-list@4.1.1: {} github-from-package@0.0.0: @@ -16681,6 +19194,26 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@16.2.3: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + is-path-inside: 4.0.0 + slash: 5.1.0 + unicorn-magic: 0.4.0 + + globrex@0.1.2: {} + glsl-inject-defines@1.0.3: dependencies: glsl-token-inject-block: 1.1.0 @@ -16743,7 +19276,7 @@ snapshots: graceful-fs: 4.2.11 inherits: 2.0.4 map-limit: 0.0.1 - resolve: 1.22.10 + resolve: 1.22.12 glslify@7.1.1: dependencies: @@ -16757,7 +19290,7 @@ snapshots: glslify-bundle: 5.1.1 glslify-deps: 1.3.2 minimist: 1.2.8 - resolve: 1.22.10 + resolve: 1.22.12 stack-trace: 0.0.9 static-eval: 2.1.1 through2: 2.0.5 @@ -16765,10 +19298,14 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.10: {} + graceful-fs@4.2.11: {} grid-index@1.1.0: {} + grouped-queue@2.1.0: {} + handlebars@4.7.9: dependencies: minimist: 1.2.8 @@ -16847,6 +19384,8 @@ snapshots: domutils: 2.8.0 entities: 2.2.0 + http-cache-semantics@4.2.0: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -16871,6 +19410,8 @@ snapshots: human-signals@2.1.0: {} + human-signals@8.0.1: {} + husky@8.0.3: {} iconv-lite@0.4.24: @@ -16901,6 +19442,10 @@ snapshots: ieee754@1.2.1: {} + ignore-walk@8.0.0: + dependencies: + minimatch: 10.2.6 + ignore@5.3.2: {} ignore@7.0.5: {} @@ -16931,6 +19476,8 @@ snapshots: indent-string@4.0.0: {} + index-to-position@1.2.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -16942,8 +19489,22 @@ snapshots: ini@4.1.3: {} + ini@5.0.0: {} + ini@6.0.0: {} + inquirer@13.4.3(@types/node@24.12.4): + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@24.12.4) + '@inquirer/prompts': 8.5.2(@types/node@24.12.4) + '@inquirer/type': 4.0.7(@types/node@24.12.4) + mute-stream: 3.0.0 + run-async: 4.0.6 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 24.12.4 + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -17049,6 +19610,8 @@ snapshots: is-interactive@1.0.0: {} + is-interactive@2.0.0: {} + is-map@2.0.3: {} is-mobile@4.0.0: {} @@ -17068,6 +19631,8 @@ snapshots: is-obj@1.0.1: {} + is-path-inside@4.0.0: {} + is-plain-obj@1.1.0: {} is-plain-obj@4.1.0: {} @@ -17103,6 +19668,8 @@ snapshots: is-stream@2.0.1: {} + is-stream@4.0.1: {} + is-string-blank@1.0.1: {} is-string@1.1.1: @@ -17124,6 +19691,10 @@ snapshots: is-unicode-supported@0.1.0: {} + is-unicode-supported@2.1.0: {} + + is-utf8@0.2.1: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -17141,10 +19712,14 @@ snapshots: isarray@2.0.5: {} + isbinaryfile@5.0.7: {} + isexe@2.0.0: {} isexe@3.1.1: {} + isexe@4.0.0: {} + isobject@3.0.1: {} istanbul-lib-coverage@3.2.2: {} @@ -17414,7 +19989,7 @@ snapshots: jest-regex-util: 30.0.1 jest-util: 30.3.0 jest-worker: 30.3.0 - picomatch: 4.0.3 + picomatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -17429,7 +20004,7 @@ snapshots: jest-regex-util: 30.4.0 jest-util: 30.4.1 jest-worker: 30.4.1 - picomatch: 4.0.3 + picomatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -17749,7 +20324,7 @@ snapshots: graceful-fs: 4.2.11 neo-async: 2.6.2 picocolors: 1.1.1 - picomatch: 4.0.3 + picomatch: 4.0.5 recast: 0.23.20 tmp: 0.2.7 write-file-atomic: 5.0.1 @@ -17793,6 +20368,8 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-parse-even-better-errors@5.0.0: {} + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -17801,6 +20378,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-nice@1.1.4: {} + json-stringify-pretty-compact@4.0.0: {} json5@1.0.2: @@ -17828,6 +20407,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -17844,6 +20425,10 @@ snapshots: junk@1.0.3: {} + just-diff-apply@5.5.0: {} + + just-diff@6.0.2: {} + katex@0.16.25: dependencies: commander: 8.3.0 @@ -17858,6 +20443,12 @@ snapshots: kind-of@6.0.3: {} + ky@1.14.3: {} + + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + leaflet@1.9.4: {} leven@3.1.0: {} @@ -17895,6 +20486,8 @@ snapshots: loader-utils@3.3.1: {} + local-pkg@0.4.3: {} + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -17908,8 +20501,18 @@ snapshots: dependencies: p-locate: 5.0.0 + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + locate-path@8.0.0: + dependencies: + p-locate: 6.0.0 + lodash-es@4.17.23: {} + lodash-es@4.18.1: {} + lodash.camelcase@4.3.0: {} lodash.clonedeep@4.5.0: {} @@ -17931,10 +20534,19 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.2.0 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + lru-cache@10.4.3: {} lru-cache@11.2.2: {} @@ -17978,6 +20590,23 @@ snapshots: make-event-props@1.6.2: {} + make-fetch-happen@15.0.6: + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/agent': 4.0.2 + '@npmcli/redact': 4.0.0 + cacache: 20.0.4 + http-cache-semantics: 4.2.0 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minipass-flush: 1.0.7 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 6.1.0 + ssri: 13.0.1 + transitivePeerDependencies: + - supports-color + makeerror@1.0.12: dependencies: tmpl: 1.0.5 @@ -18073,6 +20702,41 @@ snapshots: media-typer@1.1.0: {} + mem-fs-editor@12.0.6(@types/node@24.12.4)(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)): + dependencies: + '@types/ejs': 3.1.5 + '@types/picomatch': 4.0.3 + binaryextensions: 6.11.0 + commondir: 1.0.1 + debug: 4.4.3 + deep-extend: 0.6.0 + ejs: 6.0.1 + isbinaryfile: 5.0.7 + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) + minimatch: 10.2.6 + multimatch: 8.0.0 + normalize-path: 3.0.0 + textextensions: 6.11.0 + tinyglobby: 0.2.17 + vinyl: 3.0.1 + optionalDependencies: + '@types/node': 24.12.4 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + - supports-color + + mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12): + dependencies: + vinyl: 3.0.1 + vinyl-file: 5.0.0 + optionalDependencies: + '@types/node': 24.12.4 + '@types/vinyl': 2.0.12 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + memoize-one@6.0.0: {} mendix@10.24.75382: @@ -18120,6 +20784,8 @@ snapshots: mimic-fn@2.1.0: {} + mimic-function@5.0.1: {} + mimic-response@3.1.0: optional: true @@ -18149,10 +20815,42 @@ snapshots: minimist@1.2.8: {} + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.3 + + minipass-fetch@5.0.2: + dependencies: + minipass: 7.1.3 + minipass-sized: 2.0.0 + minizlib: 3.1.0 + optionalDependencies: + iconv-lite: 0.7.3 + + minipass-flush@1.0.7: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@2.0.0: + dependencies: + minipass: 7.1.3 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@4.2.8: {} minipass@7.1.3: {} + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + mitt@3.0.1: {} mkdirp-classic@0.5.3: @@ -18164,6 +20862,13 @@ snapshots: mkdirp@1.0.4: {} + mlly@1.8.2: + dependencies: + acorn: 8.18.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + mobx-react-lite@4.0.7(patch_hash=47fd2d1b5c35554ddd4fa32fcaa928a16fda9f82dca0ff68bcdc1f7c3e5f9d1a)(mobx@6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: mobx: 6.12.3(patch_hash=39c55279e8f75c9a322eba64dd22e1a398f621c64bbfc3632e55a97f46edfeb9) @@ -18210,8 +20915,18 @@ snapshots: ms@2.1.3: {} + multimatch@8.0.0: + dependencies: + array-differ: 4.0.0 + array-union: 3.0.1 + minimatch: 10.2.6 + murmurhash-js@1.0.0: {} + mute-stream@3.0.0: {} + + mylas@2.1.14: {} + nanoevents@9.1.0: {} nanoid@3.3.11: {} @@ -18245,7 +20960,7 @@ snapshots: node-abi@3.78.0: dependencies: - semver: 7.7.3 + semver: 7.8.5 optional: true node-addon-api@7.1.1: @@ -18272,6 +20987,19 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + node-gyp@12.4.0: + dependencies: + env-paths: 2.2.1 + exponential-backoff: 3.1.3 + graceful-fs: 4.2.11 + nopt: 9.0.0 + proc-log: 6.1.0 + semver: 7.8.5 + tar: 7.5.22 + tinyglobby: 0.2.17 + undici: 6.28.0 + which: 6.0.1 + node-int64@0.4.0: {} node-releases@2.0.23: {} @@ -18282,6 +21010,16 @@ snapshots: dependencies: abbrev: 5.0.0 + nopt@9.0.0: + dependencies: + abbrev: 4.0.0 + + normalize-package-data@8.0.0: + dependencies: + hosted-git-info: 9.0.3 + semver: 7.8.5 + validate-npm-package-license: 3.0.4 + normalize-path@3.0.0: {} normalize-svg-path@0.1.0: {} @@ -18292,6 +21030,16 @@ snapshots: normalize-url@6.1.0: {} + npm-bundled@5.0.0: + dependencies: + npm-normalize-package-bin: 5.0.0 + + npm-install-checks@8.0.0: + dependencies: + semver: 7.8.5 + + npm-normalize-package-bin@5.0.0: {} + npm-package-arg@13.0.2: dependencies: hosted-git-info: 9.0.3 @@ -18299,13 +21047,43 @@ snapshots: semver: 7.8.5 validate-npm-package-name: 7.0.2 + npm-packlist@10.0.4: + dependencies: + ignore-walk: 8.0.0 + proc-log: 6.1.0 + + npm-pick-manifest@11.0.3: + dependencies: + npm-install-checks: 8.0.0 + npm-normalize-package-bin: 5.0.0 + npm-package-arg: 13.0.2 + semver: 7.8.5 + + npm-registry-fetch@19.1.1: + dependencies: + '@npmcli/redact': 4.0.0 + jsonparse: 1.3.1 + make-fetch-happen: 15.0.6 + minipass: 7.1.3 + minipass-fetch: 5.0.2 + minizlib: 3.1.0 + npm-package-arg: 13.0.2 + proc-log: 6.1.0 + transitivePeerDependencies: + - supports-color + npm-run-path@2.0.2: dependencies: path-key: 2.0.1 npm-run-path@4.0.1: dependencies: - path-key: 3.1.1 + path-key: 3.1.1 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 nth-check@2.1.1: dependencies: @@ -18382,6 +21160,10 @@ snapshots: dependencies: mimic-fn: 2.1.0 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -18405,6 +21187,17 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + ora@9.4.1: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.2 + own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 @@ -18421,6 +21214,10 @@ snapshots: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + p-locate@3.0.0: dependencies: p-limit: 2.3.0 @@ -18433,15 +21230,40 @@ snapshots: dependencies: p-limit: 3.1.0 + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@7.0.6: {} + p-queue@6.6.2: dependencies: eventemitter3: 4.0.7 p-timeout: 3.2.0 + p-queue@8.1.1: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + + p-queue@9.3.3: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + p-timeout@3.2.0: dependencies: p-finally: 1.0.0 + p-timeout@6.1.4: {} + + p-timeout@7.0.1: {} + + p-transform@5.0.1: + dependencies: + '@types/node': 24.12.4 + p-queue: 8.1.1 + p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -18453,8 +21275,37 @@ snapshots: validate-npm-package-license: 3.0.4 validate-npm-package-name: 7.0.2 + package-json@10.0.1: + dependencies: + ky: 1.14.3 + registry-auth-token: 5.1.1 + registry-url: 6.0.1 + semver: 7.8.5 + package-name-regex@2.0.6: {} + pacote@21.5.1: + dependencies: + '@gar/promise-retry': 1.0.3 + '@npmcli/git': 7.0.2 + '@npmcli/installed-package-contents': 4.0.0 + '@npmcli/package-json': 7.0.5 + '@npmcli/promise-spawn': 9.0.1 + '@npmcli/run-script': 10.0.4 + cacache: 20.0.4 + fs-minipass: 3.0.3 + minipass: 7.1.3 + npm-package-arg: 13.0.2 + npm-packlist: 10.0.4 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1 + proc-log: 6.1.0 + sigstore: 4.1.1 + ssri: 13.0.1 + tar: 7.5.22 + transitivePeerDependencies: + - supports-color + pako@1.0.11: {} parchment@3.0.0: {} @@ -18465,13 +21316,27 @@ snapshots: parenthesis@3.1.8: {} + parse-conflict-json@5.0.1: + dependencies: + json-parse-even-better-errors: 5.0.0 + just-diff: 6.0.2 + just-diff-apply: 5.5.0 + parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.7 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-json@8.3.0: + dependencies: + '@babel/code-frame': 7.29.7 + index-to-position: 1.2.0 + type-fest: 4.41.0 + + parse-ms@4.0.0: {} + parse-rect@1.2.0: dependencies: pick-by-alias: 1.2.0 @@ -18490,12 +21355,16 @@ snapshots: path-exists@4.0.0: {} + path-exists@5.0.0: {} + path-is-absolute@1.0.1: {} path-key@2.0.1: {} path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-scurry@1.11.1: @@ -18515,6 +21384,12 @@ snapshots: path2d@0.2.2: optional: true + pathe@1.1.2: {} + + pathe@2.0.3: {} + + pathval@1.1.1: {} + pbf@3.3.0: dependencies: ieee754: 1.2.1 @@ -18557,6 +21432,12 @@ snapshots: dependencies: find-up: 4.1.0 + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + playwright-core@1.62.1: {} playwright-ctrf-json-reporter@0.0.27: {} @@ -18567,6 +21448,10 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + plimit-lit@1.6.1: + dependencies: + queue-lit: 1.5.2 + plotly.js-dist-min@3.1.1: {} plotly.js@3.1.1(mapbox-gl@1.13.3): @@ -19098,12 +21983,20 @@ snapshots: prettier@3.9.6: {} + pretty-bytes@7.1.1: {} + pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + pretty-format@30.3.0: dependencies: '@jest/schemas': 30.0.5 @@ -19117,6 +22010,10 @@ snapshots: react-is-18: react-is@18.3.1 react-is-19: react-is@19.2.8 + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + pretty-quick@4.2.2(prettier@3.9.6): dependencies: '@pkgr/core': 0.2.9 @@ -19140,6 +22037,12 @@ snapshots: process-nextick-args@2.0.1: {} + proggy@4.0.0: {} + + promise-all-reject-late@1.0.1: {} + + promise-call-limit@3.0.2: {} + promise.series@0.2.0: {} promise@7.3.1: @@ -19186,6 +22089,8 @@ snapshots: dependencies: side-channel: 1.1.0 + queue-lit@1.5.2: {} + queue-microtask@1.2.3: {} quickselect@2.0.0: {} @@ -19230,7 +22135,6 @@ snapshots: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - optional: true react-big-calendar@1.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -19376,6 +22280,22 @@ snapshots: dependencies: pify: 2.3.0 + read-cmd-shim@6.0.0: {} + + read-package-up@12.0.0: + dependencies: + find-up-simple: 1.0.1 + read-pkg: 10.1.0 + type-fest: 5.8.0 + + read-pkg@10.1.0: + dependencies: + '@types/normalize-package-data': 2.4.4 + normalize-package-data: 8.0.0 + parse-json: 8.3.0 + type-fest: 5.8.0 + unicorn-magic: 0.4.0 + readable-stream@1.0.34: dependencies: core-util-is: 1.0.3 @@ -19471,6 +22391,19 @@ snapshots: unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 + registry-auth-token@5.1.1: + dependencies: + '@pnpm/npm-conf': 3.0.3 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + registry-url@7.2.0: + dependencies: + find-up-simple: 1.0.1 + ini: 5.0.0 + regjsgen@0.8.0: {} regjsparser@0.13.0: @@ -19534,6 +22467,10 @@ snapshots: remove-accents@0.5.0: {} + remove-trailing-separator@1.1.0: {} + + replace-ext@2.0.0: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -19546,6 +22483,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve-protobuf-schema@2.1.0: dependencies: protocol-buffers-schema: 3.6.0 @@ -19585,6 +22524,11 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + reusify@1.1.0: {} right-now@1.0.0: {} @@ -19698,6 +22642,10 @@ snapshots: magic-string: 0.30.19 rollup: 4.62.4 + rollup@3.30.0: + optionalDependencies: + fsevents: 2.3.3 + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -19773,6 +22721,8 @@ snapshots: rrweb-cssom@0.8.0: {} + run-async@4.0.6: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -19783,6 +22733,10 @@ snapshots: dependencies: tslib: 1.14.1 + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -19968,6 +22922,8 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -19976,6 +22932,17 @@ snapshots: signum@1.0.0: {} + sigstore@4.1.1: + dependencies: + '@sigstore/bundle': 4.0.0 + '@sigstore/core': 3.2.1 + '@sigstore/protobuf-specs': 0.5.1 + '@sigstore/sign': 4.1.1 + '@sigstore/tuf': 4.0.2 + '@sigstore/verify': 3.1.1 + transitivePeerDependencies: + - supports-color + simple-concat@1.0.1: optional: true @@ -19986,12 +22953,43 @@ snapshots: simple-concat: 1.0.1 optional: true + simple-git@3.36.0: + dependencies: + '@kwsites/file-exists': 1.1.1 + '@kwsites/promise-deferred': 1.1.1 + '@simple-git/args-pathspec': 1.0.3 + '@simple-git/argv-parser': 1.1.1 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + slash@1.0.0: {} slash@3.0.0: {} + slash@5.1.0: {} + + smart-buffer@4.2.0: {} + smob@1.5.0: {} + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.9 + transitivePeerDependencies: + - supports-color + + socks@2.8.9: + dependencies: + ip-address: 10.5.0 + smart-buffer: 4.2.0 + + sort-keys@6.0.1: + dependencies: + is-plain-obj: 4.1.0 + sort-object-keys@1.1.3: {} sort-object-keys@2.1.0: {} @@ -20040,6 +23038,11 @@ snapshots: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.22 + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + spdx-expression-validate@2.0.0: dependencies: spdx-expression-parse: 3.0.1 @@ -20056,6 +23059,10 @@ snapshots: sprintf-js@1.0.3: {} + ssri@13.0.1: + dependencies: + minipass: 7.1.3 + stable@0.1.8: {} stack-trace@0.0.9: {} @@ -20064,12 +23071,18 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + stackback@0.0.2: {} + static-eval@2.1.1: dependencies: escodegen: 2.1.0 statuses@2.0.2: {} + std-env@3.10.0: {} + + stdin-discarder@0.3.2: {} + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -20083,6 +23096,15 @@ snapshots: stream-shift@1.0.3: {} + streamx@2.28.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + string-hash@1.1.3: {} string-length@4.0.2: @@ -20112,6 +23134,11 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.1.2 + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.1.2 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -20174,6 +23201,15 @@ snapshots: dependencies: ansi-regex: 6.2.2 + strip-bom-buf@3.0.1: + dependencies: + is-utf8: 0.2.1 + + strip-bom-stream@5.0.0: + dependencies: + first-chunk-stream: 5.0.0 + strip-bom-buf: 3.0.1 + strip-bom@3.0.0: {} strip-bom@4.0.0: {} @@ -20182,15 +23218,20 @@ snapshots: strip-final-newline@2.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 - strip-json-comments@2.0.1: - optional: true + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} + strip-literal@1.3.0: + dependencies: + acorn: 8.15.0 + strnum@1.1.2: {} strongly-connected-components@1.0.1: {} @@ -20272,6 +23313,8 @@ snapshots: tabbable@6.2.0: {} + tagged-tag@1.0.0: {} + tar-fs@2.1.4: dependencies: chownr: 1.1.4 @@ -20289,6 +23332,21 @@ snapshots: readable-stream: 3.6.2 optional: true + tar@7.5.22: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + teex@1.0.1: + dependencies: + streamx: 2.28.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.11 @@ -20302,6 +23360,18 @@ snapshots: glob: 7.2.3 minimatch: 3.1.4 + text-decoder@1.2.7: + dependencies: + b4a: 1.8.1 + transitivePeerDependencies: + - react-native-b4a + + text-table@0.2.0: {} + + textextensions@6.11.0: + dependencies: + editions: 6.22.0 + through2@0.6.5: dependencies: readable-stream: 1.0.34 @@ -20314,6 +23384,8 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + tinycolor2@1.6.0: {} tinyexec@0.3.2: {} @@ -20330,10 +23402,14 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 + tinypool@0.7.0: {} + tinyqueue@2.0.3: {} tinyqueue@3.0.0: {} + tinyspy@2.2.1: {} + tldts-core@6.1.86: {} tldts@6.1.86: @@ -20372,6 +23448,8 @@ snapshots: tree-kill@1.2.2: {} + treeverse@3.0.0: {} + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -20422,6 +23500,20 @@ snapshots: optionalDependencies: '@swc/core': 1.13.5 + tsc-alias@1.9.1: + dependencies: + chokidar: 3.6.0 + commander: 9.5.0 + get-tsconfig: 4.14.1 + globby: 11.1.0 + mylas: 2.1.14 + normalize-path: 3.0.0 + plimit-lit: 1.6.1 + + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -20433,6 +23525,20 @@ snapshots: tslib@2.8.1: {} + tsx@4.23.12: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + + tuf-js@4.1.0: + dependencies: + '@tufjs/models': 4.1.0 + debug: 4.4.3 + make-fetch-happen: 15.0.6 + transitivePeerDependencies: + - supports-color + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -20473,10 +23579,16 @@ snapshots: type-detect@4.0.8: {} + type-detect@4.1.0: {} + type-fest@0.21.3: {} type-fest@4.41.0: {} + type-fest@5.8.0: + dependencies: + tagged-tag: 1.0.0 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -20540,6 +23652,8 @@ snapshots: uc.micro@2.1.0: {} + ufo@1.6.4: {} + uglify-js@3.19.3: optional: true @@ -20560,6 +23674,8 @@ snapshots: undici-types@7.16.0: {} + undici@6.28.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -20571,6 +23687,12 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unicorn-magic@0.4.0: {} + universalify@0.1.2: {} universalify@2.0.1: {} @@ -20606,6 +23728,8 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 + untildify@6.0.0: {} + update-browserslist-db@1.1.3(browserslist@4.26.3): dependencies: browserslist: 4.26.3 @@ -20649,6 +23773,107 @@ snapshots: vary@1.1.2: {} + version-range@4.15.0: {} + + vinyl-file@5.0.0: + dependencies: + '@types/vinyl': 2.0.12 + strip-bom-buf: 3.0.1 + strip-bom-stream: 5.0.0 + vinyl: 3.0.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + vinyl@3.0.1: + dependencies: + clone: 2.1.2 + remove-trailing-separator: 1.1.0 + replace-ext: 2.0.0 + teex: 1.0.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + vite-node@0.34.6(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + mlly: 1.8.2 + pathe: 1.1.2 + picocolors: 1.1.1 + vite: 4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + + vite-tsconfig-paths@4.3.2(typescript@5.9.3)(vite@4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0) + transitivePeerDependencies: + - supports-color + - typescript + + vite@4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0): + dependencies: + esbuild: 0.18.20 + postcss: 8.5.26 + rollup: 3.30.0 + optionalDependencies: + '@types/node': 24.12.4 + fsevents: 2.3.3 + sass: 1.102.0 + terser: 5.44.0 + + vitest@0.34.6(happy-dom@19.0.2)(jsdom@26.1.0(canvas@3.2.0))(playwright@1.62.1)(sass@1.102.0)(terser@5.44.0): + dependencies: + '@types/chai': 4.3.20 + '@types/chai-subset': 1.3.6(@types/chai@4.3.20) + '@types/node': 24.12.4 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.15.0 + acorn-walk: 8.3.4 + cac: 6.7.14 + chai: 4.5.0 + debug: 4.4.3 + local-pkg: 0.4.3 + magic-string: 0.30.19 + pathe: 1.1.2 + picocolors: 1.1.1 + std-env: 3.10.0 + strip-literal: 1.3.0 + tinybench: 2.9.0 + tinypool: 0.7.0 + vite: 4.5.14(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0) + vite-node: 0.34.6(@types/node@24.12.4)(sass@1.102.0)(terser@5.44.0) + why-is-node-running: 2.3.0 + optionalDependencies: + happy-dom: 19.0.2 + jsdom: 26.1.0(canvas@3.2.0) + playwright: 1.62.1 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vlq@0.2.3: {} vt-pbf@3.1.3: @@ -20663,6 +23888,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + walk-up-path@4.0.0: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -20736,6 +23963,11 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 + which-package-manager@1.0.1: + dependencies: + find-up: 7.0.0 + micromatch: 4.0.8 + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -20758,6 +23990,15 @@ snapshots: dependencies: isexe: 3.1.1 + which@6.0.1: + dependencies: + isexe: 4.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wordwrap@1.0.0: {} @@ -20791,6 +24032,10 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 + write-file-atomic@7.0.1: + dependencies: + signal-exit: 4.1.0 + ws@7.5.10: {} ws@8.18.3: {} @@ -20822,6 +24067,10 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + + yallist@5.0.0: {} + yaml@1.10.2: {} yargs-parser@20.2.9: {} @@ -20859,10 +24108,70 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 + yeoman-environment@6.1.0(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)): + dependencies: + '@yeoman/adapter': 4.0.2(@types/node@24.12.4) + '@yeoman/conflicter': 4.1.0(@types/node@24.12.4)(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + '@yeoman/namespace': 2.1.0 + '@yeoman/transform': 2.1.2(@types/node@24.12.4) + '@yeoman/types': 1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + arrify: 3.0.0 + chalk: 5.6.2 + commander: 14.0.3 + debug: 4.4.3 + execa: 9.6.1 + fly-import: 1.0.0 + globby: 16.2.3 + grouped-queue: 2.1.0 + locate-path: 8.0.0 + lodash-es: 4.18.1 + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) + mem-fs-editor: 12.0.6(@types/node@24.12.4)(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + semver: 7.8.5 + slash: 5.1.0 + untildify: 6.0.0 + which-package-manager: 1.0.1 + transitivePeerDependencies: + - '@types/node' + - bare-abort-controller + - react-native-b4a + - supports-color + + yeoman-generator@8.2.2(@types/node@24.12.4)(@yeoman/types@1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)): + dependencies: + '@types/debug': 4.1.13 + '@types/lodash-es': 4.17.12 + '@yeoman/namespace': 2.1.0 + '@yeoman/types': 1.11.1(@types/node@24.12.4)(@yeoman/adapter@4.0.2(@types/node@24.12.4))(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + chalk: 5.6.2 + debug: 4.4.3 + execa: 9.6.1 + latest-version: 9.0.0 + lodash-es: 4.18.1 + mem-fs: 4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12) + mem-fs-editor: 12.0.6(@types/node@24.12.4)(mem-fs@4.1.5(@types/node@24.12.4)(@types/vinyl@2.0.12)) + minimist: 1.2.8 + read-package-up: 12.0.0 + semver: 7.8.5 + simple-git: 3.36.0 + sort-keys: 6.0.1 + text-table: 0.2.0 + type-fest: 5.8.0 + optionalDependencies: + '@types/node': 24.12.4 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + - supports-color + yn@3.1.1: {} yocto-queue@0.1.0: {} + yocto-queue@1.2.2: {} + + yoctocolors@2.2.0: {} + zip-a-folder@6.1.4: dependencies: lzma: 2.3.2 @@ -20872,8 +24181,14 @@ snapshots: dependencies: zod: 3.25.76 + zod-to-json-schema@3.25.1(zod@4.4.3): + dependencies: + zod: 4.4.3 + zod-validation-error@4.0.2(zod@3.25.76): dependencies: zod: 3.25.76 zod@3.25.76: {} + + zod@4.4.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2988723ef3..57f00544d8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ recursiveInstall: true packages: - "packages/*/*" + - "packages/pluggable-widgets-mcp/" - "automation/*" catalog: rollup: "3.29"